diff --git a/amps/share-services/pom.xml b/amps/share-services/pom.xml new file mode 100644 index 0000000000..6b65e8dc91 --- /dev/null +++ b/amps/share-services/pom.xml @@ -0,0 +1,248 @@ + + 4.0.0 + alfresco-share-services + Alfresco Share Services AMP + Module to be applied to alfresco.war, containing APIs for Alfresco Share + amp + + alfresco-share-parent + org.alfresco + 11.1-SNAPSHOT + + + + + org.alfresco + alfresco-remote-api + ${dependency.alfresco-community-repo.version} + provided + + + org.alfresco + alfresco-core + + + + javax.servlet + jstl + + + commons-collections + commons-collections + + + + + org.alfresco + alfresco-core + ${dependency.alfresco-community-repo.version} + provided + + + dom4j + dom4j + + + + + commons-lang + commons-lang + provided + + + + + org.alfresco + alfresco-sync-events + 1.2.14 + provided + + + + + junit + junit + 4.13.1 + test + + + org.alfresco + alfresco-repository + ${dependency.alfresco-community-repo.version} + tests + test + + + dom4j + dom4j + + + org.alfresco + alfresco-core + + + + javax.servlet + jstl + + + commons-collections + commons-collections + + + + + org.alfresco + alfresco-remote-api + ${dependency.alfresco-community-repo.version} + tests + test + + + org.alfresco.surf + spring-webscripts + ${dependency.webscripts.version} + tests + test + + + commons-collections + commons-collections + + + + + org.springframework + spring-test + ${dependency.spring.version} + test + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + org.apache.taglibs + taglibs-standard-spec + provided + + + org.apache.taglibs + taglibs-standard-impl + provided + + + org.apache.taglibs + taglibs-standard-jstlel + provided + + + + + + + src/main/resources + + + + src/main/amp + ../${project.build.finalName} + true + + + + + src/test/resources + + + + src/main/amp/config + true + + + + + + org.alfresco.maven.plugin + alfresco-maven-plugin + true + + + + true + true + + + ${version.major}.${version.minor}.${version.revision} + ${project.version} + ${maven.build.timestamp} + ${bamboo_planName} + ${bamboo_fullBuildKey} + ${bamboo_buildNumber} + ${bamboo_repository_revision_number} + + + + + + + maven-surefire-plugin + + + default-test + + + + nothing + + + + + + + + + + + integration-tests + + false + + + + + maven-surefire-plugin + + + integration-tests + + test + + + + **/*Test.java + + + + + + + + + + diff --git a/amps/share-services/src/main/amp/config/alfresco/module/alfresco-share-services/alfresco-global.properties b/amps/share-services/src/main/amp/config/alfresco/module/alfresco-share-services/alfresco-global.properties new file mode 100644 index 0000000000..07e6af8e37 --- /dev/null +++ b/amps/share-services/src/main/amp/config/alfresco/module/alfresco-share-services/alfresco-global.properties @@ -0,0 +1,5 @@ +# +# Disable load of sample site +# +sample.site.disabled=false + diff --git a/amps/share-services/src/main/amp/config/alfresco/module/alfresco-share-services/module-bootstrap-context.xml b/amps/share-services/src/main/amp/config/alfresco/module/alfresco-share-services/module-bootstrap-context.xml new file mode 100644 index 0000000000..6b16204ddb --- /dev/null +++ b/amps/share-services/src/main/amp/config/alfresco/module/alfresco-share-services/module-bootstrap-context.xml @@ -0,0 +1,25 @@ + + + + + + + + + + alfresco/model/datalistModel.xml + + + + + alfresco/messages/data-list-model + + + + + + diff --git a/amps/share-services/src/main/amp/config/alfresco/module/alfresco-share-services/module-context.xml b/amps/share-services/src/main/amp/config/alfresco/module/alfresco-share-services/module-context.xml new file mode 100644 index 0000000000..266f9e8f5c --- /dev/null +++ b/amps/share-services/src/main/amp/config/alfresco/module/alfresco-share-services/module-context.xml @@ -0,0 +1,315 @@ + + + + + + + + + patch.siteLoadPatch.swsdp + patch.siteLoadPatch.description + 0 + ${version.schema} + 100000 + ${sample.site.disabled} + + + + + + + + + + + + + swsdp + + + + + + alfresco/bootstrap/team-sample-sites/swsdp/Users.acp + + + + + alfresco/bootstrap/team-sample-sites/swsdp/People.acp + + + + + alfresco/bootstrap/team-sample-sites/swsdp/Groups.txt + + + + + alfresco/bootstrap/team-sample-sites/swsdp/Contents.acp + + + + + + + + + + + + + + + + + + dl:todoTitle,dl:todoDueDate,dl:todoPriority,dl:todoStatus,dl:todoNotes,dl:assignee,dl:attachments + + + cm:title,cm:description,dl:ganttStartDate,dl:ganttEndDate,dl:taskAssignee,dl:taskPriority,dl:taskStatus,dl:ganttPercentComplete,dl:taskComments,cm:attachments + + + cm:title,cm:description,dl:simpletaskDueDate,dl:simpletaskPriority,dl:simpletaskStatus,dl:simpletaskComments + + + dl:contactFirstName,dl:contactLastName,dl:contactEmail,dl:contactCompany,dl:contactJobTitle,dl:contactPhoneOffice,dl:contactPhoneMobile,dl:contactNotes + + + dl:issueID,cm:title,dl:issueAssignedTo,dl:issueStatus,dl:issuePriority,cm:description,dl:issueDueDate,dl:issueComments,cm:attachments + + + cm:title,cm:description,dl:eventLocation,dl:eventStartDate,dl:eventEndDate,dl:eventRegistrations,cm:attachments,dl:eventNote + + + cm:title,dl:locationAddress1,dl:locationAddress2,dl:locationAddress3,dl:locationZip,dl:locationState,dl:locationCountry,cm:description,cm:attachments + + + dl:meetingAgendaRef,cm:title,cm:description,dl:meetingAgendaTime,dl:meetingAgendaOwner,cm:attachments + + + dl:eventAgendaRef,dl:eventAgendaStartTime,dl:eventAgendaEndTime,dl:eventAgendaSessionName,dl:eventAgendaPresenter,dl:eventAgendaAudience,cm:attachments,dl:eventAgendaNotes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.service.cmr.wiki.WikiService.listWikiPages=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties + + + + + + + + + + + + + + org.alfresco.service.cmr.wiki.WikiService + + + + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.readOnly} + ${server.transaction.mode.readOnly} + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + slingshotDocLib + + + + + + + + + + + diff --git a/amps/share-services/src/main/amp/module.properties b/amps/share-services/src/main/amp/module.properties new file mode 100644 index 0000000000..917011abe0 --- /dev/null +++ b/amps/share-services/src/main/amp/module.properties @@ -0,0 +1,5 @@ +module.id=${project.artifactId} +module.title=${project.name} +module.description=${project.description} +module.version=${version} +module.repo.version.min=6.1 diff --git a/amps/share-services/src/main/java/org/alfresco/repo/jscript/SlingshotDocLibCustomResponse.java b/amps/share-services/src/main/java/org/alfresco/repo/jscript/SlingshotDocLibCustomResponse.java new file mode 100644 index 0000000000..eb216f8f2f --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/jscript/SlingshotDocLibCustomResponse.java @@ -0,0 +1,84 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.jscript; + +import org.alfresco.repo.jscript.app.CustomResponse; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.Serializable; +import java.util.Map; + +/** + * Populates DocLib webscript response with custom metadata output + * + * @author mikeh + */ +public final class SlingshotDocLibCustomResponse extends BaseScopableProcessorExtension +{ + private Map customResponses; + + /** + * Set the custom response beans + * + * @param customResponses + */ + public void setCustomResponses(Map customResponses) + { + this.customResponses = customResponses; + } + + /** + * Returns a JSON string to be added to the DocLib webscript response. + * + * @return The JSON string + */ + public String getJSON() + { + return this.getJSONObj().toString(); + } + + /** + * Returns a JSON object to be added to the DocLib webscript response. + * + * @return The JSON object + */ + protected Object getJSONObj() + { + JSONObject json = new JSONObject(); + + + for (Map.Entry entry : this.customResponses.entrySet()) + { + try + { + Serializable response = ((CustomResponse) entry.getValue()).populate(); + json.put(entry.getKey(), response == null ? JSONObject.NULL: response); + } + catch (JSONException error) + { + error.printStackTrace(); + } + } + + return json; + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/jscript/SlingshotDocLibCustomResponseRegistrar.java b/amps/share-services/src/main/java/org/alfresco/repo/jscript/SlingshotDocLibCustomResponseRegistrar.java new file mode 100644 index 0000000000..ca33588755 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/jscript/SlingshotDocLibCustomResponseRegistrar.java @@ -0,0 +1,70 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.jscript; + +import java.util.Map; + +/** + * Register an additional custom metadata output for the DocLib webscript response + * + * @author Will Abson + */ +public final class SlingshotDocLibCustomResponseRegistrar +{ + private Map responsesMap; + private String key; + private Object value; + + public String getKey() + { + return key; + } + + public void setKey(String key) + { + this.key = key; + } + + public Object getValue() + { + return value; + } + + public void setValue(Object value) + { + this.value = value; + } + + public Map getResponsesMap() + { + return responsesMap; + } + + public void setResponsesMap(Map responsesMap) + { + this.responsesMap = responsesMap; + } + + public void addCustomResponse() + { + responsesMap.put(key, value); + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/datalist/DataListDownloadWebScript.java b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/datalist/DataListDownloadWebScript.java new file mode 100644 index 0000000000..61d605be46 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/datalist/DataListDownloadWebScript.java @@ -0,0 +1,407 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.datalist; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import org.alfresco.model.ContentModel; +import org.alfresco.model.DataListModel; +import org.alfresco.repo.web.scripts.DeclarativeSpreadsheetWebScript; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.namespace.InvalidQNameException; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.poi.ss.usermodel.*; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + + +/** + * Data List Download + * + * Exports the contents of a Data List as an Excel file + * + * @author Nick Burch + */ +public class DataListDownloadWebScript extends DeclarativeSpreadsheetWebScript + implements InitializingBean +{ + // Logger + private static final Log logger = LogFactory.getLog(DataListDownloadWebScript.class); + + private static final QName DATA_LIST_ITEM_TYPE = DataListModel.PROP_DATALIST_ITEM_TYPE; + + private NodeService nodeService; + private SiteService siteService; + private NamespaceService namespaceService; + private Map> modelOrder; + private Map rawModelOrder; + + public DataListDownloadWebScript() + { + this.filenameBase = "DataListExport"; + } + + /** + * @param nodeService NodeService + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param siteService SiteService + */ + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + /** + * @param namespaceService NamespaceService + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + public void setModelOrder(Map rawModelOrder) + { + this.rawModelOrder = rawModelOrder; + } + + + @Override + public void afterPropertiesSet() throws Exception { + modelOrder = new HashMap>(); + for(String key : rawModelOrder.keySet()) + { + QName model; + List order = new ArrayList(); + + try + { + model= QName.createQName(key, namespaceService); + } + catch(InvalidQNameException e) + { + logger.warn("Skipping invalid model type " + key); + continue; + } + + StringTokenizer st = new StringTokenizer(rawModelOrder.get(key), ","); + while(st.hasMoreTokens()) + { + order.add( QName.createQName(st.nextToken(), namespaceService) ); + } + modelOrder.put(model, order); + } + } + + /** + * Identify the datalist + */ + @Override + protected Object identifyResource(String format, WebScriptRequest req) { + // Try to find the datalist they requested + NodeRef list; + Map args = req.getServiceMatch().getTemplateVars(); + if(args.get("store_type") != null) + { + list = new NodeRef( + args.get("store_type"), + args.get("store_id"), + args.get("id") + ); + } + else + { + // Get the site + SiteInfo site = siteService.getSite(args.get("site")); + if(site == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "Site not found with supplied name"); + } + + // Now find the data list container with in + NodeRef container = nodeService.getChildByName( + site.getNodeRef(), + ContentModel.ASSOC_CONTAINS, + args.get("container") + ); + if(container == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "Container not found within site"); + } + + // Now get the data list itself + list = nodeService.getChildByName( + container, + ContentModel.ASSOC_CONTAINS, + args.get("list") + ); + } + if(list == null || !nodeService.exists(list)) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "The Data List could not be found"); + } + + return list; + } + + /** + * We don't have a HTML version + */ + @Override + protected boolean allowHtmlFallback() { + return false; + } + + /** + * Fetch the properties, in the requested order, from + * the data list definition + */ + @Override + protected List> buildPropertiesForHeader( + Object resource, String format, WebScriptRequest req) { + NodeRef list = (NodeRef)resource; + QName type = buildType(list); + + // Has the user given us rules for what to do + // with this type? + List props; + if(modelOrder.containsKey(type)) + { + props = modelOrder.get(type); + } + else + { + // We'll have to try to guess it for them + // For now, just use DataList properties for the type + TypeDefinition typeDef = dictionaryService.getType(type); + Map allProps = typeDef.getProperties(); + props = new ArrayList(); + + for(QName prop : allProps.keySet()) + { + if(NamespaceService.DATALIST_MODEL_1_0_URI.equals(prop.getNamespaceURI())) + { + props.add(prop); + } + } + } + + // Everything is required + List> properties = new ArrayList>(); + for(QName qname : props) + { + properties.add(new Pair(qname, true)); + } + return properties; + } + + private QName buildType(NodeRef list) + { + String typeS = (String)nodeService.getProperty(list, DATA_LIST_ITEM_TYPE); + if(! typeS.startsWith(NamespaceService.DATALIST_MODEL_PREFIX + ":")) + { + throw new WebScriptException(Status.STATUS_NOT_IMPLEMENTED, "Unexpected list type " + typeS); + } + QName type = QName.createQName(NamespaceService.DATALIST_MODEL_1_0_URI, typeS.substring(typeS.indexOf(':')+1)); + return type; + } + + private List getItems(NodeRef list) + { + Set typeSet = new HashSet(Arrays.asList(new QName[] { buildType(list) })); + + List items = new ArrayList(); + for(ChildAssociationRef ca : nodeService.getChildAssocs(list, typeSet)) + { + items.add(ca.getChildRef()); + } + return items; + } + + @Override + protected void populateBody(Object resource, CSVPrinter csv, + List properties) throws IOException { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "CSV not currently supported"); + } + + @Override + protected void populateBody(Object resource, Workbook workbook, + Sheet sheet, List properties) throws IOException { + NodeRef list = (NodeRef)resource; + List items = getItems(list); + + // Our various formats + DataFormat formatter = workbook.createDataFormat(); + + CellStyle styleInt = workbook.createCellStyle(); + styleInt.setDataFormat( formatter.getFormat("0") ); + CellStyle styleDate = workbook.createCellStyle(); + styleDate.setDataFormat( formatter.getFormat("yyyy-mm-dd") ); + CellStyle styleDouble = workbook.createCellStyle(); + styleDouble.setDataFormat( formatter.getFormat("General") ); + CellStyle styleNewLines = workbook.createCellStyle(); + styleNewLines.setWrapText(true); + + // Export the items + int rowNum = 1, colNum = 0; + for(NodeRef item : items) + { + Row r = sheet.createRow(rowNum); + + colNum = 0; + for(QName prop : properties) + { + Cell c = r.createCell(colNum); + + Serializable val = nodeService.getProperty(item, prop); + if(val == null) + { + // Is it an association, or just missing? + List assocs = nodeService.getTargetAssocs(item, prop); + if(assocs.size() > 0) + { + StringBuffer text = new StringBuffer(); + int lines = 1; + + for(AssociationRef ref : assocs) + { + NodeRef child = ref.getTargetRef(); + QName type = nodeService.getType(child); + if(ContentModel.TYPE_PERSON.equals(type)) + { + if(text.length() > 0) { + text.append('\n'); + lines++; + } + text.append(nodeService.getProperty( + child, ContentModel.PROP_USERNAME + )); + } + else if(ContentModel.TYPE_CONTENT.equals(type)) + { + // TODO Link to the content + if(text.length() > 0) { + text.append('\n'); + lines++; + } + text.append(nodeService.getProperty( + child, ContentModel.PROP_TITLE + )); + } + else + { + System.err.println("TODO: handle " + type + " for " + child); + } + } + + String v = text.toString(); + c.setCellValue( v ); + if(lines > 1) + { + c.setCellStyle(styleNewLines); + r.setHeightInPoints( lines*sheet.getDefaultRowHeightInPoints() ); + } + } + else + { + // This property isn't set + c.setCellType(CellType.BLANK); + } + } + else + { + // Regular property, set + if(val instanceof String) + { + c.setCellValue((String)val); + } + else if(val instanceof Date) + { + c.setCellValue((Date)val); + c.setCellStyle(styleDate); + } + else if(val instanceof Integer || val instanceof Long) + { + double v = 0.0; + if(val instanceof Long) v = (double)(Long)val; + if(val instanceof Integer) v = (double)(Integer)val; + c.setCellValue(v); + c.setCellStyle(styleInt); + } + else if(val instanceof Float || val instanceof Double) + { + double v = 0.0; + if(val instanceof Float) v = (double)(Float)val; + if(val instanceof Double) v = (double)(Double)val; + c.setCellValue(v); + c.setCellStyle(styleDouble); + } + else + { + // TODO + System.err.println("TODO: handle " + val.getClass().getName() + " - " + val); + } + } + + colNum++; + } + + rowNum++; + } + + // Sensible column widths please! + colNum = 0; + for(QName prop : properties) + { + sheet.autoSizeColumn(colNum); + colNum++; + } + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/search/AutoSuggestSearchGet.java b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/search/AutoSuggestSearchGet.java new file mode 100644 index 0000000000..9731afcd78 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/search/AutoSuggestSearchGet.java @@ -0,0 +1,113 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.search; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.search.SuggesterParameters; +import org.alfresco.service.cmr.search.SuggesterResult; +import org.alfresco.service.cmr.search.SuggesterService; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class is the controller for the auto-suggest-search.get web + * script. + * + * @author Jamal Kaabi-Mofrad + * @since 5.0 + */ +public class AutoSuggestSearchGet extends DeclarativeWebScript +{ + private static final Log logger = LogFactory.getLog(AutoSuggestSearchGet.class); + + private static final String TERM = "t"; + private static final String LIMIT = "limit"; + private static final String SUGGESTIONS = "suggestions"; + + private SuggesterService suggesterService; + + public void setSuggesterService(SuggesterService suggesterService) + { + this.suggesterService = suggesterService; + } + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + + List list = new ArrayList<>(); + Map model = new HashMap(1); + model.put(SUGGESTIONS, list); + + if (!suggesterService.isEnabled()) + { + return model; + } + + String term = req.getParameter(TERM); + int limit = getLimit(req.getParameter(LIMIT)); + + if (term == null || term.isEmpty()) + { + return model; + } + + SuggesterResult result = suggesterService.getSuggestions(new SuggesterParameters(term, limit, false)); + List> suggestedTerms = result.getSuggestions(); + for (Pair pair : suggestedTerms) + { + list.add(new SearchSuggestionData(pair.getFirst(), pair.getSecond())); + + } + + if (logger.isDebugEnabled()) + { + logger.debug("Suggested terms for the [" + term + "] are: " + list); + } + + return model; + } + + private int getLimit(String limit) + { + if (limit == null) + { + return -1; + } + try + { + return Integer.parseInt(limit); + } + catch (NumberFormatException ne) + { + return -1; + } + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/search/SearchSuggestionData.java b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/search/SearchSuggestionData.java new file mode 100644 index 0000000000..05dc53592d --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/search/SearchSuggestionData.java @@ -0,0 +1,58 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.search; + +/** + * Basic POJO to represent a term suggestion. + * + * @author Jamal Kaabi-Mofrad + * @since 5.0 + */ +public class SearchSuggestionData +{ + private final String term; + private final int weight; + + public SearchSuggestionData(String term, int weight) + { + this.term = term; + this.weight = weight; + } + + public String getTerm() + { + return this.term; + } + + public int getWeight() + { + return this.weight; + } + + @Override + public String toString() + { + StringBuilder builder = new StringBuilder(100); + builder.append("SearchSuggestionData [term=").append(this.term).append(", weight=").append(this.weight) + .append("]"); + return builder.toString(); + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/site/SiteShareViewUrlGet.java b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/site/SiteShareViewUrlGet.java new file mode 100644 index 0000000000..99ee2d78bf --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/site/SiteShareViewUrlGet.java @@ -0,0 +1,273 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.site; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.model.DataListModel; +import org.alfresco.model.ForumModel; +import org.alfresco.repo.admin.SysAdminParams; +import org.alfresco.repo.calendar.CalendarModel; +import org.alfresco.repo.site.SiteModel; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.UrlUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Returns the Share URL to view a given NodeRef. + * + * The supplied NodeRef must be within a Site, and must be + * of a type supported by Share + * + * @author Nick Burch + * @since 4.0.2 + */ +public class SiteShareViewUrlGet extends DeclarativeWebScript +{ + private static Log logger = LogFactory.getLog(SiteShareViewUrlGet.class); + + protected NodeService nodeService; + protected SiteService siteService; + protected SysAdminParams sysAdminParams; + protected DictionaryService dictionaryService; + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + // Grab the NodeRef + String nodeRefS = req.getParameter("nodeRef"); + if (nodeRefS == null) + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "NodeRef must be supplied"); + if (! NodeRef.isNodeRef(nodeRefS)) + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid NodeRef"); + + // Check the node exists + NodeRef nodeRef = new NodeRef(nodeRefS); + if (! nodeService.exists(nodeRef)) + throw new WebScriptException(Status.STATUS_NOT_FOUND, "Node Does Not Exist"); + + + // Work out what site it's in, and what container in the site + SiteInfo site = null; + NodeRef siteContainer = null; + { + NodeRef current = nodeRef; + NodeRef prev = null; + while (current != null) + { + // Are we at a site yet? + QName type = nodeService.getType(current); + if (dictionaryService.isSubClass(type, SiteModel.TYPE_SITE)) + { + // Found it! + siteContainer = prev; + site = siteService.getSite(current); + break; + } + + // Step down + prev = current; + current = nodeService.getPrimaryParent(current).getParentRef(); + } + } + if (site == null) + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Node isn't part of a site"); + + // Grab the main URL for the site + String baseUrl = getShareSiteRootStem(req, site); + + // Identify the appropriate Share URL, based on the Node Type + QName nodeType = nodeService.getType(nodeRef); + + + // Start on the model + Map model = new HashMap(); + model.put("node", nodeRef); + model.put("site", site); + model.put("type", nodeType); + + // Get the URL, and we're done + String page = identifySharePage(nodeRef, site, siteContainer, nodeType); + model.put("url", baseUrl + page); + + return model; + } + + private static QName TYPE_LINK = QName.createQName(NamespaceService.LINKS_MODEL_1_0_URI, "link"); + protected String identifySharePage(NodeRef nodeRef, SiteInfo site, NodeRef siteContainer, QName nodeType) + { + // Grab the name of the Node itself - often used for the URL + String name = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + + + // Wiki and Blog both use cm:content in special containers + if (siteContainer != null && dictionaryService.isSubClass(nodeType, ContentModel.TYPE_CONTENT)) + { + QName containerName = nodeService.getPrimaryParent(siteContainer).getQName(); + if (containerName.equals( QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "blog") )) + { + // Wiki - cm:content in folder called cm:wiki + return "wiki-page?title=" + name; + } + if (containerName.equals( QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "wiki") )) + { + // Blog - cm:content in cm:blog + return "blog-postview?postId=" + name; + } + } + + // Is it a Data List? + if (dictionaryService.isSubClass(nodeType, DataListModel.TYPE_DATALIST)) + { + return "data-lists?list=" + name; + } + + // Is it a Link? + if (dictionaryService.isSubClass(nodeType, TYPE_LINK)) + { + return "links-view?linkId=" + name; + } + + // Is it a Calendar Entry? + if (dictionaryService.isSubClass(nodeType, CalendarModel.TYPE_EVENT)) + { + // Find the date + Date date = (Date)nodeService.getProperty(nodeRef, CalendarModel.PROP_FROM_DATE); + SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); + + // Build the URL based on it + return "calendar?date=" + fmt.format(date); + } + + // Is it a discussions topic or post? + if (dictionaryService.isSubClass(nodeType, ForumModel.TYPE_TOPIC)) + { + // Topic is easy + return "discussions-topicview?topicId=" + name; + } + if (dictionaryService.isSubClass(nodeType, ForumModel.TYPE_POST)) + { + // Go from post to the topic, then list from there + NodeRef postTopic = nodeService.getPrimaryParent(nodeRef).getParentRef(); + String topicName = (String)nodeService.getProperty(postTopic, ContentModel.PROP_NAME); + return "discussions-topicview?topicId=" + topicName; + } + + // Is it just regular content? + if (dictionaryService.isSubClass(nodeType, ContentModel.TYPE_CONTENT)) + { + // Simple, Document Details with a noderef + return "document-details?nodeRef=" + nodeRef.toString(); + } + + // Is it a normal folder? + if (dictionaryService.isSubClass(nodeType, ContentModel.TYPE_FOLDER)) + { + // Need the path within the site + List paths = new ArrayList(); + NodeRef current = nodeRef; + while (current != null && !current.equals(siteContainer) && !current.equals(site.getNodeRef())) + { + paths.add( (String)nodeService.getProperty(current, ContentModel.PROP_NAME) ); + current = nodeService.getPrimaryParent(current).getParentRef(); + } + + // Invert to build the path + StringBuilder path = new StringBuilder(); + for (int i=paths.size()-1; i>=0; i--) + { + path.append('/'); + path.append(paths.get(i)); + } + + if (path.length() > 0) + { + // Becomes documentlibrary?path=/Docs/Beta + return "documentlibrary?path=" + path.toString(); + } + else + { + // Just the root of the document library + return "documentlibrary"; + } + } + + // If we can't work out what it is, log and take them to the site dashboard + if (logger.isDebugEnabled()) + logger.debug("COuldn't identify specific URL for Node " + nodeRef + " of type " + nodeType); + return "dashboard"; + } + + /** + * Returns the root of the Share Site pages for a given site, eg + * https://test.alfresco.com/share/page/site/test-site/ + */ + protected String getShareSiteRootStem(WebScriptRequest req, SiteInfo site) + { + return getShareRootUrl(req) + "page/site/" + site.getShortName() + "/"; + } + /** + * Returns the root of the Share WebApp, eg + * http://localhost:8081/share/ + */ + protected String getShareRootUrl(WebScriptRequest req) + { + return UrlUtil.getShareUrl(sysAdminParams) + "/"; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + public void setSysAdminParams(SysAdminParams sysAdminParams) + { + this.sysAdminParams = sysAdminParams; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } +} \ No newline at end of file diff --git a/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/AbstractWikiWebScript.java b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/AbstractWikiWebScript.java new file mode 100644 index 0000000000..bfcc7f5962 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/AbstractWikiWebScript.java @@ -0,0 +1,306 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.wiki; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.query.PagingRequest; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.activities.ActivityService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.NoSuchPersonException; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.wiki.WikiPageInfo; +import org.alfresco.service.cmr.wiki.WikiService; +import org.alfresco.util.ScriptPagingDetails; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.springframework.extensions.surf.util.URLEncoder; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.json.JSONWriter; + +/** + * @author Nick Burch + * @since 4.0 + */ +public abstract class AbstractWikiWebScript extends DeclarativeWebScript +{ + public static final String WIKI_SERVICE_ACTIVITY_APP_NAME = "wiki"; + + /** + * When no maximum or paging info is given, what should we use? + */ + protected static final int MAX_QUERY_ENTRY_COUNT = 1000; + + private static Log logger = LogFactory.getLog(AbstractWikiWebScript.class); + + // Injected services + protected NodeService nodeService; + protected SiteService siteService; + protected WikiService wikiService; + protected PersonService personService; + protected ActivityService activityService; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + public void setWikiService(WikiService wikiService) + { + this.wikiService = wikiService; + } + + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public void setActivityService(ActivityService activityService) + { + this.activityService = activityService; + } + + + protected String getOrNull(JSONObject json, String key) + { + if (json.containsKey(key)) + { + return (String)json.get(key); + } + return null; + } + + /** + * Builds up a listing Paging request, based on the arguments + * specified in the URL + */ + protected PagingRequest buildPagingRequest(WebScriptRequest req) + { + return new ScriptPagingDetails(req, MAX_QUERY_ENTRY_COUNT); + } + + protected void addActivityEntry(String event, WikiPageInfo wikiPage, SiteInfo site, + WebScriptRequest req, JSONObject json) + { + addActivityEntry(event, wikiPage, site, req, json, Collections.emptyMap()); + } + + /** + * Generates an activity entry for the link + * + * @param event a String representing the event. + * @param wikiPage the wiki page generating the activity. + * @param site the site in which the wiki page was created. + * @param req the {@link WebScriptRequest}. + * @param json JSONObject + * @param additionalData any additional data required for the activity. + */ + protected void addActivityEntry(String event, + WikiPageInfo wikiPage, SiteInfo site, + WebScriptRequest req, JSONObject json, + Map additionalData) + { + // What page is this for? + String page = req.getParameter("page"); + if (page == null && json != null) + { + if (json.containsKey("page")) + { + page = (String)json.get("page"); + } + } + if (page == null) + { + // Default + page = "wiki"; + } + + try + { + StringWriter activityJson = new StringWriter(); + JSONWriter activity = new JSONWriter(activityJson); + activity.startObject(); + activity.writeValue("title", wikiPage.getTitle()); + activity.writeValue("page", page + "?title=" + URLEncoder.encodeUriComponent(wikiPage.getTitle())); + for (Map.Entry entry : additionalData.entrySet()) + { + activity.writeValue(entry.getKey(), entry.getValue()); + } + activity.endObject(); + + activityService.postActivity( + "org.alfresco.wiki.page-" + event, + site.getShortName(), + WIKI_SERVICE_ACTIVITY_APP_NAME, + activityJson.toString()); + } + catch (Exception e) + { + // Warn, but carry on + logger.warn("Error adding wiki page " + event + " to activities feed", e); + } + } + + protected NodeRef personForModel(String username) + { + if (username == null || username.isEmpty()) + { + return null; + } + + try + { + // Will turn into a Script Node needed of the person + return personService.getPerson(username); + } + catch(NoSuchPersonException e) + { + // This is normally caused by the person having been deleted + return null; + } + } + + protected Map renderWikiPage(WikiPageInfo page) + { + Map res = new HashMap<>(); + res.put("page", page); + res.put("node", page.getNodeRef()); + res.put("name", page.getSystemName()); + res.put("title", page.getTitle()); + res.put("contents", page.getContents()); + res.put("tags", page.getTags()); + + // Both forms used for dates + res.put("createdOn", page.getCreatedAt()); + res.put("modifiedOn", page.getModifiedAt()); + res.put("created", page.getCreatedAt()); + res.put("modified", page.getModifiedAt()); + + // For most things, we want blank instead of null + for (Map.Entry entry : res.entrySet()) + { + if (entry.getValue() == null) entry.setValue(""); + } + + // FTL needs a script node of the people, or null if unavailable + res.put("createdBy", personForModel(page.getCreator())); + res.put("modifiedBy", personForModel(page.getModifier())); + + // All done + return res; + } + + @Override + protected Map executeImpl(WebScriptRequest req, + Status status, Cache cache) + { + Map templateVars = req.getServiceMatch().getTemplateVars(); + if (templateVars == null) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "No parameters supplied"); + } + + + // Parse the JSON, if supplied + JSONObject json = null; + String contentType = req.getContentType(); + if (contentType != null && contentType.indexOf(';') != -1) + { + contentType = contentType.substring(0, contentType.indexOf(';')); + } + if (MimetypeMap.MIMETYPE_JSON.equals(contentType)) + { + JSONParser parser = new JSONParser(); + try + { + json = (JSONObject)parser.parse(req.getContent().getContent()); + } + catch (IOException | ParseException io) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + io.getMessage()); + } + } + + + // Get the site short name. Try quite hard to do so... + String siteName = templateVars.get("siteId"); + if (siteName == null) + { + siteName = req.getParameter("site"); + } + if (siteName == null && json != null) + { + if (json.containsKey("siteid")) + { + siteName = (String)json.get("siteid"); + } + else if (json.containsKey("siteId")) + { + siteName = (String)json.get("siteId"); + } + else if(json.containsKey("site")) + { + siteName = (String)json.get("site"); + } + } + if (siteName == null) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "No site given"); + } + + // Grab the requested site + SiteInfo site = siteService.getSite(siteName); + if (site == null) + { + String error = "Could not find site: " + siteName; + throw new WebScriptException(Status.STATUS_NOT_FOUND, error); + } + + String pageTitle = templateVars.get("pageTitle"); + + // Have the real work done + return executeImpl(site, pageTitle, req, json, status, cache); + } + + protected abstract Map executeImpl(SiteInfo site, + String pageTitle, WebScriptRequest req, JSONObject json, + Status status, Cache cache); +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageDelete.java b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageDelete.java new file mode 100644 index 0000000000..c75167ec46 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageDelete.java @@ -0,0 +1,66 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.wiki; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.wiki.WikiPageInfo; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class is the controller for the wiki page listing page.delete webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class WikiPageDelete extends AbstractWikiWebScript +{ + @Override + protected Map executeImpl(SiteInfo site, String pageTitle, + WebScriptRequest req, JSONObject json, Status status, Cache cache) + { + Map model = new HashMap(); + + // Try to find the page + WikiPageInfo page = wikiService.getWikiPage(site.getShortName(), pageTitle); + if (page == null) + { + String message = "The Wiki Page could not be found"; + throw new WebScriptException(Status.STATUS_NOT_FOUND, message); + } + + // Have the page deleted + wikiService.deleteWikiPage(page); + + // Generate an activity for this + addActivityEntry("deleted", page, site, req, json); + + // Mark it as gone + status.setCode(Status.STATUS_NO_CONTENT); + return model; + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageGet.java b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageGet.java new file mode 100644 index 0000000000..3c043e74b4 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageGet.java @@ -0,0 +1,120 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.wiki; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.wiki.WikiPageInfo; +import org.apache.commons.lang.StringEscapeUtils; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class is the controller for the wiki page fetching page.get webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class WikiPageGet extends AbstractWikiWebScript +{ + private static final String MSG_NOT_FOUND= "page-not-found"; + + // For matching links. Not the best pattern ever... + private static final Pattern LINK_PATTERN = Pattern.compile("\\[\\[([^\\|#\\]]+)"); + + @Override + protected Map executeImpl(SiteInfo site, String pageTitle, + WebScriptRequest req, JSONObject json, Status status, Cache cache) + { + String strMinWikiData = req.getParameter("minWikiData"); + boolean minWikiData = strMinWikiData != null ? Boolean.parseBoolean(strMinWikiData) : false; + + final ResourceBundle rb = getResources(); + Map model = new HashMap<>(); + + // Try to find the page + WikiPageInfo page = wikiService.getWikiPage(site.getShortName(), pageTitle); + if (page == null) + { + String message = "The Wiki Page could not be found"; + status.setCode(Status.STATUS_NOT_FOUND); + status.setMessage(message); + status.setRedirect(true); + + // MNT-11595 Downgrading permission from Manager to Consumer, user still allowed to create WIKI pages + // Record these + model.put("container", site.getNodeRef()); + model.put("error", rb.getString(MSG_NOT_FOUND)); + + // Bail out + return model; + } + + + // Identify all the internal page links, valid and not + // TODO This may be a candidate for the service in future + List links = new ArrayList<>(); + List pageTitles = new ArrayList<>(); + if (page.getContents() != null) + { + Matcher m = LINK_PATTERN.matcher(page.getContents()); + while (m.find()) + { + String link = m.group(1); + if (! links.contains(link)) + { + links.add(link); + // build the list of available pages + WikiPageInfo wikiPage = wikiService.getWikiPage(site.getShortName(), StringEscapeUtils.unescapeHtml(link)); + if (wikiPage != null) + { + pageTitles.add(wikiPage.getTitle()); + } + } + } + } + + // All done + model.put("page", page); + model.put("node", page.getNodeRef()); + model.put("container", page.getContainerNodeRef()); + model.put("links", links); + model.put("pageList", pageTitles); + model.put("tags", page.getTags()); + model.put("siteId", site.getShortName()); + model.put("site", site); + model.put("minWikiData", minWikiData); + + // Double wrap + Map result = new HashMap<>(); + result.put("result", model); + return result; + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageListGet.java b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageListGet.java new file mode 100644 index 0000000000..fee4a2c1c2 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageListGet.java @@ -0,0 +1,202 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.wiki; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.admin.SysAdminParams; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.wiki.WikiServiceImpl; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.TemplateService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.wiki.WikiPageInfo; +import org.alfresco.util.UrlUtil; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class is the controller for the wiki page listing pagelist.get webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class WikiPageListGet extends AbstractWikiWebScript +{ + protected static final int RECENT_SEARCH_PERIOD_DAYS = 7; + protected static final long ONE_DAY_MS = 24*60*60*1000; + + // Injected services + private SysAdminParams sysAdminParams; + + public void setSysAdminParams(SysAdminParams sysAdminParams) + { + this.sysAdminParams = sysAdminParams; + } + + @Override + protected Map executeImpl(SiteInfo site, String pageTitle, + WebScriptRequest req, JSONObject json, Status status, Cache cache) + { + // Decide on what kind of request they wanted + String filter = req.getParameter("filter"); + String strPageMetaOnly = req.getParameter("pageMetaOnly"); + boolean pageMetaOnly = strPageMetaOnly != null ? Boolean.parseBoolean(strPageMetaOnly) : false; + + // User? + boolean userFiltering = false; + String user = null; + if ("user".equals(filter) || "myPages".equals(filter)) + { + userFiltering = true; + user = AuthenticationUtil.getFullyAuthenticatedUser(); + } + + // Date? + boolean dateFiltering = false; + boolean dateIsCreated = true; + Date from = null; + Date to = null; + if ("recentlyAdded".equals(filter) || + "recentlyCreated".equals(filter) || + "recentlyModified".equals(filter)) + { + dateFiltering = true; + if ("recentlyModified".equals(filter)) + { + dateIsCreated = false; + } + + int days = RECENT_SEARCH_PERIOD_DAYS; + String daysS = req.getParameter("days"); + if (daysS != null && daysS.length() > 0) + { + days = Integer.parseInt(daysS); + } + + Date now = new Date(); + from = new Date(now.getTime() - days*ONE_DAY_MS); + to = new Date(now.getTime() + ONE_DAY_MS); + } + + + // Get the links for the list + PagingRequest paging = buildPagingRequest(req); + PagingResults pages; + if (userFiltering) + { + pages = wikiService.listWikiPages(site.getShortName(), user, paging); + } + else if (dateFiltering) + { + if (dateIsCreated) + { + pages = wikiService.listWikiPagesByCreated(site.getShortName(), from, to, paging); + } + else + { + pages = wikiService.listWikiPagesByModified(site.getShortName(), from, to, paging); + } + } + else + { + pages = wikiService.listWikiPages(site.getShortName(), paging); + } + + + // For each one in our page, grab details of any ignored instances + List> items = new ArrayList>(); + for (WikiPageInfo page : pages.getPage()) + { + Map result = renderWikiPage(page); + items.add(result); + } + Map data = new HashMap(); + data.put("pages", items); + data.put("pageSize", paging.getMaxItems()); + data.put("startIndex", paging.getSkipCount()); + data.put("itemCount", items.size()); + + int total = items.size(); + if (pages.getTotalResultCount() != null && pages.getTotalResultCount().getFirst() != null) + { + total = pages.getTotalResultCount().getFirst(); + } + data.put("total", total); + + // We need the container node for permissions checking + NodeRef container; + if (pages.getPage().size() > 0) + { + container = pages.getPage().get(0).getContainerNodeRef(); + } + else + { + // Find the container (if it's been created yet) + container = siteService.getContainer( + site.getShortName(), WikiServiceImpl.WIKI_COMPONENT); + + if (container == null) + { + // Brand new site, no write operations on links have happened + // Fudge it for now with the site itself, the first write call + // will have the container created + container = site.getNodeRef(); + } + } + + // All done + Map wiki = new HashMap(); + wiki.put("pages", items); // Old style + wiki.put("container", container); + + if (userFiltering) + { + // We need to get all the wiki pages for "My Pages" filter otherwise + // the links for renamed wiki pages won't be rendered correctly, + // which were created by other users + pages = wikiService.listWikiPages(site.getShortName(), paging); + List pageTitles = new ArrayList(pages.getPage().size()); + for (WikiPageInfo page : pages.getPage()) + { + pageTitles.add(page.getTitle()); + } + wiki.put("pageTitles", pageTitles); + } + + Map model = new HashMap(); + model.put("data", data); // New style + model.put("wiki", wiki); + model.put("siteId", site.getShortName()); + model.put("site", site); + model.put(TemplateService.KEY_SHARE_URL, UrlUtil.getShareUrl(sysAdminParams)); + model.put("pageMetaOnly", pageMetaOnly); + return model; + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageMovePost.java b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageMovePost.java new file mode 100644 index 0000000000..53491cf756 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageMovePost.java @@ -0,0 +1,119 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.wiki; + +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; + +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.wiki.WikiPageInfo; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class is the controller for the wiki page renaming move.post webscript. + * + * TODO Track links to pages, so we can avoid creating the "This page has been moved" + * stubs as now, for cases where nothing links to the page being renamed. (ALF-3844) + * + * @author Nick Burch + * @since 4.0 + */ +public class WikiPageMovePost extends AbstractWikiWebScript +{ + private static final String MSG_MOVED = "page-moved"; + private static final String MSG_MOVED_HERE = "page-moved-here"; + private static final String MSG_NOT_FOUND= "page-not-found"; + + // The 'custom0' key here refers to the org.alfresco.wiki.page-renamed {2} in activity-list.get.properties + private static final String OLD_TITLE_KEY = "custom0"; + + @Override + protected Map executeImpl(SiteInfo site, String pageTitle, + WebScriptRequest req, JSONObject json, Status status, Cache cache) + { + final Map model = new HashMap(); + final ResourceBundle rb = getResources(); + + // Try to find the page we're renaming + WikiPageInfo page = wikiService.getWikiPage(site.getShortName(), pageTitle); + if (page == null) + { + String message = "The Wiki Page could not be found"; + status.setCode(Status.STATUS_NOT_FOUND); + status.setMessage(message); + + // Wrap and bail + model.put("error", rb.getString(MSG_NOT_FOUND)); + Map result = new HashMap(); + result.put("result", model); + return result; + } + + + // Grab the new Title + // The "name" in the JSON is actually the title! + String newTitle = (String)json.get("name"); + + + // Have the page re-named, if possible + String oldTitle = page.getTitle().length() == 0 ? pageTitle : page.getTitle(); + try + { + page.setTitle(newTitle); + page = wikiService.updateWikiPage(page); + } + catch (FileExistsException e) + { + throw new WebScriptException(Status.STATUS_CONFLICT, "Duplicate page name"); + } + + + // Create the "This page has been moved" entry for the old page + String movedContent = rb.getString(MSG_MOVED) + " [[" + page.getTitle() + + "|" + rb.getString(MSG_MOVED_HERE) + "]]."; + wikiService.createWikiPage(site.getShortName(), oldTitle, movedContent); + + Map additionalData = new HashMap(); + additionalData.put(OLD_TITLE_KEY, oldTitle); + + // Add an activity entry for the rename + addActivityEntry("renamed", page, site, req, json, additionalData); + + + // All done + model.put("name", page.getSystemName()); + model.put("title", page.getTitle()); + model.put("page", page); + model.put("siteId", site.getShortName()); + model.put("site", site); + + // Double wrap + Map result = new HashMap(); + result.put("result", model); + return result; + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPagePut.java b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPagePut.java new file mode 100644 index 0000000000..3784d18cb2 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPagePut.java @@ -0,0 +1,191 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.wiki; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.cmr.wiki.WikiPageInfo; +import org.alfresco.service.namespace.QName; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class is the controller for the wiki page creating/editing page.put webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class WikiPagePut extends AbstractWikiWebScript +{ + // Implementation note: the wiki page webscripts do not follow the standard Alfresco convention whereby + // POST = CREATE and PUT = UPDATE. + // In this case, put is used for both create and update. + + private VersionService versionService; + public void setVersionService(VersionService versionService) + { + this.versionService = versionService; + } + + @Override + protected Map executeImpl(SiteInfo site, String pageTitle, + WebScriptRequest req, JSONObject json, Status status, Cache cache) + { + Map model = new HashMap<>(); + + // Grab the details of the change + // Fetch the contents + String contents = (String)json.get("pagecontent"); + + // Fetch the title, used only when creating + String title; + if (json.containsKey("title")) + { + title = (String)json.get("title"); + } + else + { + title = pageTitle; + } + + // Fetch the versioning details + boolean forceSave = json.containsKey("forceSave"); + String currentVersion = null; + if (json.containsKey("currentVersion")) + { + currentVersion = (String)json.get("currentVersion"); + } + + // Fetch the tags, if given + List tags = null; + if (json.containsKey("tags")) + { + tags = new ArrayList<>(); + if (!json.get("tags").equals("")) + { + // Array of tags + JSONArray tagsA = (JSONArray)json.get("tags"); + for (int i=0; i result = new HashMap<>(); + result.put("result", model); + return result; + } + + private boolean pageVersionMatchesSubmitted(WikiPageInfo page, String currentVersion) + { + // If they didn't give version, it can't be right + if (currentVersion == null) + { + return false; + } + + // Grab the current version + Version version = versionService.getCurrentVersion(page.getNodeRef()); + if (version == null) + { + // It should be versioned already, fix that + makeVersioned(page); + + // Wasn't versioned before, so can't detect conflict + return true; + } + + return version.getVersionLabel().equals(currentVersion); + } + + private void makeVersioned(WikiPageInfo page) + { + Map versionProps = new HashMap<>(); + versionProps.put(ContentModel.PROP_AUTO_VERSION, true); + versionProps.put(ContentModel.PROP_AUTO_VERSION_PROPS, true); + versionService.ensureVersioningEnabled(page.getNodeRef(), versionProps); + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageVersionGet.java b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageVersionGet.java new file mode 100644 index 0000000000..d00faa1602 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/web/scripts/wiki/WikiPageVersionGet.java @@ -0,0 +1,170 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.wiki; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.AspectMissingException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionDoesNotExistException; +import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.cmr.wiki.WikiPageInfo; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class is the controller for the wiki page version fetching version.get webscript. + * + * @author Nick Burch + * @since 4.0 + */ +public class WikiPageVersionGet extends AbstractWikiWebScript +{ + // For spotting if a version string is an ID or a Label + private static final Pattern LABEL_PATTERN = Pattern.compile("\\d+\\.\\d+"); + + private static final String PARAM_CONTENT = "content"; + + private ContentService contentService; + private VersionService versionService; + + public void setVersionService(VersionService versionService) + { + this.versionService = versionService; + } + + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + @Override + protected Map executeImpl(SiteInfo site, String pageTitle, + WebScriptRequest req, JSONObject json, Status status, Cache cache) + { + Map model = new HashMap<>(); + + // Grab the version string + Map templateVars = req.getServiceMatch().getTemplateVars(); + String versionId = templateVars.get("versionId"); + if (versionId == null) + { + String error = "No versionId supplied"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + // Try to find the page + WikiPageInfo page = wikiService.getWikiPage(site.getShortName(), pageTitle); + if (page == null) + { + String message = "The Wiki Page could not be found"; + status.setCode(Status.STATUS_NOT_FOUND); + status.setMessage(message); + + // Return an empty string though + model.put(PARAM_CONTENT, ""); + return model; + } + + + // Fetch the version history for the node + VersionHistory versionHistory = null; + Version version = null; + try + { + versionHistory = versionService.getVersionHistory(page.getNodeRef()); + } + catch (AspectMissingException e) {} + + if (versionHistory == null) + { + // Not been versioned, return an empty string + model.put(PARAM_CONTENT, ""); + return model; + } + + + // Fetch the version by either ID or Label + Matcher m = LABEL_PATTERN.matcher(versionId); + if (m.matches()) + { + // It's a version label like 2.3 + try + { + version = versionHistory.getVersion(versionId); + } + catch (VersionDoesNotExistException e) {} + } + else + { + // It's a version ID like ed00bac1-f0da-4042-8598-45a0d39cb74d + // (The ID is usually part of the NodeRef of the frozen node, but we + // don't assume to be able to just generate the full NodeRef) + for (Version v : versionHistory.getAllVersions()) + { + if (v.getFrozenStateNodeRef().getId().equals(versionId)) + { + version = v; + } + } + } + + + // Did we find the right version in the end? + String contents; + if (version != null) + { + ContentReader reader = contentService.getReader(version.getFrozenStateNodeRef(), ContentModel.PROP_CONTENT); + if (reader != null) + { + contents = reader.getContentString(); + } + else + { + // No content was stored in the version history + contents = ""; + } + } + else + { + // No warning of the missing version, just return an empty string + contents = ""; + } + + // All done + model.put(PARAM_CONTENT, contents); + model.put("page", page); + model.put("site", site); + model.put("siteId", site.getShortName()); + return model; + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/wiki/WikiPageInfoImpl.java b/amps/share-services/src/main/java/org/alfresco/repo/wiki/WikiPageInfoImpl.java new file mode 100644 index 0000000000..663ec714e9 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/wiki/WikiPageInfoImpl.java @@ -0,0 +1,162 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.wiki; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.wiki.WikiPageInfo; + +/** + * An implementation of {@link WikiPageInfo} + * + * @author Nick Burch (based on existing webscript controllers in the REST API) + * @since 4.0 + */ +public class WikiPageInfoImpl implements WikiPageInfo +{ + private NodeRef nodeRef; + private NodeRef containerNodeRef; + private String systemName; + private String title; + private String contents; + private String creator; + private String modifier; + private Date createdAt; + private Date modifiedAt; + private List tags = new ArrayList(); + + /** + * Creates a new, empty WikiPageInfo + */ + public WikiPageInfoImpl() + { + } + + /** + * Create a WikiPageInfo object from an existing node + */ + public WikiPageInfoImpl(NodeRef nodeRef, NodeRef containerNodeRef, String systemName) + { + this.nodeRef = nodeRef; + this.containerNodeRef = containerNodeRef; + this.systemName = systemName; + } + + @Override + public NodeRef getContainerNodeRef() + { + return containerNodeRef; + } + + @Override + public NodeRef getNodeRef() + { + return nodeRef; + } + + @Override + public String getSystemName() + { + return systemName; + } + + @Override + public String getTitle() + { + return title; + } + + @Override + public String getContents() + { + return contents; + } + + @Override + public String getCreator() + { + return creator; + } + + @Override + public String getModifier() + { + return modifier; + } + + @Override + public Date getCreatedAt() + { + return createdAt; + } + + @Override + public Date getModifiedAt() + { + return modifiedAt; + } + + @Override + public List getTags() + { + return tags; + } + + @Override + public void setTitle(String title) + { + this.title = title; + } + + @Override + public void setContents(String contents) + { + this.contents = contents; + } + + public void setCreator(String creator) + { + this.creator = creator; + } + + public void setModifier(String modifier) + { + this.modifier = modifier; + } + + public void setCreatedAt(Date createdAt) + { + this.createdAt = createdAt; + } + + public void setModifiedAt(Date modifiedAt) + { + this.modifiedAt = modifiedAt; + } + + public void setTags(List tags) + { + this.tags = tags; + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/repo/wiki/WikiServiceImpl.java b/amps/share-services/src/main/java/org/alfresco/repo/wiki/WikiServiceImpl.java new file mode 100644 index 0000000000..b8b0edea69 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/repo/wiki/WikiServiceImpl.java @@ -0,0 +1,410 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.wiki; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.query.CannedQueryFactory; +import org.alfresco.query.CannedQueryResults; +import org.alfresco.query.CannedQuerySortDetails; +import org.alfresco.query.EmptyPagingResults; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.node.getchildren.GetChildrenAuditableCannedQuery; +import org.alfresco.repo.node.getchildren.GetChildrenAuditableCannedQueryFactory; +import org.alfresco.repo.query.NodeBackedEntity; +import org.alfresco.repo.site.SiteServiceImpl; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.tagging.TaggingService; +import org.alfresco.service.cmr.wiki.WikiPageInfo; +import org.alfresco.service.cmr.wiki.WikiService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.Pair; +import org.alfresco.util.registry.NamedObjectRegistry; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author Nick Burch (based on existing webscript controllers in the REST API) + * @since 4.0 + */ +public class WikiServiceImpl implements WikiService +{ + public static final String WIKI_COMPONENT = "wiki"; + + protected static final String CANNED_QUERY_GET_CHILDREN = "wikiGetChildrenCannedQueryFactory"; + + /** + * The logger + */ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(WikiServiceImpl.class); + + private NodeDAO nodeDAO; + private NodeService nodeService; + private SiteService siteService; + private ContentService contentService; + private TaggingService taggingService; + private FileFolderService fileFolderService; + private TransactionService transactionService; + private NamedObjectRegistry> cannedQueryRegistry; + + public void setNodeDAO(NodeDAO nodeDAO) + { + this.nodeDAO = nodeDAO; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + public void setTaggingService(TaggingService taggingService) + { + this.taggingService = taggingService; + } + + public void setFileFolderService(FileFolderService fileFolderService) + { + this.fileFolderService = fileFolderService; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + /** + * Set the registry of {@link CannedQueryFactory canned queries} + */ + public void setCannedQueryRegistry(NamedObjectRegistry> cannedQueryRegistry) + { + this.cannedQueryRegistry = cannedQueryRegistry; + } + + /** + * Fetches the Wiki Container on a site, creating as required if requested. + */ + protected NodeRef getSiteWikiContainer(final String siteShortName, boolean create) + { + return SiteServiceImpl.getSiteContainer( + siteShortName, WIKI_COMPONENT, create, + siteService, transactionService, taggingService); + } + + /** + * Turns a Title into a Page Name. + */ + private static String buildName(String title) + { + // The name is based on the title, but with underscores + String name = title.replace(' ', '_'); + name = name.replaceAll("\"", "%22"); + name = name.replaceAll("[*]", "%2a"); + name = name.replaceAll("<", "%3c"); + name = name.replaceAll(">", "%3e"); + name = name.replaceAll(":", "%3a"); + name = name.replaceAll("([.]?[.]+$)", "%2e"); + return name; + } + + private WikiPageInfo buildPage(NodeRef nodeRef, NodeRef container, String name, String preLoadedContents) + { + WikiPageInfoImpl page = new WikiPageInfoImpl(nodeRef, container, name); + + // Grab all the properties, we need the bulk of them anyway + Map props = nodeService.getProperties(nodeRef); + + // Start with the auditable properties + page.setCreator((String)props.get(ContentModel.PROP_CREATOR)); + page.setModifier((String)props.get(ContentModel.PROP_MODIFIER)); + page.setCreatedAt((Date)props.get(ContentModel.PROP_CREATED)); + page.setModifiedAt((Date)props.get(ContentModel.PROP_MODIFIED)); + + // Now the wiki ones + page.setTitle(((String)props.get(ContentModel.PROP_TITLE)).replaceAll(" ", "_")); + + // Finally, do the content + String contents = preLoadedContents; + if (contents == null) + { + ContentReader reader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); + if (reader != null) + { + contents = reader.getContentString(); + } + } + page.setContents(contents); + + // Finally tags + page.setTags(taggingService.getTags(nodeRef)); + + // All done + return page; + } + + + @Override + public WikiPageInfo getWikiPage(String siteShortName, String pageTitle) + { + NodeRef container = getSiteWikiContainer(siteShortName, false); + if (container == null) + { + // No links + return null; + } + + String pageName = buildName(pageTitle); + NodeRef link = nodeService.getChildByName(container, ContentModel.ASSOC_CONTAINS, pageName); + if (link != null) + { + return buildPage(link, container, pageName, null); + } + return null; + } + + @Override + public WikiPageInfo createWikiPage(String siteShortName, String title, + String content) + { + // Grab the location to store in + NodeRef container = getSiteWikiContainer(siteShortName, true); + + // Build the name + String name = buildName(title); + + // Get the properties for the node + Map props = new HashMap(); + props.put(ContentModel.PROP_NAME, name); + props.put(ContentModel.PROP_TITLE, title); + + // Build the node + NodeRef nodeRef = nodeService.createNode( + container, + ContentModel.ASSOC_CONTAINS, + QName.createQName(name), + ContentModel.TYPE_CONTENT, + props + ).getChildRef(); + + // Store the content + ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_HTML); + writer.setEncoding("UTF-8"); + writer.putContent(content); + + // Generate the wrapping object for it + // Build it that way, so creator and created date come through + return buildPage(nodeRef, container, name, content); + } + + @Override + public WikiPageInfo updateWikiPage(WikiPageInfo page) + { + // Sanity check what we were given + if (page.getNodeRef() == null) + { + throw new IllegalArgumentException("Can't update a page that was never persisted, call create instead"); + } + + NodeRef nodeRef = page.getNodeRef(); + String nodeName = buildName(page.getTitle()); + + // Handle the rename case + boolean renamed = false; + if (! nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE).equals(page.getTitle())) + { + try + { + fileFolderService.rename(nodeRef, nodeName); + renamed = true; + } + catch (FileNotFoundException e) + { + throw new AlfrescoRuntimeException("Invalid node state - wiki page no longer found"); + } + nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, nodeName); + nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, page.getTitle()); + } + + // Change the content + ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_HTML); + writer.setEncoding("UTF-8"); + writer.putContent(page.getContents()); + + // Now do the tags + taggingService.setTags(nodeRef, page.getTags()); + + // If we re-named, re-create the object + if (renamed) + { + page = buildPage(nodeRef, page.getContainerNodeRef(), nodeName, page.getContents()); + } + + // All done + return page; + } + + @Override + public void deleteWikiPage(WikiPageInfo page) + { + if (page.getNodeRef() == null) + { + throw new IllegalArgumentException("Can't delete a wiki page that was never persisted"); + } + + nodeService.deleteNode(page.getNodeRef()); + } + + @Override + public PagingResults listWikiPages(String siteShortName, PagingRequest paging) + { + return listWikiPages(siteShortName, null, paging); + } + + @Override + public PagingResults listWikiPages(String siteShortName, String user, + PagingRequest paging) + { + return listWikiPages(siteShortName, user, null, null, null, null, paging); + } + + @Override + public PagingResults listWikiPagesByCreated(String siteShortName, + Date from, Date to, PagingRequest paging) + { + return listWikiPages(siteShortName, null, from, to, null, null, paging); + } + + @Override + public PagingResults listWikiPagesByModified(String siteShortName, + Date from, Date to, PagingRequest paging) + { + return listWikiPages(siteShortName, null, null, null, from, to, paging); + } + + public PagingResults listWikiPages(String siteShortName, String username, + Date createdFrom, Date createdTo, Date modifiedFrom, Date modifiedTo, PagingRequest paging) + { + NodeRef container = getSiteWikiContainer(siteShortName, false); + if (container == null) + { + // No events + return new EmptyPagingResults(); + } + + // Grab the factory + GetChildrenAuditableCannedQueryFactory getChildrenCannedQueryFactory = + (GetChildrenAuditableCannedQueryFactory)cannedQueryRegistry.getNamedObject(CANNED_QUERY_GET_CHILDREN); + + // Do the sorting, newest first by created date + CannedQuerySortDetails sorting = getChildrenCannedQueryFactory.createDateDescendingCQSortDetails(); + + // Run the canned query + GetChildrenAuditableCannedQuery cq = (GetChildrenAuditableCannedQuery)getChildrenCannedQueryFactory.getCannedQuery( + container, ContentModel.TYPE_CONTENT, username, createdFrom, createdTo, null, + modifiedFrom, modifiedTo, sorting, paging); + + // Execute the canned query + CannedQueryResults results = cq.execute(); + + // Convert to Link objects + return wrap(results, container); + } + + /** + * Our class to wrap up paged results of NodeBackedEntities as + * WikiPageInfo instances + */ + private PagingResults wrap(final PagingResults results, final NodeRef container) + { + // Pre-load the nodes before we create them + List ids = new ArrayList(); + for (NodeBackedEntity node : results.getPage()) + { + ids.add(node.getId()); + } + nodeDAO.cacheNodesById(ids); + + // Wrap + return new PagingResults() + { + @Override + public String getQueryExecutionId() + { + return results.getQueryExecutionId(); + } + + @Override + public List getPage() + { + List pages = new ArrayList(); + for (NodeBackedEntity node : results.getPage()) + { + NodeRef nodeRef = node.getNodeRef(); + String name = node.getName(); + pages.add(buildPage(nodeRef, container, name, null)); + } + return pages; + } + + @Override + public boolean hasMoreItems() + { + return results.hasMoreItems(); + } + + @Override + public Pair getTotalResultCount() + { + return results.getTotalResultCount(); + } + }; + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/service/cmr/wiki/WikiPageInfo.java b/amps/share-services/src/main/java/org/alfresco/service/cmr/wiki/WikiPageInfo.java new file mode 100644 index 0000000000..982d1364a0 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/service/cmr/wiki/WikiPageInfo.java @@ -0,0 +1,97 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.service.cmr.wiki; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +import org.alfresco.repo.security.permissions.PermissionCheckValue; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * This class represents a Wiki Paeg in a site + * + * @author Nick Burch + * @since 4.0 + */ +public interface WikiPageInfo extends Serializable, PermissionCheckValue +{ + /** + * @return the NodeRef of the underlying wiki page + */ + NodeRef getNodeRef(); + + /** + * @return the NodeRef of the site container this belongs to + */ + NodeRef getContainerNodeRef(); + + /** + * @return the name of the wiki page + */ + String getSystemName(); + + /** + * @return the Title of the wiki page + */ + String getTitle(); + + /** + * Sets the Title of the wiki page + */ + void setTitle(String title); + + /** + * @return the HTML Content of the wiki page + */ + String getContents(); + + /** + * Sets the (HTML) Content of the wiki page + */ + void setContents(String contentHTML); + + /** + * @return the creator of the wiki page + */ + String getCreator(); + + /** + * @return the modifier of the wiki page + */ + String getModifier(); + + /** + * @return the creation date and time + */ + Date getCreatedAt(); + + /** + * @return the modification date and time + */ + Date getModifiedAt(); + + /** + * @return the Tags associated with the wiki page + */ + List getTags(); +} diff --git a/amps/share-services/src/main/java/org/alfresco/service/cmr/wiki/WikiService.java b/amps/share-services/src/main/java/org/alfresco/service/cmr/wiki/WikiService.java new file mode 100644 index 0000000000..7e624632f1 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/service/cmr/wiki/WikiService.java @@ -0,0 +1,93 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.service.cmr.wiki; + +import java.util.Date; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.service.NotAuditable; + +/** + * The Wiki service. + * + * @author Nick Burch + * @since 4.0 + */ +public interface WikiService +{ + /** + * Creates a new {@link WikiPageInfo} in the given site, with the + * specified contents + * + * @return The newly created {@link WikiPageInfo} + */ + @NotAuditable + WikiPageInfo createWikiPage(String siteShortName, String title, String contents); + + /** + * Updates an existing {@link WikiPageInfo} in the repository. + * + * @return The updated {@link WikiPageInfo} + */ + @NotAuditable + WikiPageInfo updateWikiPage(WikiPageInfo wikiPage); + + /** + * Deletes an existing {@link WikiPageInfo} from the repository + */ + @NotAuditable + void deleteWikiPage(WikiPageInfo wikiPage); + + /** + * Retrieves an existing {@link WikiPageInfo} from the repository + */ + @NotAuditable + WikiPageInfo getWikiPage(String siteShortName, String pageName); + + /** + * Retrieves all {@link WikiPageInfo} instances in the repository + * for the given site. + */ + @NotAuditable + PagingResults listWikiPages(String siteShortName, PagingRequest paging); + + /** + * Retrieves all {@link WikiPageInfo} instances in the repository + * for the given site and the specified user. + */ + @NotAuditable + PagingResults listWikiPages(String siteShortName, String user, PagingRequest paging); + + /** + * Retrieves all {@link WikiPageInfo} instances in the repository + * for the given site, created in the specified date range + */ + @NotAuditable + PagingResults listWikiPagesByCreated(String siteShortName, Date from, Date to, PagingRequest paging); + + /** + * Retrieves all {@link WikiPageInfo} instances in the repository + * for the given site, modified in the specified date range + */ + @NotAuditable + PagingResults listWikiPagesByModified(String siteShortName, Date from, Date to, PagingRequest paging); +} diff --git a/amps/share-services/src/main/java/org/alfresco/slingshot/web/scripts/NodeBrowserScript.java b/amps/share-services/src/main/java/org/alfresco/slingshot/web/scripts/NodeBrowserScript.java new file mode 100644 index 0000000000..bf4042e72b --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/slingshot/web/scripts/NodeBrowserScript.java @@ -0,0 +1,222 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.slingshot.web.scripts; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.web.scripts.admin.NodeBrowserPost; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.LimitBy; +import org.alfresco.service.cmr.search.SearchParameters; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Node browser web script to handle search results, node details and workspaces. + * Extends the NodeBrowserPost script to inherit useful helper classes. + * + * @author dcaruana + * @author wabson + */ +public class NodeBrowserScript extends NodeBrowserPost implements Serializable +{ + private static final long serialVersionUID = 48743409337475896L; + + private Long searchElapsedTime = null; + + /** + * Action to submit search + * + * @return next action + */ + public List submitSearch(final String store, final String query, final String queryLanguage, final int maxResults) throws IOException + { + long start = System.currentTimeMillis(); + final StoreRef storeRef = new StoreRef(store); + RetryingTransactionCallback> searchCallback = new RetryingTransactionCallback>() + { + public List execute() throws Throwable + { + List searchResults = null; + + if (queryLanguage.equals("storeroot")) + { + NodeRef rootNodeRef = getNodeService().getRootNode(storeRef); + searchResults = new ArrayList(1); + searchResults.add(new Node(rootNodeRef)); + return searchResults; + } + else if (queryLanguage.equals("noderef")) + { + // ensure node exists + NodeRef nodeRef = new NodeRef(query); + boolean exists = getNodeService().exists(nodeRef); + if (!exists) + { + throw new WebScriptException(500, "Node " + nodeRef + " does not exist."); + } + searchResults = new ArrayList(1); + searchResults.add(new Node(nodeRef)); + return searchResults; + } + SearchParameters sp = new SearchParameters(); + sp.addStore(storeRef); + sp.setLanguage(queryLanguage); + sp.setQuery(query); + if (maxResults > 0) + { + sp.setLimit(maxResults); + sp.setLimitBy(LimitBy.FINAL_SIZE); + } + + // perform search + List nodeRefs = getSearchService().query(sp).getNodeRefs(); + searchResults = new ArrayList(nodeRefs.size()); + for (NodeRef nodeRef : nodeRefs) { + searchResults.add(new Node(nodeRef)); + } + return searchResults; + } + }; + + try + { + List results = getTransactionService().getRetryingTransactionHelper().doInTransaction(searchCallback, true); + this.searchElapsedTime = System.currentTimeMillis() - start; + return results; + } + catch (Throwable e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * @return the searchElapsedTime + */ + protected Long getSearchElapsedTime() + { + return this.searchElapsedTime; + } + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + if (req.getPathInfo().equals("/slingshot/node/search")) + { + List nodes; + Map tmplMap = new HashMap(1); + try + { + if (req.getParameter("store") == null || req.getParameter("store").length() == 0) + { + status.setCode(HttpServletResponse.SC_BAD_REQUEST); + status.setMessage("Store name not provided"); + status.setRedirect(true); + return null; + } + if (req.getParameter("q") == null || req.getParameter("q").length() == 0) + { + status.setCode(HttpServletResponse.SC_BAD_REQUEST); + status.setMessage("Search query not provided"); + status.setRedirect(true); + return null; + } + if (req.getParameter("lang") == null || req.getParameter("lang").length() == 0) + { + status.setCode(HttpServletResponse.SC_BAD_REQUEST); + status.setMessage("Search language not provided"); + status.setRedirect(true); + return null; + } + + int maxResult = 0; + try + { + maxResult = Integer.parseInt(req.getParameter("maxResults")); + } + catch (NumberFormatException ex) + { + } + + nodes = submitSearch(req.getParameter("store"), req.getParameter("q"), req.getParameter("lang"), maxResult); + tmplMap.put("results", nodes); + tmplMap.put("searchElapsedTime", getSearchElapsedTime()); + } + catch (IOException e) + { + status.setCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + status.setMessage(e.getMessage()); + status.setException(e); + status.setRedirect(true); + } + return tmplMap; + } + else if (req.getPathInfo().equals("/slingshot/node/stores")) + { + Map model = new HashMap(); + model.put("stores", getStores()); + return model; + } + else // Assume we are looking for a node + { + Map templateVars = req.getServiceMatch().getTemplateVars(); + if (templateVars.get("protocol") == null || templateVars.get("protocol").length() == 0 || + templateVars.get("store") == null || templateVars.get("store").length() == 0 || + templateVars.get("id") == null || templateVars.get("id").length() == 0) + { + status.setCode(HttpServletResponse.SC_BAD_REQUEST); + status.setMessage("Node not provided"); + status.setRedirect(true); + return null; + } + NodeRef nodeRef = new NodeRef(templateVars.get("protocol"), templateVars.get("store"), templateVars.get("id")); + + Map permissionInfo = new HashMap(3); + permissionInfo.put("entries", getPermissions(nodeRef)); + permissionInfo.put("owner", this.getOwnableService().getOwner(nodeRef)); + permissionInfo.put("inherit", this.getInheritPermissions(nodeRef)); + permissionInfo.put("storePermissions", getStorePermissionMasks(nodeRef)); + + Map model = new HashMap(); + model.put("node", new Node(nodeRef)); + model.put("properties", getProperties(nodeRef)); + model.put("aspects", getAspects(nodeRef)); + model.put("children", getChildren(nodeRef)); + model.put("parents", getParents(nodeRef)); + model.put("assocs", getAssocs(nodeRef)); + model.put("sourceAssocs", getSourceAssocs(nodeRef)); + model.put("permissions", permissionInfo); + return model; + } + } +} diff --git a/amps/share-services/src/main/java/org/alfresco/slingshot/web/scripts/SlingshotContentGet.java b/amps/share-services/src/main/java/org/alfresco/slingshot/web/scripts/SlingshotContentGet.java new file mode 100644 index 0000000000..609c906db8 --- /dev/null +++ b/amps/share-services/src/main/java/org/alfresco/slingshot/web/scripts/SlingshotContentGet.java @@ -0,0 +1,135 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.slingshot.web.scripts; + +import java.io.IOException; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.sync.repo.Client; +import org.alfresco.sync.repo.Client.ClientType; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.web.scripts.content.ContentGet; +import org.alfresco.service.cmr.activities.ActivityPoster; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; + +/** + * Share specific ContentGet implementation. + *

+ * Checks to see if: + * a) the request is an explicit download (attachment) + * b) the requested NodeRef within the context of a Share Site + *

+ * If both tests are true then generates an Activity feed item to record the Download request. + * All other requests and any further processing is performed by the super class. + * + * @author Kevin Roast + */ +public class SlingshotContentGet extends ContentGet +{ + protected SiteService siteService; + private ActivityPoster poster; + private RetryingTransactionHelper transactionHelper; + + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + public void setPoster(ActivityPoster poster) + { + this.poster = poster; + } + + public void setTransactionHelper(RetryingTransactionHelper transactionHelper) + { + this.transactionHelper = transactionHelper; + } + + + @Override + public void execute(final WebScriptRequest req, final WebScriptResponse res) throws IOException + { + // are we downloading content as an attachment? + if (Boolean.valueOf(req.getParameter("a"))) + { + // is this private ActivityPoster poster; node part of a Site context? + Map templateVars = req.getServiceMatch().getTemplateVars(); + String storeType = templateVars.get("store_type"); + String storeId = templateVars.get("store_id"); + String nodeId = templateVars.get("id"); + + // create the NodeRef and ensure it is valid + if (storeType != null && storeId != null && nodeId != null) + { + // MNT-16380 + String nodeIdTmp = nodeId; + if (nodeId.contains("/")) + { + nodeIdTmp = nodeId.substring(0, nodeId.indexOf('/')); + } + final NodeRef nodeRef = new NodeRef(storeType, storeId, nodeIdTmp); + SiteInfo site = null; + try + { + site = this.siteService.getSite(nodeRef); + } + catch (AccessDeniedException ade) + { + // We don't have access to the site, don't post any permissions + } + if (site != null) + { + // found a valid parent Site - gather the details to post an Activity + String filename = templateVars.get("filename"); + if (filename == null || filename.length() == 0) + { + filename = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + if (nodeId.contains("/")) + { + filename = nodeId.substring(nodeId.lastIndexOf("/") + 1); + } + } + final String strFilename = filename; + final String siteName = site.getShortName(); + transactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + // post an activity - mirror the mechanism as if from the Share application + poster.postFileFolderActivity(ActivityPoster.DOWNLOADED, null, null, + siteName, null, nodeRef, strFilename, "documentlibrary", Client.asType(ClientType.webclient), null); + return null; + } + }, false, true); + } + } + } + super.execute(req, res); + } +} diff --git a/amps/share-services/src/main/resources/alfresco/bootstrap/team-sample-sites/swsdp/Contents.acp b/amps/share-services/src/main/resources/alfresco/bootstrap/team-sample-sites/swsdp/Contents.acp new file mode 100644 index 0000000000..417cd8296a Binary files /dev/null and b/amps/share-services/src/main/resources/alfresco/bootstrap/team-sample-sites/swsdp/Contents.acp differ diff --git a/amps/share-services/src/main/resources/alfresco/bootstrap/team-sample-sites/swsdp/Groups.txt b/amps/share-services/src/main/resources/alfresco/bootstrap/team-sample-sites/swsdp/Groups.txt new file mode 100644 index 0000000000..971f4a75e6 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/bootstrap/team-sample-sites/swsdp/Groups.txt @@ -0,0 +1,2 @@ +abeecher=GROUP_site_swsdp_SiteCollaborator +mjackson=GROUP_site_swsdp_SiteManager diff --git a/amps/share-services/src/main/resources/alfresco/bootstrap/team-sample-sites/swsdp/People.acp b/amps/share-services/src/main/resources/alfresco/bootstrap/team-sample-sites/swsdp/People.acp new file mode 100644 index 0000000000..bbe9169e9b Binary files /dev/null and b/amps/share-services/src/main/resources/alfresco/bootstrap/team-sample-sites/swsdp/People.acp differ diff --git a/amps/share-services/src/main/resources/alfresco/bootstrap/team-sample-sites/swsdp/Users.acp b/amps/share-services/src/main/resources/alfresco/bootstrap/team-sample-sites/swsdp/Users.acp new file mode 100644 index 0000000000..0ee3915344 Binary files /dev/null and b/amps/share-services/src/main/resources/alfresco/bootstrap/team-sample-sites/swsdp/Users.acp differ diff --git a/amps/share-services/src/main/resources/alfresco/messages/data-list-model.properties b/amps/share-services/src/main/resources/alfresco/messages/data-list-model.properties new file mode 100755 index 0000000000..2d34421b42 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/messages/data-list-model.properties @@ -0,0 +1,114 @@ +# Display labels for Share Data Lists Model +dl_datalistmodel.description=Alfresco Share Data List Model + +dl_datalistmodel.type.dl_dataList.title=Data List folder type +dl_datalistmodel.type.dl_dataList.description=Holds Data List items of the type specified in the dl:dataListItemType property. +dl_datalistmodel.property.dl_dataListItemType.title=Data List Item Type +dl_datalistmodel.property.dl_dataListItemType.description=Determines which subtype of dl:dataListItem will be used when create new items within the Data List. + +dl_datalistmodel.type.dl_dataListItem.title=Data List parent type +dl_datalistmodel.type.dl_dataListItem.description=Parent type from which sample Data List Item types are derived. + +## Simple "To do" List +dl_datalistmodel.type.dl_todoList.title=To Do List +dl_datalistmodel.type.dl_todoList.description=A simple to do list with optional assignee. +dl_datalistmodel.property.dl_todoTitle.title=Title +dl_datalistmodel.property.dl_todoDueDate.title=Due Date +dl_datalistmodel.property.dl_todoPriority.title=Priority +dl_datalistmodel.property.dl_todoStatus.title=Status +dl_datalistmodel.property.dl_todoNotes.title=Notes +dl_datalistmodel.association.dl_assignee.title=Assignee +dl_datalistmodel.association.dl_attachments.title=Attachments + +# Gantt Aspect +dl_datalistmodel.property.dl_ganttStartDate.title=Start Date +dl_datalistmodel.property.dl_ganttEndDate.title=End Date +dl_datalistmodel.property.dl_ganttPercentComplete.title=% Complete + +# Task List (Advanced) +dl_datalistmodel.type.dl_task.title=Task List (Advanced) +dl_datalistmodel.type.dl_task.description=Advanced tasks list including title, description, start and end dates, priority, status, comments, assignees and attachments. +dl_datalistmodel.property.dl_taskPriority.title=Priority +dl_datalistmodel.property.dl_taskStatus.title=Status +dl_datalistmodel.property.dl_taskComments.title=Comments +dl_datalistmodel.association.dl_taskAssignee.title=Assignee + +# Task List (Simple) +dl_datalistmodel.type.dl_simpletask.title=Task List (Simple) +dl_datalistmodel.type.dl_simpletask.description=Simple tasks list including title, description, due date, priority, status, comments. +dl_datalistmodel.property.dl_simpletaskDueDate.title=Due Date +dl_datalistmodel.property.dl_simpletaskPriority.title=Priority +dl_datalistmodel.property.dl_simpletaskStatus.title=Status +dl_datalistmodel.property.dl_simpletaskComments.title=Comments + +# Contact +dl_datalistmodel.type.dl_contact.title=Contact List +dl_datalistmodel.type.dl_contact.description=Contacts list including first name, last name, full name, email, job title, phone (office), phone (mobile). +dl_datalistmodel.property.dl_contactFirstName.title=First Name +dl_datalistmodel.property.dl_contactLastName.title=Last Name +dl_datalistmodel.property.dl_contactEmail.title=Email +dl_datalistmodel.property.dl_contactCompany.title=Company +dl_datalistmodel.property.dl_contactJobTitle.title=Job Title +dl_datalistmodel.property.dl_contactPhoneOffice.title=Phone (Office) +dl_datalistmodel.property.dl_contactPhoneMobile.title=Phone (Mobile) +dl_datalistmodel.property.dl_contactNotes.title=Notes + +# Issues +dl_datalistmodel.type.dl_issue.title=Issue List +dl_datalistmodel.type.dl_issue.description=Issues list including ID, status, priority, description, due data, comments, assign to, related issues. +dl_datalistmodel.property.dl_issueID.title=Issue ID +dl_datalistmodel.property.dl_issueStatus.title=Status +dl_datalistmodel.property.dl_issuePriority.title=Priority +dl_datalistmodel.property.dl_issueDescription.title=Description +dl_datalistmodel.property.dl_issueDueDate.title=Due Date +dl_datalistmodel.property.dl_issueComments.title=Comments +dl_datalistmodel.association.dl_issueAssignedTo.title=Assigned To +dl_datalistmodel.property.dl_issueRelatedIssues.title=Related Issues + +# Event +dl_datalistmodel.type.dl_event.title=Event List +dl_datalistmodel.type.dl_event.description=Events list including title, description, location, start and end date/time. +dl_datalistmodel.property.dl_eventLocation.title=Location +dl_datalistmodel.property.dl_eventStartDate.title=Start Date +dl_datalistmodel.property.dl_eventEndDate.title=End Date +dl_datalistmodel.property.dl_eventRegistrations.title=Registrations +dl_datalistmodel.property.dl_eventNote.title=Notes + +# Location +dl_datalistmodel.type.dl_location.title=Location List +dl_datalistmodel.type.dl_location.description=Locations/Addresses list +dl_datalistmodel.property.dl_locationAddress1.title=Address Line 1 +dl_datalistmodel.property.dl_locationAddress2.title=Address Line 2 +dl_datalistmodel.property.dl_locationAddress3.title=Address Line 3 +dl_datalistmodel.property.dl_locationZip.title=Zip/Post Code +dl_datalistmodel.property.dl_locationState.title=State/County +dl_datalistmodel.property.dl_locationCountry.title=Country +dl_datalistmodel.property.dl_locationNote.title=Notes + +# Meeting Agenda +dl_datalistmodel.type.dl_meetingAgenda.title=Meeting Agenda +dl_datalistmodel.type.dl_meetingAgenda.description=Manage meeting agenda items including description, owner, allocated time. +dl_datalistmodel.property.dl_meetingAgendaRef.title=Reference +dl_datalistmodel.property.dl_meetingAgendaTime.title=Time (mins) +dl_datalistmodel.property.dl_meetingAgendaOwner.title=Owner + +# Event Agenda +dl_datalistmodel.type.dl_eventAgenda.title=Event Agenda +dl_datalistmodel.type.dl_eventAgenda.description=Manage event agenda items including session names, presenters, start and end times. +dl_datalistmodel.property.dl_eventAgendaRef.title=Reference +dl_datalistmodel.property.dl_eventAgendaStartTime.title=Start Time +dl_datalistmodel.property.dl_eventAgendaEndTime.title=End Time +dl_datalistmodel.property.dl_eventAgendaSessionName.title=Session Name +dl_datalistmodel.property.dl_eventAgendaPresenter.title=Presenter +dl_datalistmodel.property.dl_eventAgendaAudience.title=Audience +dl_datalistmodel.property.dl_eventAgendaNotes.title=Notes + +# List constraint display labels +listconstraint.dl_task_status.Not\ Started=Not Started +listconstraint.dl_task_status.In\ Progress=In Progress +listconstraint.dl_task_status.On\ Hold= On Hold +listconstraint.dl_task_status.Complete=Complete +listconstraint.dl_priority_value.High=High +listconstraint.dl_priority_value.Normal=Normal +listconstraint.dl_priority_value.Low=Low + diff --git a/amps/share-services/src/main/resources/alfresco/messages/data-list-model_de.properties b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_de.properties new file mode 100755 index 0000000000..d8d5f75753 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_de.properties @@ -0,0 +1,114 @@ +# Display labels for Share Data Lists Model +dl_datalistmodel.description=Alfresco Share Datenlisten-Modell + +dl_datalistmodel.type.dl_dataList.title=Ordnertyp der Datenliste +dl_datalistmodel.type.dl_dataList.description=H\u00e4lt Datenlisten-Elemente des angegebenen Typs in der Eigenschaft dl: dataListItemType. +dl_datalistmodel.property.dl_dataListItemType.title=Elementtyp Datenliste +dl_datalistmodel.property.dl_dataListItemType.description=Legt fest, welcher Subtyp von dl: dataListItem verwendet wird, wenn neue Elemente in der Datenliste erstellt werden. + +dl_datalistmodel.type.dl_dataListItem.title=\u00dcbergeordneter Typ der Datenliste +dl_datalistmodel.type.dl_dataListItem.description=\u00dcbergeordneter Typ, von dem Mustertypen von Elementen der Datenliste abgeleitet werden. + +## Simple "To do" List +dl_datalistmodel.type.dl_todoList.title=Liste zu erledigender Aufgaben +dl_datalistmodel.type.dl_todoList.description=Einfache Liste zu erledigender Aufgaben mit optionaler zugewiesener Person. +dl_datalistmodel.property.dl_todoTitle.title=Titel +dl_datalistmodel.property.dl_todoDueDate.title=F\u00e4lligkeitsdatum +dl_datalistmodel.property.dl_todoPriority.title=Priorit\u00e4t +dl_datalistmodel.property.dl_todoStatus.title=Status +dl_datalistmodel.property.dl_todoNotes.title=Notizen +dl_datalistmodel.association.dl_assignee.title=Zugewiesene Person +dl_datalistmodel.association.dl_attachments.title=Anh\u00e4nge + +# Gantt Aspect +dl_datalistmodel.property.dl_ganttStartDate.title=Anfangsdatum +dl_datalistmodel.property.dl_ganttEndDate.title=Enddatum +dl_datalistmodel.property.dl_ganttPercentComplete.title=% Abgeschlossen + +# Task List (Advanced) +dl_datalistmodel.type.dl_task.title=Aufgabenliste (Erweitert) +dl_datalistmodel.type.dl_task.description=Erweiterte Aufgabenliste einschlie\u00dflich Titel, Beschreibung, Anfangs- und Enddatum, Priorit\u00e4t, Status, Bemerkungen, zugewiesenen Personen und Anlagen. +dl_datalistmodel.property.dl_taskPriority.title=Priorit\u00e4t +dl_datalistmodel.property.dl_taskStatus.title=Status +dl_datalistmodel.property.dl_taskComments.title=Kommentare +dl_datalistmodel.association.dl_taskAssignee.title=Zugewiesene Person + +# Task List (Simple) +dl_datalistmodel.type.dl_simpletask.title=Aufgabenliste (Einfach) +dl_datalistmodel.type.dl_simpletask.description=Einfache Aufgabenliste einschlie\u00dflich Titel, Beschreibung, F\u00e4lligkeitsdatum, Priorit\u00e4t, Status, Kommentaren. +dl_datalistmodel.property.dl_simpletaskDueDate.title=F\u00e4lligkeitsdatum +dl_datalistmodel.property.dl_simpletaskPriority.title=Priorit\u00e4t +dl_datalistmodel.property.dl_simpletaskStatus.title=Status +dl_datalistmodel.property.dl_simpletaskComments.title=Kommentare + +# Contact +dl_datalistmodel.type.dl_contact.title=Liste der Kontakte +dl_datalistmodel.type.dl_contact.description=Liste der Kontakte einschlie\u00dflich Vornamen, Nachnamen, vollst\u00e4ndigem Namen, E-Mail, Jobtitel, Telefon (B\u00fcro), Telefon (Mobil). +dl_datalistmodel.property.dl_contactFirstName.title=Vorname +dl_datalistmodel.property.dl_contactLastName.title=Nachname +dl_datalistmodel.property.dl_contactEmail.title=E-Mail +dl_datalistmodel.property.dl_contactCompany.title=Unternehmen +dl_datalistmodel.property.dl_contactJobTitle.title=Jobtitel +dl_datalistmodel.property.dl_contactPhoneOffice.title=Telefon (B\u00fcro) +dl_datalistmodel.property.dl_contactPhoneMobile.title=Telefon (Mobil) +dl_datalistmodel.property.dl_contactNotes.title=Notizen + +# Issues +dl_datalistmodel.type.dl_issue.title=Themenliste +dl_datalistmodel.type.dl_issue.description=Themenliste, die ID, Status, Priorit\u00e4t, Beschreibung, F\u00e4lligkeitsdatum, zugewiesen zu und verwandte Themen enth\u00e4lt. +dl_datalistmodel.property.dl_issueID.title=Themen ID +dl_datalistmodel.property.dl_issueStatus.title=Status +dl_datalistmodel.property.dl_issuePriority.title=Priorit\u00e4t +dl_datalistmodel.property.dl_issueDescription.title=Beschreibung +dl_datalistmodel.property.dl_issueDueDate.title=F\u00e4lligkeitsdatum +dl_datalistmodel.property.dl_issueComments.title=Kommentare +dl_datalistmodel.association.dl_issueAssignedTo.title=Zugewiesen an +dl_datalistmodel.property.dl_issueRelatedIssues.title=\u00c4hnliche Elemente + +# Event +dl_datalistmodel.type.dl_event.title=Ereignisliste +dl_datalistmodel.type.dl_event.description=Ereignisliste einschlie\u00dflich Titel, Beschreibung, Ort, Anfangs- und Enddatum/-zeit. +dl_datalistmodel.property.dl_eventLocation.title=Speicherort +dl_datalistmodel.property.dl_eventStartDate.title=Anfangsdatum +dl_datalistmodel.property.dl_eventEndDate.title=Enddatum +dl_datalistmodel.property.dl_eventRegistrations.title=Registrierungen +dl_datalistmodel.property.dl_eventNote.title=Notizen + +# Location +dl_datalistmodel.type.dl_location.title=Standortliste +dl_datalistmodel.type.dl_location.description=Liste Liste von Standorten/Adressen +dl_datalistmodel.property.dl_locationAddress1.title=Adresse Zeile 1 +dl_datalistmodel.property.dl_locationAddress2.title=Adresse Zeile 2 +dl_datalistmodel.property.dl_locationAddress3.title=Adresse Zeile 3 +dl_datalistmodel.property.dl_locationZip.title=Postleitzahl +dl_datalistmodel.property.dl_locationState.title=Bundesland +dl_datalistmodel.property.dl_locationCountry.title=Land +dl_datalistmodel.property.dl_locationNote.title=Notizen + +# Meeting Agenda +dl_datalistmodel.type.dl_meetingAgenda.title=Sitzungskalender +dl_datalistmodel.type.dl_meetingAgenda.description=Verwaltung von Elementen des Sitzungskalenders einschlie\u00dflich Beschreibung, Eigent\u00fcmer, zugewiesene Zeit. +dl_datalistmodel.property.dl_meetingAgendaRef.title=Referenz +dl_datalistmodel.property.dl_meetingAgendaTime.title=Zeit (Minuten) +dl_datalistmodel.property.dl_meetingAgendaOwner.title=Eigent\u00fcmer + +# Event Agenda +dl_datalistmodel.type.dl_eventAgenda.title=Ereigniskalender +dl_datalistmodel.type.dl_eventAgenda.description=Ereigniskalender einschlie\u00dflich Namen der Sitzungen, Pr\u00e4sentatoren, Anfangs- und Endzeit verwalten. +dl_datalistmodel.property.dl_eventAgendaRef.title=Referenz +dl_datalistmodel.property.dl_eventAgendaStartTime.title=Anfangszeit +dl_datalistmodel.property.dl_eventAgendaEndTime.title=Endzeit +dl_datalistmodel.property.dl_eventAgendaSessionName.title=Name der Sitzung +dl_datalistmodel.property.dl_eventAgendaPresenter.title=Pr\u00e4sentator +dl_datalistmodel.property.dl_eventAgendaAudience.title=Publikum +dl_datalistmodel.property.dl_eventAgendaNotes.title=Notizen + +# List constraint display labels +listconstraint.dl_task_status.Not\ Started=nicht gestartet +listconstraint.dl_task_status.In\ Progress=in Bearbeitung +listconstraint.dl_task_status.On\ Hold=in Wartestellung +listconstraint.dl_task_status.Complete=Abgeschlossen +listconstraint.dl_priority_value.High=Hoch +listconstraint.dl_priority_value.Normal=Normal +listconstraint.dl_priority_value.Low=Niedrig + diff --git a/amps/share-services/src/main/resources/alfresco/messages/data-list-model_es.properties b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_es.properties new file mode 100755 index 0000000000..646013d438 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_es.properties @@ -0,0 +1,114 @@ +# Display labels for Share Data Lists Model +dl_datalistmodel.description=Modelo de lista de datos de Alfresco Share + +dl_datalistmodel.type.dl_dataList.title=Tipo de carpeta de lista de datos +dl_datalistmodel.type.dl_dataList.description=Contiene elementos de lista de datos del tipo especificado en la propiedad dl:dataListItemType. +dl_datalistmodel.property.dl_dataListItemType.title=Tipo de elemento de lista de datos +dl_datalistmodel.property.dl_dataListItemType.description=Determina qu\u00e9 subtipo de dl:DataListItem se utilizar\u00e1 al crear nuevos elementos en la lista de datos. + +dl_datalistmodel.type.dl_dataListItem.title=Tipo de padre de lista de datos +dl_datalistmodel.type.dl_dataListItem.description=Tipo de padre del que se derivan los tipos de elementos de muestra de listas de datos. + +## Simple "To do" List +dl_datalistmodel.type.dl_todoList.title=Lista de tareas pendientes +dl_datalistmodel.type.dl_todoList.description=Una lista sencilla de tareas pendientes con usuario opcional a asignar la tarea. +dl_datalistmodel.property.dl_todoTitle.title=T\u00edtulo +dl_datalistmodel.property.dl_todoDueDate.title=Fecha de vencimiento +dl_datalistmodel.property.dl_todoPriority.title=Prioridad +dl_datalistmodel.property.dl_todoStatus.title=Estado +dl_datalistmodel.property.dl_todoNotes.title=Notas +dl_datalistmodel.association.dl_assignee.title=Usuario asignado +dl_datalistmodel.association.dl_attachments.title=Adjuntos + +# Gantt Aspect +dl_datalistmodel.property.dl_ganttStartDate.title=Fecha de inicio +dl_datalistmodel.property.dl_ganttEndDate.title=Fecha de fin +dl_datalistmodel.property.dl_ganttPercentComplete.title=% Completado + +# Task List (Advanced) +dl_datalistmodel.type.dl_task.title=Lista de tareas (Avanzada) +dl_datalistmodel.type.dl_task.description=Lista de tareas avanzada, incluyendo t\u00edtulo, descripci\u00f3n, fechas de inicio y finalizaci\u00f3n, prioridad, estado, comentarios, usuarios a asignar la tarea y adjuntos. +dl_datalistmodel.property.dl_taskPriority.title=Prioridad +dl_datalistmodel.property.dl_taskStatus.title=Estado +dl_datalistmodel.property.dl_taskComments.title=Comentarios +dl_datalistmodel.association.dl_taskAssignee.title=Usuario asignado + +# Task List (Simple) +dl_datalistmodel.type.dl_simpletask.title=Lista de tareas (sencillas) +dl_datalistmodel.type.dl_simpletask.description=Lista de tareas sencilla, incluyendo t\u00edtulo, descripci\u00f3n, fecha de vencimiento, prioridad, estado, comentarios. +dl_datalistmodel.property.dl_simpletaskDueDate.title=Fecha de vencimiento +dl_datalistmodel.property.dl_simpletaskPriority.title=Prioridad +dl_datalistmodel.property.dl_simpletaskStatus.title=Estado +dl_datalistmodel.property.dl_simpletaskComments.title=Comentarios + +# Contact +dl_datalistmodel.type.dl_contact.title=Lista de contactos +dl_datalistmodel.type.dl_contact.description=Lista de contactos incluyendo nombre, apellido, nombre completo, email, t\u00edtulo del trabajo, tel\u00e9fono (oficina), tel\u00e9fono (m\u00f3vil). +dl_datalistmodel.property.dl_contactFirstName.title=Nombre +dl_datalistmodel.property.dl_contactLastName.title=Apellidos +dl_datalistmodel.property.dl_contactEmail.title=Correo electr\u00f3nico +dl_datalistmodel.property.dl_contactCompany.title=Empresa +dl_datalistmodel.property.dl_contactJobTitle.title=Cargo +dl_datalistmodel.property.dl_contactPhoneOffice.title=Tel\u00e9fono (Oficina) +dl_datalistmodel.property.dl_contactPhoneMobile.title=Tel\u00e9fono (M\u00f3vil) +dl_datalistmodel.property.dl_contactNotes.title=Notas + +# Issues +dl_datalistmodel.type.dl_issue.title=Lista de temas +dl_datalistmodel.type.dl_issue.description=Lista de temas incluyendo ID, estado, prioridad, descripci\u00f3n, fechas de vencimiento, comentarios, asignar a, temas relacionados. +dl_datalistmodel.property.dl_issueID.title=ID de tema +dl_datalistmodel.property.dl_issueStatus.title=Estado +dl_datalistmodel.property.dl_issuePriority.title=Prioridad +dl_datalistmodel.property.dl_issueDescription.title=Descripci\u00f3n +dl_datalistmodel.property.dl_issueDueDate.title=Fecha de vencimiento +dl_datalistmodel.property.dl_issueComments.title=Comentarios +dl_datalistmodel.association.dl_issueAssignedTo.title=Asignado a +dl_datalistmodel.property.dl_issueRelatedIssues.title=Temas relacionados + +# Event +dl_datalistmodel.type.dl_event.title=Lista de eventos +dl_datalistmodel.type.dl_event.description=Lista de eventos, incluyendo t\u00edtulo, descripci\u00f3n, ubicaci\u00f3n, fecha/hora de inicio y fin. +dl_datalistmodel.property.dl_eventLocation.title=Ubicaci\u00f3n +dl_datalistmodel.property.dl_eventStartDate.title=Fecha de inicio +dl_datalistmodel.property.dl_eventEndDate.title=Fecha de fin +dl_datalistmodel.property.dl_eventRegistrations.title=Inscripciones +dl_datalistmodel.property.dl_eventNote.title=Notas + +# Location +dl_datalistmodel.type.dl_location.title=Lista de ubicaciones +dl_datalistmodel.type.dl_location.description=Lista de ubicaciones/direcciones +dl_datalistmodel.property.dl_locationAddress1.title=Direcci\u00f3n l\u00ednea 1 +dl_datalistmodel.property.dl_locationAddress2.title=Direcci\u00f3n l\u00ednea 2 +dl_datalistmodel.property.dl_locationAddress3.title=Direcci\u00f3n l\u00ednea 3 +dl_datalistmodel.property.dl_locationZip.title=Zip/C\u00f3digo postal +dl_datalistmodel.property.dl_locationState.title=Estado/Condado +dl_datalistmodel.property.dl_locationCountry.title=Pa\u00eds +dl_datalistmodel.property.dl_locationNote.title=Notas + +# Meeting Agenda +dl_datalistmodel.type.dl_meetingAgenda.title=Agenda de reuni\u00f3n +dl_datalistmodel.type.dl_meetingAgenda.description=Administrar elementos de agenda de reuni\u00f3n, incluyendo descripci\u00f3n, propietario, el tiempo asignado. +dl_datalistmodel.property.dl_meetingAgendaRef.title=Referencia +dl_datalistmodel.property.dl_meetingAgendaTime.title=Tiempo (min) +dl_datalistmodel.property.dl_meetingAgendaOwner.title=Propietario + +# Event Agenda +dl_datalistmodel.type.dl_eventAgenda.title=Agenda de eventos +dl_datalistmodel.type.dl_eventAgenda.description=Administrar agenda de eventos incluyendo nombres de sesi\u00f3n, participantes, horas de inicio y fin. +dl_datalistmodel.property.dl_eventAgendaRef.title=Referencia +dl_datalistmodel.property.dl_eventAgendaStartTime.title=Hora de inicio +dl_datalistmodel.property.dl_eventAgendaEndTime.title=Hora de fin +dl_datalistmodel.property.dl_eventAgendaSessionName.title=Nombre de sesi\u00f3n +dl_datalistmodel.property.dl_eventAgendaPresenter.title=Participante +dl_datalistmodel.property.dl_eventAgendaAudience.title=Audiencia +dl_datalistmodel.property.dl_eventAgendaNotes.title=Notas + +# List constraint display labels +listconstraint.dl_task_status.Not\ Started=No iniciada +listconstraint.dl_task_status.In\ Progress=En curso +listconstraint.dl_task_status.On\ Hold=En espera +listconstraint.dl_task_status.Complete=Completa +listconstraint.dl_priority_value.High=Alta +listconstraint.dl_priority_value.Normal=Normal +listconstraint.dl_priority_value.Low=Baja + diff --git a/amps/share-services/src/main/resources/alfresco/messages/data-list-model_fr.properties b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_fr.properties new file mode 100755 index 0000000000..6a2b40090a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_fr.properties @@ -0,0 +1,114 @@ +# Display labels for Share Data Lists Model +dl_datalistmodel.description=Mod\u00e8le de liste de donn\u00e9es Alfresco Share + +dl_datalistmodel.type.dl_dataList.title=Type de dossier de liste de donn\u00e9es +dl_datalistmodel.type.dl_dataList.description=Stocke les \u00e9l\u00e9ments de liste de donn\u00e9es du type sp\u00e9cifi\u00e9 dans la propri\u00e9t\u00e9 dl:dataListItemType. +dl_datalistmodel.property.dl_dataListItemType.title=Type d'\u00e9l\u00e9ment de liste de donn\u00e9es +dl_datalistmodel.property.dl_dataListItemType.description=D\u00e9termine quel sous-type de dl:dataListItem sera utilis\u00e9 quand un nouvel \u00e9l\u00e9ment de la liste de donn\u00e9es sera cr\u00e9\u00e9. + +dl_datalistmodel.type.dl_dataListItem.title=Type parent de liste de donn\u00e9es +dl_datalistmodel.type.dl_dataListItem.description=Type parent \u00e0 partir duquel d\u00e9rivent les types d'\u00e9l\u00e9ment de liste de donn\u00e9es. + +## Simple "To do" List +dl_datalistmodel.type.dl_todoList.title=Liste de t\u00e2ches +dl_datalistmodel.type.dl_todoList.description=Une liste de t\u00e2ches avec personne assign\u00e9e facultative. +dl_datalistmodel.property.dl_todoTitle.title=Titre +dl_datalistmodel.property.dl_todoDueDate.title=Ech\u00e9ance +dl_datalistmodel.property.dl_todoPriority.title=Priorit\u00e9 +dl_datalistmodel.property.dl_todoStatus.title=Statut +dl_datalistmodel.property.dl_todoNotes.title=Notes +dl_datalistmodel.association.dl_assignee.title=Personne assign\u00e9e +dl_datalistmodel.association.dl_attachments.title=El\u00e9ments attach\u00e9s + +# Gantt Aspect +dl_datalistmodel.property.dl_ganttStartDate.title=Date de d\u00e9but +dl_datalistmodel.property.dl_ganttEndDate.title=Date de fin +dl_datalistmodel.property.dl_ganttPercentComplete.title=% achev\u00e9 + +# Task List (Advanced) +dl_datalistmodel.type.dl_task.title=Liste de t\u00e2ches (avanc\u00e9es) +dl_datalistmodel.type.dl_task.description=Liste de t\u00e2ches avanc\u00e9es comprenant le titre, la description, les dates de d\u00e9but et de fin, la priorit\u00e9, le statut, les commentaires, les acteurs et les pi\u00e8ces jointes. +dl_datalistmodel.property.dl_taskPriority.title=Priorit\u00e9 +dl_datalistmodel.property.dl_taskStatus.title=Statut +dl_datalistmodel.property.dl_taskComments.title=Commentaires +dl_datalistmodel.association.dl_taskAssignee.title=Personne assign\u00e9e + +# Task List (Simple) +dl_datalistmodel.type.dl_simpletask.title=Liste de t\u00e2ches (simples) +dl_datalistmodel.type.dl_simpletask.description=Liste de t\u00e2ches simples comprenant le titre, la description, la date d'\u00e9ch\u00e9ance, la priorit\u00e9, le statut et les commentaires. +dl_datalistmodel.property.dl_simpletaskDueDate.title=Ech\u00e9ance +dl_datalistmodel.property.dl_simpletaskPriority.title=Priorit\u00e9 +dl_datalistmodel.property.dl_simpletaskStatus.title=Statut +dl_datalistmodel.property.dl_simpletaskComments.title=Commentaires + +# Contact +dl_datalistmodel.type.dl_contact.title=Liste de contacts +dl_datalistmodel.type.dl_contact.description=Liste de contacts comprenant le pr\u00e9nom, le nom, le nom complet, l'e-mail, l'intitul\u00e9 du poste, le num\u00e9ro de t\u00e9l\u00e9phone (bureau) et le num\u00e9ro de t\u00e9l\u00e9phone (portable). +dl_datalistmodel.property.dl_contactFirstName.title=Pr\u00e9nom +dl_datalistmodel.property.dl_contactLastName.title=Nom +dl_datalistmodel.property.dl_contactEmail.title=E-mail +dl_datalistmodel.property.dl_contactCompany.title=Soci\u00e9t\u00e9 +dl_datalistmodel.property.dl_contactJobTitle.title=Intitul\u00e9 du poste +dl_datalistmodel.property.dl_contactPhoneOffice.title=T\u00e9l\u00e9phone (bureau) +dl_datalistmodel.property.dl_contactPhoneMobile.title=T\u00e9l\u00e9phone (portable) +dl_datalistmodel.property.dl_contactNotes.title=Notes + +# Issues +dl_datalistmodel.type.dl_issue.title=Liste de publications +dl_datalistmodel.type.dl_issue.description=Liste de publications comprenant l'identifiant, le statut, la priorit\u00e9, la description, la date d'\u00e9ch\u00e9ance, les commentaires, l'assignation et les publications apparent\u00e9es. +dl_datalistmodel.property.dl_issueID.title=Identifiant de publication +dl_datalistmodel.property.dl_issueStatus.title=Statut +dl_datalistmodel.property.dl_issuePriority.title=Priorit\u00e9 +dl_datalistmodel.property.dl_issueDescription.title=Description +dl_datalistmodel.property.dl_issueDueDate.title=Ech\u00e9ance +dl_datalistmodel.property.dl_issueComments.title=Commentaires +dl_datalistmodel.association.dl_issueAssignedTo.title=Assign\u00e9 \u00e0 +dl_datalistmodel.property.dl_issueRelatedIssues.title=Publications apparent\u00e9es + +# Event +dl_datalistmodel.type.dl_event.title=Liste d'\u00e9v\u00e9nements +dl_datalistmodel.type.dl_event.description=Liste d'\u00e9v\u00e9nements comprenant le titre, la description, le lieu et les dates/heures de d\u00e9but et de fin. +dl_datalistmodel.property.dl_eventLocation.title=Emplacement +dl_datalistmodel.property.dl_eventStartDate.title=Date de d\u00e9but +dl_datalistmodel.property.dl_eventEndDate.title=Date de fin +dl_datalistmodel.property.dl_eventRegistrations.title=Inscriptions +dl_datalistmodel.property.dl_eventNote.title=Notes + +# Location +dl_datalistmodel.type.dl_location.title=Carnet d'adresses +dl_datalistmodel.type.dl_location.description=Carnet d'adresses +dl_datalistmodel.property.dl_locationAddress1.title=Adresse (ligne 1) +dl_datalistmodel.property.dl_locationAddress2.title=Adresse (ligne 2) +dl_datalistmodel.property.dl_locationAddress3.title=Adresse (ligne 3) +dl_datalistmodel.property.dl_locationZip.title=Code postal +dl_datalistmodel.property.dl_locationState.title=D\u00e9partement +dl_datalistmodel.property.dl_locationCountry.title=Pays +dl_datalistmodel.property.dl_locationNote.title=Notes + +# Meeting Agenda +dl_datalistmodel.type.dl_meetingAgenda.title=Ordre du jour +dl_datalistmodel.type.dl_meetingAgenda.description=G\u00e9rer un ordre du jour comprenant la description, le propri\u00e9taire et le temps imparti. +dl_datalistmodel.property.dl_meetingAgendaRef.title=R\u00e9f\u00e9rence +dl_datalistmodel.property.dl_meetingAgendaTime.title=Temps (min) +dl_datalistmodel.property.dl_meetingAgendaOwner.title=Propri\u00e9taire + +# Event Agenda +dl_datalistmodel.type.dl_eventAgenda.title=Agenda d'\u00e9v\u00e9nement +dl_datalistmodel.type.dl_eventAgenda.description=G\u00e9rer un agenda d'\u00e9v\u00e9nement comprenant les noms de s\u00e9ances, les intervenants et les heures de d\u00e9but et de fin de s\u00e9ance. +dl_datalistmodel.property.dl_eventAgendaRef.title=R\u00e9f\u00e9rence +dl_datalistmodel.property.dl_eventAgendaStartTime.title=Heure de d\u00e9but +dl_datalistmodel.property.dl_eventAgendaEndTime.title=Heure de fin +dl_datalistmodel.property.dl_eventAgendaSessionName.title=Nom de la s\u00e9ance +dl_datalistmodel.property.dl_eventAgendaPresenter.title=Intervenant +dl_datalistmodel.property.dl_eventAgendaAudience.title=Auditoire +dl_datalistmodel.property.dl_eventAgendaNotes.title=Notes + +# List constraint display labels +listconstraint.dl_task_status.Not\ Started=Pas d\u00e9marr\u00e9 +listconstraint.dl_task_status.In\ Progress=En cours +listconstraint.dl_task_status.On\ Hold=Suspendu +listconstraint.dl_task_status.Complete=Achev\u00e9 +listconstraint.dl_priority_value.High=\u00c9lev\u00e9e +listconstraint.dl_priority_value.Normal=Normale +listconstraint.dl_priority_value.Low=Basse + diff --git a/amps/share-services/src/main/resources/alfresco/messages/data-list-model_it.properties b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_it.properties new file mode 100755 index 0000000000..5b5fc5a9c6 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_it.properties @@ -0,0 +1,114 @@ +# Display labels for Share Data Lists Model +dl_datalistmodel.description=Modello di elenco dati Alfresco Share + +dl_datalistmodel.type.dl_dataList.title=Tipo di cartella dell'elenco dati +dl_datalistmodel.type.dl_dataList.description=Contiene elementi dell'elenco dati del tipo specificato nella propriet\u00e0 dl:dataListItemType. +dl_datalistmodel.property.dl_dataListItemType.title=Tipo di elemento dell'elenco dati +dl_datalistmodel.property.dl_dataListItemType.description=Determina il sottotipo di dl:dataListItem che verr\u00e0 utilizzato durante la creazione di nuovi elementi nell'elenco dati. + +dl_datalistmodel.type.dl_dataListItem.title=Tipo di genitore dell'elenco dati +dl_datalistmodel.type.dl_dataListItem.description=Tipo di genitore da cui derivano i tipi di elementi dell'elenco dati di esempio. + +## Simple "To do" List +dl_datalistmodel.type.dl_todoList.title=Elenco Da fare +dl_datalistmodel.type.dl_todoList.description=Semplice elenco di compiti da eseguire con assegnatario opzionale. +dl_datalistmodel.property.dl_todoTitle.title=Titolo +dl_datalistmodel.property.dl_todoDueDate.title=Data di scadenza +dl_datalistmodel.property.dl_todoPriority.title=Priorit\u00e0 +dl_datalistmodel.property.dl_todoStatus.title=Stato +dl_datalistmodel.property.dl_todoNotes.title=Note +dl_datalistmodel.association.dl_assignee.title=Assegnatario +dl_datalistmodel.association.dl_attachments.title=Allegati + +# Gantt Aspect +dl_datalistmodel.property.dl_ganttStartDate.title=Data di inizio +dl_datalistmodel.property.dl_ganttEndDate.title=Data di fine +dl_datalistmodel.property.dl_ganttPercentComplete.title=Percentuale di completamento + +# Task List (Advanced) +dl_datalistmodel.type.dl_task.title=Elenco di compiti (avanzato) +dl_datalistmodel.type.dl_task.description=Elenco di compiti avanzato contenente il titolo, la descrizione, le date di inizio e di fine, la priorit\u00e0, lo stato, i commenti, gli assegnatari e gli allegati. +dl_datalistmodel.property.dl_taskPriority.title=Priorit\u00e0 +dl_datalistmodel.property.dl_taskStatus.title=Stato +dl_datalistmodel.property.dl_taskComments.title=Commenti +dl_datalistmodel.association.dl_taskAssignee.title=Assegnatario + +# Task List (Simple) +dl_datalistmodel.type.dl_simpletask.title=Elenco di compiti (semplice) +dl_datalistmodel.type.dl_simpletask.description=Elenco di compiti semplice contenente il titolo, la descrizione, la data di scadenza, la priorit\u00e0, lo stato e i commenti. +dl_datalistmodel.property.dl_simpletaskDueDate.title=Data di scadenza +dl_datalistmodel.property.dl_simpletaskPriority.title=Priorit\u00e0 +dl_datalistmodel.property.dl_simpletaskStatus.title=Stato +dl_datalistmodel.property.dl_simpletaskComments.title=Commenti + +# Contact +dl_datalistmodel.type.dl_contact.title=Elenco di contatti +dl_datalistmodel.type.dl_contact.description=Elenco di contatti contenente il nome, il cognome, il nome completo, l'indirizzo e-mail, la qualifica, il telefono dell'ufficio e il cellulare. +dl_datalistmodel.property.dl_contactFirstName.title=Nome +dl_datalistmodel.property.dl_contactLastName.title=Cognome +dl_datalistmodel.property.dl_contactEmail.title=E-mail +dl_datalistmodel.property.dl_contactCompany.title=Azienda +dl_datalistmodel.property.dl_contactJobTitle.title=Qualifica +dl_datalistmodel.property.dl_contactPhoneOffice.title=Telefono (ufficio) +dl_datalistmodel.property.dl_contactPhoneMobile.title=Cellulare +dl_datalistmodel.property.dl_contactNotes.title=Note + +# Issues +dl_datalistmodel.type.dl_issue.title=Elenco di problemi +dl_datalistmodel.type.dl_issue.description=Elenco di problemi contenente l'ID, lo stato, la priorit\u00e0, la descrizione, la data di scadenza, i commenti, l'assegnatario e i problemi correlati. +dl_datalistmodel.property.dl_issueID.title=ID problema +dl_datalistmodel.property.dl_issueStatus.title=Stato +dl_datalistmodel.property.dl_issuePriority.title=Priorit\u00e0 +dl_datalistmodel.property.dl_issueDescription.title=Descrizione +dl_datalistmodel.property.dl_issueDueDate.title=Data di scadenza +dl_datalistmodel.property.dl_issueComments.title=Commenti +dl_datalistmodel.association.dl_issueAssignedTo.title=Assegnato a +dl_datalistmodel.property.dl_issueRelatedIssues.title=Problemi correlati + +# Event +dl_datalistmodel.type.dl_event.title=Elenco di eventi +dl_datalistmodel.type.dl_event.description=Elenco di eventi contenente il titolo, la descrizione, la localit\u00e0 e la data/ora di inizio e di fine. +dl_datalistmodel.property.dl_eventLocation.title=Localit\u00e0 +dl_datalistmodel.property.dl_eventStartDate.title=Data di inizio +dl_datalistmodel.property.dl_eventEndDate.title=Data di fine +dl_datalistmodel.property.dl_eventRegistrations.title=Registrazioni +dl_datalistmodel.property.dl_eventNote.title=Note + +# Location +dl_datalistmodel.type.dl_location.title=Elenco di localit\u00e0 +dl_datalistmodel.type.dl_location.description=Elenco di localit\u00e0/indirizzi +dl_datalistmodel.property.dl_locationAddress1.title=Riga 1 indirizzo +dl_datalistmodel.property.dl_locationAddress2.title=Riga 2 indirizzo +dl_datalistmodel.property.dl_locationAddress3.title=Riga 3 indirizzo +dl_datalistmodel.property.dl_locationZip.title=CAP/Codice postale +dl_datalistmodel.property.dl_locationState.title=Stato +dl_datalistmodel.property.dl_locationCountry.title=Paese +dl_datalistmodel.property.dl_locationNote.title=Note + +# Meeting Agenda +dl_datalistmodel.type.dl_meetingAgenda.title=Agenda riunioni +dl_datalistmodel.type.dl_meetingAgenda.description=Consente di gestire le voci dell'agenda delle riunioni, tra cui la descrizione, il proprietario e l'ora di assegnazione. +dl_datalistmodel.property.dl_meetingAgendaRef.title=Riferimento +dl_datalistmodel.property.dl_meetingAgendaTime.title=Ora (minuti) +dl_datalistmodel.property.dl_meetingAgendaOwner.title=Proprietario + +# Event Agenda +dl_datalistmodel.type.dl_eventAgenda.title=Agenda eventi +dl_datalistmodel.type.dl_eventAgenda.description=Consente di gestire le voci dell'agenda degli eventi, tra cui i nomi delle sessioni, i relatori e le ore di inizio e di fine. +dl_datalistmodel.property.dl_eventAgendaRef.title=Riferimento +dl_datalistmodel.property.dl_eventAgendaStartTime.title=Ora di inizio +dl_datalistmodel.property.dl_eventAgendaEndTime.title=Ora di fine +dl_datalistmodel.property.dl_eventAgendaSessionName.title=Nome sessione +dl_datalistmodel.property.dl_eventAgendaPresenter.title=Relatore +dl_datalistmodel.property.dl_eventAgendaAudience.title=Destinatari +dl_datalistmodel.property.dl_eventAgendaNotes.title=Note + +# List constraint display labels +listconstraint.dl_task_status.Not\ Started=Non avviato +listconstraint.dl_task_status.In\ Progress=In corso +listconstraint.dl_task_status.On\ Hold=In attesa +listconstraint.dl_task_status.Complete=Completato +listconstraint.dl_priority_value.High=Alta +listconstraint.dl_priority_value.Normal=Normale +listconstraint.dl_priority_value.Low=Bassa + diff --git a/amps/share-services/src/main/resources/alfresco/messages/data-list-model_ja.properties b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_ja.properties new file mode 100755 index 0000000000..a6059888fb --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_ja.properties @@ -0,0 +1,114 @@ +# Display labels for Share Data Lists Model +dl_datalistmodel.description=Alfresco Share \u30c7\u30fc\u30bf\u30ea\u30b9\u30c8\u30e2\u30c7\u30eb + +dl_datalistmodel.type.dl_dataList.title=\u30c7\u30fc\u30bf\u30ea\u30b9\u30c8\u30d5\u30a9\u30eb\u30c0\u306e\u30bf\u30a4\u30d7 +dl_datalistmodel.type.dl_dataList.description=dl:dataListItemType \u30d7\u30ed\u30d1\u30c6\u30a3\u3067\u6307\u5b9a\u3055\u308c\u305f\u30bf\u30a4\u30d7\u306e\u30c7\u30fc\u30bf\u30ea\u30b9\u30c8\u30a2\u30a4\u30c6\u30e0\u3092\u4fdd\u6301\u3057\u307e\u3059\u3002 +dl_datalistmodel.property.dl_dataListItemType.title=\u30c7\u30fc\u30bf\u30ea\u30b9\u30c8\u30a2\u30a4\u30c6\u30e0\u306e\u30bf\u30a4\u30d7 +dl_datalistmodel.property.dl_dataListItemType.description=\u30c7\u30fc\u30bf\u30ea\u30b9\u30c8\u5185\u3067\u65b0\u3057\u3044\u30a2\u30a4\u30c6\u30e0\u3092\u4f5c\u6210\u3059\u308b\u3068\u304d\u306b\u4f7f\u7528\u3059\u308b dl:dataListItem \u306e\u30b5\u30d6\u30bf\u30a4\u30d7\u3092\u6307\u5b9a\u3057\u307e\u3059\u3002 + +dl_datalistmodel.type.dl_dataListItem.title=\u30c7\u30fc\u30bf\u30ea\u30b9\u30c8\u306e\u89aa\u30bf\u30a4\u30d7 +dl_datalistmodel.type.dl_dataListItem.description=\u89aa\u30bf\u30a4\u30d7\u3068\u306f\u3001\u30b5\u30f3\u30d7\u30eb\u306e\u30c7\u30fc\u30bf\u30ea\u30b9\u30c8\u30a2\u30a4\u30c6\u30e0\u306e\u30bf\u30a4\u30d7\u304c\u5f93\u5c5e\u3059\u308b\u30bf\u30a4\u30d7\u3092\u6307\u3057\u307e\u3059\u3002 + +## Simple "To do" List +dl_datalistmodel.type.dl_todoList.title=\u7528\u4ef6\u30ea\u30b9\u30c8 +dl_datalistmodel.type.dl_todoList.description=\u30b7\u30f3\u30d7\u30eb\u306a\u7528\u4ef6\u30ea\u30b9\u30c8\u3002\u62c5\u5f53\u8005\u3092\u5272\u308a\u5f53\u3066\u308b\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u3002 +dl_datalistmodel.property.dl_todoTitle.title=\u30bf\u30a4\u30c8\u30eb +dl_datalistmodel.property.dl_todoDueDate.title=\u671f\u9650 +dl_datalistmodel.property.dl_todoPriority.title=\u512a\u5148\u5ea6 +dl_datalistmodel.property.dl_todoStatus.title=\u30b9\u30c6\u30fc\u30bf\u30b9 +dl_datalistmodel.property.dl_todoNotes.title=\u30e1\u30e2 +dl_datalistmodel.association.dl_assignee.title=\u62c5\u5f53\u8005 +dl_datalistmodel.association.dl_attachments.title=\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb + +# Gantt Aspect +dl_datalistmodel.property.dl_ganttStartDate.title=\u958b\u59cb\u65e5 +dl_datalistmodel.property.dl_ganttEndDate.title=\u7d42\u4e86\u65e5 +dl_datalistmodel.property.dl_ganttPercentComplete.title=% \u5b8c\u4e86 + +# Task List (Advanced) +dl_datalistmodel.type.dl_task.title=\u30bf\u30b9\u30af\u30ea\u30b9\u30c8 (\u8a73\u7d30) +dl_datalistmodel.type.dl_task.description=\u30bf\u30b9\u30af\u306e\u30bf\u30a4\u30c8\u30eb\u3001\u8aac\u660e\u3001\u958b\u59cb\u65e5\u3001\u7d42\u4e86\u65e5\u3001\u512a\u5148\u5ea6\u3001\u30b9\u30c6\u30fc\u30bf\u30b9\u3001\u30b3\u30e1\u30f3\u30c8\u3001\u62c5\u5f53\u8005\u3001\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002 +dl_datalistmodel.property.dl_taskPriority.title=\u512a\u5148\u5ea6 +dl_datalistmodel.property.dl_taskStatus.title=\u30b9\u30c6\u30fc\u30bf\u30b9 +dl_datalistmodel.property.dl_taskComments.title=\u30b3\u30e1\u30f3\u30c8 +dl_datalistmodel.association.dl_taskAssignee.title=\u62c5\u5f53\u8005 + +# Task List (Simple) +dl_datalistmodel.type.dl_simpletask.title=\u30bf\u30b9\u30af\u30ea\u30b9\u30c8 (\u30b7\u30f3\u30d7\u30eb) +dl_datalistmodel.type.dl_simpletask.description=\u30bf\u30b9\u30af\u306e\u30bf\u30a4\u30c8\u30eb\u3001\u8aac\u660e\u3001\u671f\u9650\u3001\u512a\u5148\u5ea6\u3001\u30b9\u30c6\u30fc\u30bf\u30b9\u3001\u30b3\u30e1\u30f3\u30c8\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002 +dl_datalistmodel.property.dl_simpletaskDueDate.title=\u671f\u9650 +dl_datalistmodel.property.dl_simpletaskPriority.title=\u512a\u5148\u5ea6 +dl_datalistmodel.property.dl_simpletaskStatus.title=\u30b9\u30c6\u30fc\u30bf\u30b9 +dl_datalistmodel.property.dl_simpletaskComments.title=\u30b3\u30e1\u30f3\u30c8 + +# Contact +dl_datalistmodel.type.dl_contact.title=\u9023\u7d61\u5148\u30ea\u30b9\u30c8 +dl_datalistmodel.type.dl_contact.description=\u540d\u524d\u3001E \u30e1\u30fc\u30eb\u3001\u5f79\u8077\u3001\u96fb\u8a71 (\u4f1a\u793e)\u3001\u96fb\u8a71 (\u643a\u5e2f) \u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002 +dl_datalistmodel.property.dl_contactFirstName.title=\u540d +dl_datalistmodel.property.dl_contactLastName.title=\u59d3 +dl_datalistmodel.property.dl_contactEmail.title=E \u30e1\u30fc\u30eb +dl_datalistmodel.property.dl_contactCompany.title=\u4f1a\u793e\u540d +dl_datalistmodel.property.dl_contactJobTitle.title=\u5f79\u8077\u540d +dl_datalistmodel.property.dl_contactPhoneOffice.title=\u96fb\u8a71\uff08\u4f1a\u793e\uff09 +dl_datalistmodel.property.dl_contactPhoneMobile.title=\u96fb\u8a71\uff08\u643a\u5e2f\uff09 +dl_datalistmodel.property.dl_contactNotes.title=\u30e1\u30e2 + +# Issues +dl_datalistmodel.type.dl_issue.title=\u8ab2\u984c\u30ea\u30b9\u30c8 +dl_datalistmodel.type.dl_issue.description=ID\u3001\u30b9\u30c6\u30fc\u30bf\u30b9\u3001\u512a\u5148\u5ea6\u3001\u8aac\u660e\u3001\u671f\u9650\u3001\u30b3\u30e1\u30f3\u30c8\u3001\u62c5\u5f53\u8005\u3001\u95a2\u9023\u8ab2\u984c\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002 +dl_datalistmodel.property.dl_issueID.title=\u8ab2\u984c ID +dl_datalistmodel.property.dl_issueStatus.title=\u30b9\u30c6\u30fc\u30bf\u30b9 +dl_datalistmodel.property.dl_issuePriority.title=\u512a\u5148\u5ea6 +dl_datalistmodel.property.dl_issueDescription.title=\u8aac\u660e +dl_datalistmodel.property.dl_issueDueDate.title=\u671f\u9650 +dl_datalistmodel.property.dl_issueComments.title=\u30b3\u30e1\u30f3\u30c8 +dl_datalistmodel.association.dl_issueAssignedTo.title=\u62c5\u5f53\u8005 +dl_datalistmodel.property.dl_issueRelatedIssues.title=\u95a2\u9023\u8ab2\u984c + +# Event +dl_datalistmodel.type.dl_event.title=\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30c8 +dl_datalistmodel.type.dl_event.description=\u30a4\u30d9\u30f3\u30c8\u306e\u30bf\u30a4\u30c8\u30eb\u3001\u8aac\u660e\u3001\u5834\u6240\u3001\u512a\u5148\u5ea6\u3001\u958b\u59cb\u65e5\u6642\u3001\u7d42\u4e86\u65e5\u6642\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002 +dl_datalistmodel.property.dl_eventLocation.title=\u5834\u6240 +dl_datalistmodel.property.dl_eventStartDate.title=\u958b\u59cb\u65e5 +dl_datalistmodel.property.dl_eventEndDate.title=\u7d42\u4e86\u65e5 +dl_datalistmodel.property.dl_eventRegistrations.title=\u767b\u9332\u6570 +dl_datalistmodel.property.dl_eventNote.title=\u30e1\u30e2 + +# Location +dl_datalistmodel.type.dl_location.title=\u4f4f\u6240\u30ea\u30b9\u30c8 +dl_datalistmodel.type.dl_location.description=\u4f4f\u6240\u304c\u8868\u793a\u3055\u308c\u307e\u3059 +dl_datalistmodel.property.dl_locationAddress1.title=\u4f4f\u62401 +dl_datalistmodel.property.dl_locationAddress2.title=\u4f4f\u6240 2 +dl_datalistmodel.property.dl_locationAddress3.title=\u4f4f\u6240 3 +dl_datalistmodel.property.dl_locationZip.title=\u90f5\u4fbf\u756a\u53f7 +dl_datalistmodel.property.dl_locationState.title=\u90fd\u9053\u5e9c\u770c +dl_datalistmodel.property.dl_locationCountry.title=\u56fd +dl_datalistmodel.property.dl_locationNote.title=\u30e1\u30e2 + +# Meeting Agenda +dl_datalistmodel.type.dl_meetingAgenda.title=\u4f1a\u8b70\u30a2\u30b8\u30a7\u30f3\u30c0 +dl_datalistmodel.type.dl_meetingAgenda.description=\u4f1a\u8b70\u306e\u30a2\u30b8\u30a7\u30f3\u30c0 (\u8aac\u660e\u3001\u4e3b\u50ac\u8005\u3001\u4e88\u5b9a\u6642\u9593) \u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002 +dl_datalistmodel.property.dl_meetingAgendaRef.title=\u53c2\u7167 +dl_datalistmodel.property.dl_meetingAgendaTime.title=\u6642\u9593 (\u5206) +dl_datalistmodel.property.dl_meetingAgendaOwner.title=\u6240\u6709\u8005 + +# Event Agenda +dl_datalistmodel.type.dl_eventAgenda.title=\u30a4\u30d9\u30f3\u30c8\u30a2\u30b8\u30a7\u30f3\u30c0 +dl_datalistmodel.type.dl_eventAgenda.description=\u30a4\u30d9\u30f3\u30c8\u306e\u30a2\u30b8\u30a7\u30f3\u30c0 (\u30bb\u30c3\u30b7\u30e7\u30f3\u540d\u3001\u767a\u8868\u8005\u3001\u958b\u59cb\u6642\u523b\u3001\u7d42\u4e86\u6642\u523b) \u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002 +dl_datalistmodel.property.dl_eventAgendaRef.title=\u53c2\u7167 +dl_datalistmodel.property.dl_eventAgendaStartTime.title=\u958b\u59cb\u6642\u523b +dl_datalistmodel.property.dl_eventAgendaEndTime.title=\u7d42\u4e86\u6642\u523b +dl_datalistmodel.property.dl_eventAgendaSessionName.title=\u30bb\u30c3\u30b7\u30e7\u30f3\u540d +dl_datalistmodel.property.dl_eventAgendaPresenter.title=\u767a\u8868\u8005 +dl_datalistmodel.property.dl_eventAgendaAudience.title=\u53c2\u52a0\u8005 +dl_datalistmodel.property.dl_eventAgendaNotes.title=\u30e1\u30e2 + +# List constraint display labels +listconstraint.dl_task_status.Not\ Started=\u958b\u59cb\u524d +listconstraint.dl_task_status.In\ Progress=\u9032\u884c\u4e2d +listconstraint.dl_task_status.On\ Hold=\u4fdd\u7559\u4e2d +listconstraint.dl_task_status.Complete=\u5b8c\u4e86 +listconstraint.dl_priority_value.High=\u9ad8 +listconstraint.dl_priority_value.Normal=\u4e2d +listconstraint.dl_priority_value.Low=\u4f4e + diff --git a/amps/share-services/src/main/resources/alfresco/messages/data-list-model_nb.properties b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_nb.properties new file mode 100755 index 0000000000..e4181c1bd5 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_nb.properties @@ -0,0 +1,114 @@ +# Display labels for Share Data Lists Model +dl_datalistmodel.description=Datalistemodell for Alfresco Deling + +dl_datalistmodel.type.dl_dataList.title=Mappetype for dataliste +dl_datalistmodel.type.dl_dataList.description=Holder datalisteelementer av den typen som angis i egenskapen dl:dataListItemType. +dl_datalistmodel.property.dl_dataListItemType.title=Type datalisteelement +dl_datalistmodel.property.dl_dataListItemType.description=Fastsetter hvilken undertype av dl:dataListItem som brukes n\u00e5r nye elementer opprettes innenfor datalisten. + +dl_datalistmodel.type.dl_dataListItem.title=Overordnet type for dataliste +dl_datalistmodel.type.dl_dataListItem.description=Fra hvilken overordnet type som utvalg av datalisteelementtyper hentes fra. + +## Simple "To do" List +dl_datalistmodel.type.dl_todoList.title=Liste over gj\u00f8rem\u00e5l +dl_datalistmodel.type.dl_todoList.description=En enkel liste over gj\u00f8rem\u00e5l med valgfri tilordnet person. +dl_datalistmodel.property.dl_todoTitle.title=Tittel +dl_datalistmodel.property.dl_todoDueDate.title=Forfallsdato +dl_datalistmodel.property.dl_todoPriority.title=Prioritet +dl_datalistmodel.property.dl_todoStatus.title=Status +dl_datalistmodel.property.dl_todoNotes.title=Merknader +dl_datalistmodel.association.dl_assignee.title=Tilordnet +dl_datalistmodel.association.dl_attachments.title=Vedlegg + +# Gantt Aspect +dl_datalistmodel.property.dl_ganttStartDate.title=Startdato +dl_datalistmodel.property.dl_ganttEndDate.title=Sluttdato +dl_datalistmodel.property.dl_ganttPercentComplete.title=% fullf\u00f8rt + +# Task List (Advanced) +dl_datalistmodel.type.dl_task.title=Oppgaveliste (avansert) +dl_datalistmodel.type.dl_task.description=Avansert oppgaveliste som inkluderer tittel, beskrivelse, start- og sluttdatoer, prioritet, status, kommentarer, tilordnede og vedlegg. +dl_datalistmodel.property.dl_taskPriority.title=Prioritet +dl_datalistmodel.property.dl_taskStatus.title=Status +dl_datalistmodel.property.dl_taskComments.title=Kommentarer +dl_datalistmodel.association.dl_taskAssignee.title=Tilordnet + +# Task List (Simple) +dl_datalistmodel.type.dl_simpletask.title=Oppgaveliste (enkel) +dl_datalistmodel.type.dl_simpletask.description=Enkel oppgaveliste som inkluderer tittel, beskrivelse, forfallsdato, prioritet, status og kommentarer. +dl_datalistmodel.property.dl_simpletaskDueDate.title=Forfallsdato +dl_datalistmodel.property.dl_simpletaskPriority.title=Prioritet +dl_datalistmodel.property.dl_simpletaskStatus.title=Status +dl_datalistmodel.property.dl_simpletaskComments.title=Kommentarer + +# Contact +dl_datalistmodel.type.dl_contact.title=Kontaktliste +dl_datalistmodel.type.dl_contact.description=Kontaktliste som inkluderer fornavn, etternavn, fullt navn, jobbtittel, telefon (kontor), telefon (mobil). +dl_datalistmodel.property.dl_contactFirstName.title=Fornavn +dl_datalistmodel.property.dl_contactLastName.title=Etternavn +dl_datalistmodel.property.dl_contactEmail.title=E-post +dl_datalistmodel.property.dl_contactCompany.title=Firma +dl_datalistmodel.property.dl_contactJobTitle.title=Stillingstittel +dl_datalistmodel.property.dl_contactPhoneOffice.title=Telefon (kontor) +dl_datalistmodel.property.dl_contactPhoneMobile.title=Telefon (mobil) +dl_datalistmodel.property.dl_contactNotes.title=Merknader + +# Issues +dl_datalistmodel.type.dl_issue.title=Problemliste +dl_datalistmodel.type.dl_issue.description=Problemliste som inkluderer ID, status, prioritet, beskrivelse, forfallsdato, kommentarer, tilordnet til, relaterte problemer. +dl_datalistmodel.property.dl_issueID.title=Problem-ID +dl_datalistmodel.property.dl_issueStatus.title=Status +dl_datalistmodel.property.dl_issuePriority.title=Prioritet +dl_datalistmodel.property.dl_issueDescription.title=Beskrivelse +dl_datalistmodel.property.dl_issueDueDate.title=Forfallsdato +dl_datalistmodel.property.dl_issueComments.title=Kommentarer +dl_datalistmodel.association.dl_issueAssignedTo.title=Tilordnet til +dl_datalistmodel.property.dl_issueRelatedIssues.title=Relaterte problemer + +# Event +dl_datalistmodel.type.dl_event.title=Hendelsesliste +dl_datalistmodel.type.dl_event.description=Hendelsesliste som inkluderer tittel, beskrivelse, sted, start- og sluttdato/-tid. +dl_datalistmodel.property.dl_eventLocation.title=Sted +dl_datalistmodel.property.dl_eventStartDate.title=Startdato +dl_datalistmodel.property.dl_eventEndDate.title=Sluttdato +dl_datalistmodel.property.dl_eventRegistrations.title=Registreringer +dl_datalistmodel.property.dl_eventNote.title=Merknader + +# Location +dl_datalistmodel.type.dl_location.title=Stedsliste +dl_datalistmodel.type.dl_location.description=Liste over steder/adresser +dl_datalistmodel.property.dl_locationAddress1.title=Adresserad 1 +dl_datalistmodel.property.dl_locationAddress2.title=Adresserad 2 +dl_datalistmodel.property.dl_locationAddress3.title=Adresserad 3 +dl_datalistmodel.property.dl_locationZip.title=Postnummer +dl_datalistmodel.property.dl_locationState.title=Fylke/kommune +dl_datalistmodel.property.dl_locationCountry.title=Land +dl_datalistmodel.property.dl_locationNote.title=Merknader + +# Meeting Agenda +dl_datalistmodel.type.dl_meetingAgenda.title=Saksliste +dl_datalistmodel.type.dl_meetingAgenda.description=Administrer sakslisteelementer som inkluderer beskrivelse, eier, tildelt tid. +dl_datalistmodel.property.dl_meetingAgendaRef.title=Referanse +dl_datalistmodel.property.dl_meetingAgendaTime.title=Tid (minutter) +dl_datalistmodel.property.dl_meetingAgendaOwner.title=Eier + +# Event Agenda +dl_datalistmodel.type.dl_eventAgenda.title=Hendelsesagenda +dl_datalistmodel.type.dl_eventAgenda.description=Administrer hendelsesagendaelementer som inkluderer \u00f8ktnavn, person som presenterer, start- og sluttider. +dl_datalistmodel.property.dl_eventAgendaRef.title=Referanse +dl_datalistmodel.property.dl_eventAgendaStartTime.title=Starttid +dl_datalistmodel.property.dl_eventAgendaEndTime.title=Sluttid +dl_datalistmodel.property.dl_eventAgendaSessionName.title=\u00d8ktnavn +dl_datalistmodel.property.dl_eventAgendaPresenter.title=Person som presenterer +dl_datalistmodel.property.dl_eventAgendaAudience.title=Publikum +dl_datalistmodel.property.dl_eventAgendaNotes.title=Merknader + +# List constraint display labels +listconstraint.dl_task_status.Not\ Started=Ikke startet +listconstraint.dl_task_status.In\ Progress=P\u00e5g\u00e5r +listconstraint.dl_task_status.On\ Hold=P\u00e5 vent +listconstraint.dl_task_status.Complete=Fullf\u00f8r +listconstraint.dl_priority_value.High=H\u00f8y +listconstraint.dl_priority_value.Normal=Normal +listconstraint.dl_priority_value.Low=Lav + diff --git a/amps/share-services/src/main/resources/alfresco/messages/data-list-model_nl.properties b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_nl.properties new file mode 100755 index 0000000000..94984dbfc8 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_nl.properties @@ -0,0 +1,114 @@ +# Display labels for Share Data Lists Model +dl_datalistmodel.description=Alfresco Share-gegevenslijstmodel + +dl_datalistmodel.type.dl_dataList.title=Maptype van gegevenslijst +dl_datalistmodel.type.dl_dataList.description=Bevat gegevenslijstobjecten van het type dat is opgegeven in de eigenschap dl:dataListItemType. +dl_datalistmodel.property.dl_dataListItemType.title=Objecttype van gegevenslijst +dl_datalistmodel.property.dl_dataListItemType.description=Bepaalt welk subtype van dl:dataListItem wordt gebruikt bij het maken van nieuwe objecten in de gegevenslijst. + +dl_datalistmodel.type.dl_dataListItem.title=Bovenliggend type van gegevenslijst +dl_datalistmodel.type.dl_dataListItem.description=Bovenliggend type waarvan voorbeeldobjecttypen van de gegevenslijst zijn afgeleid. + +## Simple "To do" List +dl_datalistmodel.type.dl_todoList.title=Takenlijst +dl_datalistmodel.type.dl_todoList.description=Een eenvoudige takenlijst met een optionele toegewezen persoon. +dl_datalistmodel.property.dl_todoTitle.title=Titel +dl_datalistmodel.property.dl_todoDueDate.title=Vervaldatum +dl_datalistmodel.property.dl_todoPriority.title=Prioriteit +dl_datalistmodel.property.dl_todoStatus.title=Status +dl_datalistmodel.property.dl_todoNotes.title=Opmerkingen +dl_datalistmodel.association.dl_assignee.title=Toegewezen persoon +dl_datalistmodel.association.dl_attachments.title=Bijlagen + +# Gantt Aspect +dl_datalistmodel.property.dl_ganttStartDate.title=Begindatum +dl_datalistmodel.property.dl_ganttEndDate.title=Einddatum +dl_datalistmodel.property.dl_ganttPercentComplete.title=% voltooid + +# Task List (Advanced) +dl_datalistmodel.type.dl_task.title=Takenlijst (geavanceerd) +dl_datalistmodel.type.dl_task.description=Geavanceerde takenlijst, inclusief titel, beschrijving, begin- en einddatum, prioriteit, status, opmerkingen, toegewezen personen en bijlagen. +dl_datalistmodel.property.dl_taskPriority.title=Prioriteit +dl_datalistmodel.property.dl_taskStatus.title=Status +dl_datalistmodel.property.dl_taskComments.title=Opmerkingen +dl_datalistmodel.association.dl_taskAssignee.title=Toegewezen persoon + +# Task List (Simple) +dl_datalistmodel.type.dl_simpletask.title=Takenlijst (eenvoudig) +dl_datalistmodel.type.dl_simpletask.description=Eenvoudige takenlijst, inclusief titel, beschrijving, vervaldatum, prioriteit, status en opmerkingen. +dl_datalistmodel.property.dl_simpletaskDueDate.title=Vervaldatum +dl_datalistmodel.property.dl_simpletaskPriority.title=Prioriteit +dl_datalistmodel.property.dl_simpletaskStatus.title=Status +dl_datalistmodel.property.dl_simpletaskComments.title=Opmerkingen + +# Contact +dl_datalistmodel.type.dl_contact.title=Lijst met contactpersonen +dl_datalistmodel.type.dl_contact.description=Lijst met contactpersonen, inclusief voornaam, achternaam, volledige naam, e-mailadres, functie, telefoon (kantoor), telefoon (mobiel). +dl_datalistmodel.property.dl_contactFirstName.title=Voornaam +dl_datalistmodel.property.dl_contactLastName.title=Achternaam +dl_datalistmodel.property.dl_contactEmail.title=E-mailadres +dl_datalistmodel.property.dl_contactCompany.title=Bedrijf +dl_datalistmodel.property.dl_contactJobTitle.title=Functie +dl_datalistmodel.property.dl_contactPhoneOffice.title=Telefoon (kantoor) +dl_datalistmodel.property.dl_contactPhoneMobile.title=Telefoon (mobiel) +dl_datalistmodel.property.dl_contactNotes.title=Opmerkingen + +# Issues +dl_datalistmodel.type.dl_issue.title=Lijst met problemen +dl_datalistmodel.type.dl_issue.description=Lijst met problemen, inclusief id, status, prioriteit, beschrijving, vervaldatums, opmerkingen, toewijzen aan, gerelateerde problemen. +dl_datalistmodel.property.dl_issueID.title=Probleem-id +dl_datalistmodel.property.dl_issueStatus.title=Status +dl_datalistmodel.property.dl_issuePriority.title=Prioriteit +dl_datalistmodel.property.dl_issueDescription.title=Beschrijving +dl_datalistmodel.property.dl_issueDueDate.title=Vervaldatum +dl_datalistmodel.property.dl_issueComments.title=Opmerkingen +dl_datalistmodel.association.dl_issueAssignedTo.title=Toegewezen aan +dl_datalistmodel.property.dl_issueRelatedIssues.title=Gerelateerde problemen + +# Event +dl_datalistmodel.type.dl_event.title=Lijst met gebeurtenissen +dl_datalistmodel.type.dl_event.description=Lijst met gebeurtenissen, inclusief titel, beschrijving, locatie en begin- en einddatum/-tijd. +dl_datalistmodel.property.dl_eventLocation.title=Locatie +dl_datalistmodel.property.dl_eventStartDate.title=Begindatum +dl_datalistmodel.property.dl_eventEndDate.title=Einddatum +dl_datalistmodel.property.dl_eventRegistrations.title=Registraties +dl_datalistmodel.property.dl_eventNote.title=Opmerkingen + +# Location +dl_datalistmodel.type.dl_location.title=Lijst met locaties +dl_datalistmodel.type.dl_location.description=Lijst met locaties/adressen +dl_datalistmodel.property.dl_locationAddress1.title=Adresregel 1 +dl_datalistmodel.property.dl_locationAddress2.title=Adresregel 2 +dl_datalistmodel.property.dl_locationAddress3.title=Adresregel 3 +dl_datalistmodel.property.dl_locationZip.title=Postcode +dl_datalistmodel.property.dl_locationState.title=Staat/provincie +dl_datalistmodel.property.dl_locationCountry.title=Land +dl_datalistmodel.property.dl_locationNote.title=Opmerkingen + +# Meeting Agenda +dl_datalistmodel.type.dl_meetingAgenda.title=Vergaderagenda +dl_datalistmodel.type.dl_meetingAgenda.description=Beheer vergaderagenda-items zoals beschrijving, eigenaar, toegewezen tijd. +dl_datalistmodel.property.dl_meetingAgendaRef.title=Verwijzing +dl_datalistmodel.property.dl_meetingAgendaTime.title=Tijd (min.) +dl_datalistmodel.property.dl_meetingAgendaOwner.title=Eigenaar + +# Event Agenda +dl_datalistmodel.type.dl_eventAgenda.title=Gebeurtenisagenda +dl_datalistmodel.type.dl_eventAgenda.description=Beheer gebeurtenisagenda-items zoals sessienamen, presentators en begin- en eindtijden. +dl_datalistmodel.property.dl_eventAgendaRef.title=Verwijzing +dl_datalistmodel.property.dl_eventAgendaStartTime.title=Begintijd +dl_datalistmodel.property.dl_eventAgendaEndTime.title=Eindtijd +dl_datalistmodel.property.dl_eventAgendaSessionName.title=Sessienaam +dl_datalistmodel.property.dl_eventAgendaPresenter.title=Presentator +dl_datalistmodel.property.dl_eventAgendaAudience.title=Publiek +dl_datalistmodel.property.dl_eventAgendaNotes.title=Opmerkingen + +# List constraint display labels +listconstraint.dl_task_status.Not\ Started=Niet gestart +listconstraint.dl_task_status.In\ Progress=In uitvoering +listconstraint.dl_task_status.On\ Hold=In bewaring +listconstraint.dl_task_status.Complete=Voltooid +listconstraint.dl_priority_value.High=Hoog +listconstraint.dl_priority_value.Normal=Normaal +listconstraint.dl_priority_value.Low=Laag + diff --git a/amps/share-services/src/main/resources/alfresco/messages/data-list-model_pt_BR.properties b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_pt_BR.properties new file mode 100644 index 0000000000..96e68fadc4 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_pt_BR.properties @@ -0,0 +1,114 @@ +# Display labels for Share Data Lists Model +dl_datalistmodel.description=Modelo de lista de dados do Alfresco Share + +dl_datalistmodel.type.dl_dataList.title=Tipo de pasta de lista de dados +dl_datalistmodel.type.dl_dataList.description=Cont\u00e9m itens de lista de dados do tipo especificado na propriedade dl:dataListItemType. +dl_datalistmodel.property.dl_dataListItemType.title=Tipo de item da lista de dados +dl_datalistmodel.property.dl_dataListItemType.description=Determina qual subtipo de dl:dataListItem ser\u00e1 usado quando criar novos itens na lista de dados. + +dl_datalistmodel.type.dl_dataListItem.title=Tipo prim\u00e1rio da lista de dados +dl_datalistmodel.type.dl_dataListItem.description=Tipo prim\u00e1rio a partir do qual os tipos de item da lista de dados amostrais s\u00e3o derivados. + +## Simple "To do" List +dl_datalistmodel.type.dl_todoList.title=Lista de tarefas +dl_datalistmodel.type.dl_todoList.description=Uma lista de tarefas simples com destinat\u00e1rio opcional. +dl_datalistmodel.property.dl_todoTitle.title=T\u00edtulo +dl_datalistmodel.property.dl_todoDueDate.title=Data de vencimento +dl_datalistmodel.property.dl_todoPriority.title=Prioridade +dl_datalistmodel.property.dl_todoStatus.title=Status +dl_datalistmodel.property.dl_todoNotes.title=Notas +dl_datalistmodel.association.dl_assignee.title=Destinat\u00e1rio +dl_datalistmodel.association.dl_attachments.title=Anexos + +# Gantt Aspect +dl_datalistmodel.property.dl_ganttStartDate.title=Data de in\u00edcio +dl_datalistmodel.property.dl_ganttEndDate.title=Data de t\u00e9rmino +dl_datalistmodel.property.dl_ganttPercentComplete.title=% Completa + +# Task List (Advanced) +dl_datalistmodel.type.dl_task.title=Lista de tarefas (avan\u00e7ada) +dl_datalistmodel.type.dl_task.description=Lista de tarefas avan\u00e7ada, incluindo t\u00edtulo, descri\u00e7\u00e3o, datas de in\u00edcio e t\u00e9rmino, prioridade, status, coment\u00e1rios, destinat\u00e1rios e anexos. +dl_datalistmodel.property.dl_taskPriority.title=Prioridade +dl_datalistmodel.property.dl_taskStatus.title=Status +dl_datalistmodel.property.dl_taskComments.title=Coment\u00e1rios +dl_datalistmodel.association.dl_taskAssignee.title=Destinat\u00e1rio + +# Task List (Simple) +dl_datalistmodel.type.dl_simpletask.title=Lista de tarefas (simples) +dl_datalistmodel.type.dl_simpletask.description=Lista de tarefas simples, incluindo t\u00edtulo, descri\u00e7\u00e3o, prazo, prioridade, status e coment\u00e1rios. +dl_datalistmodel.property.dl_simpletaskDueDate.title=Data de vencimento +dl_datalistmodel.property.dl_simpletaskPriority.title=Prioridade +dl_datalistmodel.property.dl_simpletaskStatus.title=Status +dl_datalistmodel.property.dl_simpletaskComments.title=Coment\u00e1rios + +# Contact +dl_datalistmodel.type.dl_contact.title=Lista de contatos +dl_datalistmodel.type.dl_contact.description=Lista de contatos, incluindo o nome, sobrenome, nome completo, email, cargo, telefone (escrit\u00f3rio) e telefone (celular). +dl_datalistmodel.property.dl_contactFirstName.title=Nome +dl_datalistmodel.property.dl_contactLastName.title=Sobrenome +dl_datalistmodel.property.dl_contactEmail.title=Email +dl_datalistmodel.property.dl_contactCompany.title=Empresa +dl_datalistmodel.property.dl_contactJobTitle.title=Cargo +dl_datalistmodel.property.dl_contactPhoneOffice.title=Telefone (escrit\u00f3rio) +dl_datalistmodel.property.dl_contactPhoneMobile.title=Telefone (celular) +dl_datalistmodel.property.dl_contactNotes.title=Notas + +# Issues +dl_datalistmodel.type.dl_issue.title=Lista de problemas +dl_datalistmodel.type.dl_issue.description=Lista de problemas, incluindo ID, status, prioridade, descri\u00e7\u00e3o, prazo, coment\u00e1rios, atribuir e problemas relacionados. +dl_datalistmodel.property.dl_issueID.title=ID do problema +dl_datalistmodel.property.dl_issueStatus.title=Status +dl_datalistmodel.property.dl_issuePriority.title=Prioridade +dl_datalistmodel.property.dl_issueDescription.title=Descri\u00e7\u00e3o +dl_datalistmodel.property.dl_issueDueDate.title=Data de vencimento +dl_datalistmodel.property.dl_issueComments.title=Coment\u00e1rios +dl_datalistmodel.association.dl_issueAssignedTo.title=Atribu\u00eddo para +dl_datalistmodel.property.dl_issueRelatedIssues.title=Problemas relacionados + +# Event +dl_datalistmodel.type.dl_event.title=Lista de eventos +dl_datalistmodel.type.dl_event.description=Lista de eventos incluindo t\u00edtulo, descri\u00e7\u00e3o, localiza\u00e7\u00e3o e data/hora de in\u00edcio e t\u00e9rmino. +dl_datalistmodel.property.dl_eventLocation.title=Localiza\u00e7\u00e3o +dl_datalistmodel.property.dl_eventStartDate.title=Data de in\u00edcio +dl_datalistmodel.property.dl_eventEndDate.title=Data de t\u00e9rmino +dl_datalistmodel.property.dl_eventRegistrations.title=Registros +dl_datalistmodel.property.dl_eventNote.title=Notas + +# Location +dl_datalistmodel.type.dl_location.title=Lista de localiza\u00e7\u00e3o +dl_datalistmodel.type.dl_location.description=Lista de localiza\u00e7\u00f5es/endere\u00e7os +dl_datalistmodel.property.dl_locationAddress1.title=Endere\u00e7o, linha 1 +dl_datalistmodel.property.dl_locationAddress2.title=Endere\u00e7o, linha 2 +dl_datalistmodel.property.dl_locationAddress3.title=Endere\u00e7o, linha 3 +dl_datalistmodel.property.dl_locationZip.title=C\u00f3digo postal/CEP +dl_datalistmodel.property.dl_locationState.title=Estado/munic\u00edpio +dl_datalistmodel.property.dl_locationCountry.title=Pa\u00eds +dl_datalistmodel.property.dl_locationNote.title=Notas + +# Meeting Agenda +dl_datalistmodel.type.dl_meetingAgenda.title=Agenda de reuni\u00e3o +dl_datalistmodel.type.dl_meetingAgenda.description=Gerencie itens de agenda de reuni\u00e3o, incluindo descri\u00e7\u00e3o, propriet\u00e1rio e tempo alocado. +dl_datalistmodel.property.dl_meetingAgendaRef.title=Refer\u00eancia +dl_datalistmodel.property.dl_meetingAgendaTime.title=Tempo (minutos) +dl_datalistmodel.property.dl_meetingAgendaOwner.title=Propriet\u00e1rio + +# Event Agenda +dl_datalistmodel.type.dl_eventAgenda.title=Agenda de evento +dl_datalistmodel.type.dl_eventAgenda.description=Gerencie itens de agenda de evento, incluindo nomes de sess\u00e3o, apresentadores e horas de in\u00edcio e t\u00e9rmino. +dl_datalistmodel.property.dl_eventAgendaRef.title=Refer\u00eancia +dl_datalistmodel.property.dl_eventAgendaStartTime.title=Hora de in\u00edcio +dl_datalistmodel.property.dl_eventAgendaEndTime.title=Hora de t\u00e9rmino +dl_datalistmodel.property.dl_eventAgendaSessionName.title=Nome da sess\u00e3o +dl_datalistmodel.property.dl_eventAgendaPresenter.title=Apresentador +dl_datalistmodel.property.dl_eventAgendaAudience.title=P\u00fablico +dl_datalistmodel.property.dl_eventAgendaNotes.title=Notas + +# List constraint display labels +listconstraint.dl_task_status.Not\ Started=N\u00e3o iniciado +listconstraint.dl_task_status.In\ Progress=Em andamento +listconstraint.dl_task_status.On\ Hold=Em espera +listconstraint.dl_task_status.Complete=Conclu\u00eddo +listconstraint.dl_priority_value.High=Alto +listconstraint.dl_priority_value.Normal=Normal +listconstraint.dl_priority_value.Low=Baixo + diff --git a/amps/share-services/src/main/resources/alfresco/messages/data-list-model_ru.properties b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_ru.properties new file mode 100755 index 0000000000..8769340b4d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_ru.properties @@ -0,0 +1,114 @@ +# Display labels for Share Data Lists Model +dl_datalistmodel.description=\u041c\u043e\u0434\u0435\u043b\u044c \u0441\u043f\u0438\u0441\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 Alfresco Share + +dl_datalistmodel.type.dl_dataList.title=\u0422\u0438\u043f \u043f\u0430\u043f\u043a\u0438 \u0441\u043f\u0438\u0441\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 +dl_datalistmodel.type.dl_dataList.description=\u0421\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0441\u043f\u0438\u0441\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0442\u0438\u043f\u0430, \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0432 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0435 dl:dataListItemType. +dl_datalistmodel.property.dl_dataListItemType.title=\u0422\u0438\u043f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u043f\u0438\u0441\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 +dl_datalistmodel.property.dl_dataListItemType.description=\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0442\u0438\u043f dl:dataListItem \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043d\u043e\u0432\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u043f\u0438\u0441\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445. + +dl_datalistmodel.type.dl_dataListItem.title=\u0420\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0442\u0438\u043f \u0441\u043f\u0438\u0441\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 +dl_datalistmodel.type.dl_dataListItem.description=\u0420\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0442\u0438\u043f, \u043e\u0442 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u044b \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0442\u0438\u043f\u043e\u0432 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u043f\u0438\u0441\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445. + +## Simple "To do" List +dl_datalistmodel.type.dl_todoList.title=\u0421\u043f\u0438\u0441\u043e\u043a \u0434\u0435\u043b +dl_datalistmodel.type.dl_todoList.description=\u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0434\u0435\u043b \u0441 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0438\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044f\u043c\u0438. +dl_datalistmodel.property.dl_todoTitle.title=\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a +dl_datalistmodel.property.dl_todoDueDate.title=\u0421\u0440\u043e\u043a +dl_datalistmodel.property.dl_todoPriority.title=\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 +dl_datalistmodel.property.dl_todoStatus.title=\u0421\u0442\u0430\u0442\u0443\u0441 +dl_datalistmodel.property.dl_todoNotes.title=\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u044f +dl_datalistmodel.association.dl_assignee.title=\u0418\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c +dl_datalistmodel.association.dl_attachments.title=\u0412\u043b\u043e\u0436\u0435\u043d\u0438\u044f + +# Gantt Aspect +dl_datalistmodel.property.dl_ganttStartDate.title=\u0414\u0430\u0442\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 +dl_datalistmodel.property.dl_ganttEndDate.title=\u0414\u0430\u0442\u0430 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f +dl_datalistmodel.property.dl_ganttPercentComplete.title=\u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043e % + +# Task List (Advanced) +dl_datalistmodel.type.dl_task.title=\u0421\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447 (\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0439) +dl_datalistmodel.type.dl_task.description=\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0438\u0439 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a, \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435, \u0434\u0430\u0442\u044b \u043d\u0430\u0447\u0430\u043b\u0430 \u0438 \u043a\u043e\u043d\u0446\u0430, \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442, \u0441\u0442\u0430\u0442\u0443\u0441, \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0438\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u0435\u0439 \u0438 \u0432\u043b\u043e\u0436\u0435\u043d\u0438\u044f. +dl_datalistmodel.property.dl_taskPriority.title=\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 +dl_datalistmodel.property.dl_taskStatus.title=\u0421\u0442\u0430\u0442\u0443\u0441 +dl_datalistmodel.property.dl_taskComments.title=\u041a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 +dl_datalistmodel.association.dl_taskAssignee.title=\u0418\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c + +# Task List (Simple) +dl_datalistmodel.type.dl_simpletask.title=\u0421\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447 (\u043f\u0440\u043e\u0441\u0442\u043e\u0439) +dl_datalistmodel.type.dl_simpletask.description=\u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0438\u0439 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a, \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435, \u0441\u0440\u043e\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442, \u0441\u0442\u0430\u0442\u0443\u0441, \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438. +dl_datalistmodel.property.dl_simpletaskDueDate.title=\u0421\u0440\u043e\u043a +dl_datalistmodel.property.dl_simpletaskPriority.title=\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 +dl_datalistmodel.property.dl_simpletaskStatus.title=\u0421\u0442\u0430\u0442\u0443\u0441 +dl_datalistmodel.property.dl_simpletaskComments.title=\u041a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 + +# Contact +dl_datalistmodel.type.dl_contact.title=\u0421\u043f\u0438\u0441\u043e\u043a \u043a\u043e\u043d\u0442\u0430\u043a\u0442\u043e\u0432 +dl_datalistmodel.type.dl_contact.description=\u0421\u043f\u0438\u0441\u043e\u043a \u043a\u043e\u043d\u0442\u0430\u043a\u0442\u043e\u0432, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0438\u0439 \u0438\u043c\u044f, \u0444\u0430\u043c\u0438\u043b\u0438\u044e, \u043f\u043e\u043b\u043d\u043e\u0435 \u0438\u043c\u044f, \u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b, \u0434\u043e\u043b\u0436\u043d\u043e\u0441\u0442\u044c, \u043d\u043e\u043c\u0435\u0440 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430 (\u043e\u0444\u0438\u0441), \u043d\u043e\u043c\u0435\u0440 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430 (\u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e). +dl_datalistmodel.property.dl_contactFirstName.title=\u0418\u043c\u044f +dl_datalistmodel.property.dl_contactLastName.title=\u0424\u0430\u043c\u0438\u043b\u0438\u044f +dl_datalistmodel.property.dl_contactEmail.title=\u042d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0430\u044f \u043f\u043e\u0447\u0442\u0430 +dl_datalistmodel.property.dl_contactCompany.title=\u041a\u043e\u043c\u043f\u0430\u043d\u0438\u044f +dl_datalistmodel.property.dl_contactJobTitle.title=\u0414\u043e\u043b\u0436\u043d\u043e\u0441\u0442\u044c +dl_datalistmodel.property.dl_contactPhoneOffice.title=\u041d\u043e\u043c\u0435\u0440 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430 (\u043e\u0444\u0438\u0441) +dl_datalistmodel.property.dl_contactPhoneMobile.title=\u041d\u043e\u043c\u0435\u0440 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430 (\u043c\u043e\u0431) +dl_datalistmodel.property.dl_contactNotes.title=\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u044f + +# Issues +dl_datalistmodel.type.dl_issue.title=\u0421\u043f\u0438\u0441\u043e\u043a \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u0432 +dl_datalistmodel.type.dl_issue.description=\u0421\u043f\u0438\u0441\u043e\u043a \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u0432, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0438\u0439 \u043d\u043e\u043c\u0435\u0440 \u0432\u043e\u043f\u0440\u043e\u0441\u0430, \u0441\u0442\u0430\u0442\u0443\u0441, \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442, \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435, \u0441\u0440\u043e\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438, \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b. +dl_datalistmodel.property.dl_issueID.title=\u041d\u043e\u043c\u0435\u0440 \u0432\u043e\u043f\u0440\u043e\u0441\u0430 +dl_datalistmodel.property.dl_issueStatus.title=\u0421\u0442\u0430\u0442\u0443\u0441 +dl_datalistmodel.property.dl_issuePriority.title=\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 +dl_datalistmodel.property.dl_issueDescription.title=\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 +dl_datalistmodel.property.dl_issueDueDate.title=\u0421\u0440\u043e\u043a +dl_datalistmodel.property.dl_issueComments.title=\u041a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 +dl_datalistmodel.association.dl_issueAssignedTo.title=\u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043e +dl_datalistmodel.property.dl_issueRelatedIssues.title=\u0421\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b + +# Event +dl_datalistmodel.type.dl_event.title=\u0421\u043f\u0438\u0441\u043e\u043a \u0441\u043e\u0431\u044b\u0442\u0438\u0439 +dl_datalistmodel.type.dl_event.description=\u0421\u043f\u0438\u0441\u043e\u043a \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0438\u0439 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a, \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435, \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0434\u0430\u0442\u0443/\u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0438 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f. +dl_datalistmodel.property.dl_eventLocation.title=\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 +dl_datalistmodel.property.dl_eventStartDate.title=\u0414\u0430\u0442\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 +dl_datalistmodel.property.dl_eventEndDate.title=\u0414\u0430\u0442\u0430 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f +dl_datalistmodel.property.dl_eventRegistrations.title=\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 +dl_datalistmodel.property.dl_eventNote.title=\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u044f + +# Location +dl_datalistmodel.type.dl_location.title=\u0421\u043f\u0438\u0441\u043e\u043a \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0439 +dl_datalistmodel.type.dl_location.description=\u0421\u043f\u0438\u0441\u043e\u043a \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0439/\u0430\u0434\u0440\u0435\u0441\u043e\u0432 +dl_datalistmodel.property.dl_locationAddress1.title=\u0410\u0434\u0440\u0435\u0441 (\u0441\u0442\u0440\u043e\u043a\u0430 1) +dl_datalistmodel.property.dl_locationAddress2.title=\u0410\u0434\u0440\u0435\u0441 (\u0441\u0442\u0440\u043e\u043a\u0430 2) +dl_datalistmodel.property.dl_locationAddress3.title=\u0410\u0434\u0440\u0435\u0441 (\u0441\u0442\u0440\u043e\u043a\u0430 3) +dl_datalistmodel.property.dl_locationZip.title=\u041f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 +dl_datalistmodel.property.dl_locationState.title=\u041e\u0431\u043b\u0430\u0441\u0442\u044c +dl_datalistmodel.property.dl_locationCountry.title=\u0421\u0442\u0440\u0430\u043d\u0430 +dl_datalistmodel.property.dl_locationNote.title=\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u044f + +# Meeting Agenda +dl_datalistmodel.type.dl_meetingAgenda.title=\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0441\u043e\u0431\u0440\u0430\u043d\u0438\u044f +dl_datalistmodel.type.dl_meetingAgenda.description=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0441\u043e\u0431\u0440\u0430\u043d\u0438\u044f, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435, \u0432\u043b\u0430\u0434\u0435\u043b\u044c\u0446\u0430, \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f. +dl_datalistmodel.property.dl_meetingAgendaRef.title=\u0421\u0441\u044b\u043b\u043a\u0430 +dl_datalistmodel.property.dl_meetingAgendaTime.title=\u0412\u0440\u0435\u043c\u044f (\u043c\u0438\u043d.) +dl_datalistmodel.property.dl_meetingAgendaOwner.title=\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446 + +# Event Agenda +dl_datalistmodel.type.dl_eventAgenda.title=\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u043c\u0435\u0440\u043e\u043f\u0440\u0438\u044f\u0442\u0438\u044f +dl_datalistmodel.type.dl_eventAgenda.description=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u043c\u0435\u0440\u043e\u043f\u0440\u0438\u044f\u0442\u0438\u044f, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0434\u043e\u043a\u043b\u0430\u0434\u043e\u0432, \u0438\u043c\u0435\u043d\u0430 \u0434\u043e\u043a\u043b\u0430\u0434\u0447\u0438\u043a\u043e\u0432, \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0438 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f \u0434\u043e\u043a\u043b\u0430\u0434\u043e\u0432. +dl_datalistmodel.property.dl_eventAgendaRef.title=\u0421\u0441\u044b\u043b\u043a\u0430 +dl_datalistmodel.property.dl_eventAgendaStartTime.title=\u0412\u0440\u0435\u043c\u044f \u043d\u0430\u0447\u0430\u043b\u0430 +dl_datalistmodel.property.dl_eventAgendaEndTime.title=\u0412\u0440\u0435\u043c\u044f \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f +dl_datalistmodel.property.dl_eventAgendaSessionName.title=\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 +dl_datalistmodel.property.dl_eventAgendaPresenter.title=\u0414\u043e\u043a\u043b\u0430\u0434\u0447\u0438\u043a +dl_datalistmodel.property.dl_eventAgendaAudience.title=\u0410\u0443\u0434\u0438\u0442\u043e\u0440\u0438\u044f +dl_datalistmodel.property.dl_eventAgendaNotes.title=\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u044f + +# List constraint display labels +listconstraint.dl_task_status.Not\ Started=\u041d\u0435 \u043d\u0430\u0447\u0430\u0442\u043e +listconstraint.dl_task_status.In\ Progress=\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 +listconstraint.dl_task_status.On\ Hold=\u0417\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0430 +listconstraint.dl_task_status.Complete=\u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043e +listconstraint.dl_priority_value.High=\u0412\u044b\u0441\u043e\u043a\u0438\u0439 +listconstraint.dl_priority_value.Normal=\u0421\u0440\u0435\u0434\u043d\u0438\u0439 +listconstraint.dl_priority_value.Low=\u041d\u0438\u0437\u043a\u0438\u0439 + diff --git a/amps/share-services/src/main/resources/alfresco/messages/data-list-model_zh_CN.properties b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_zh_CN.properties new file mode 100755 index 0000000000..c1ec8260af --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/messages/data-list-model_zh_CN.properties @@ -0,0 +1,114 @@ +# Display labels for Share Data Lists Model +dl_datalistmodel.description=Alfresco Share \u6570\u636e\u5217\u8868\u6a21\u578b + +dl_datalistmodel.type.dl_dataList.title=\u6570\u636e\u5217\u8868\u6587\u4ef6\u5939\u7c7b\u578b +dl_datalistmodel.type.dl_dataList.description=\u7528\u4e8e\u5b58\u50a8 dl:dataListItemType \u5c5e\u6027\u4e2d\u6307\u5b9a\u7c7b\u578b\u7684\u6570\u636e\u5217\u8868\u9879\u3002 +dl_datalistmodel.property.dl_dataListItemType.title=\u6570\u636e\u5217\u8868\u9879\u7c7b\u578b +dl_datalistmodel.property.dl_dataListItemType.description=\u7528\u4e8e\u786e\u5b9a\u5728\u6570\u636e\u5217\u8868\u5185\u65b0\u5efa\u9879\u65f6\u5c06\u4f7f\u7528\u7684\u54ea\u79cd dl:dataListItem \u7684\u5b50\u7c7b\u578b\u3002 + +dl_datalistmodel.type.dl_dataListItem.title=\u6570\u636e\u5217\u8868\u7236\u7c7b\u578b +dl_datalistmodel.type.dl_dataListItem.description=\u6d3e\u751f\u6837\u672c\u6570\u636e\u5217\u8868\u9879\u7c7b\u578b\u7684\u6765\u6e90\u7236\u7c7b\u578b\u3002 + +## Simple "To do" List +dl_datalistmodel.type.dl_todoList.title=\u4ee3\u529e\u4e8b\u5b9c\u5217\u8868 +dl_datalistmodel.type.dl_todoList.description=\u4e00\u4e2a\u7b80\u5355\u7684\u4ee3\u529e\u4e8b\u5b9c\u5217\u8868\uff0c\u5176\u4e2d\u5305\u542b\u53ef\u9009\u7684\u88ab\u6307\u6d3e\u8005\u3002 +dl_datalistmodel.property.dl_todoTitle.title=\u6807\u9898 +dl_datalistmodel.property.dl_todoDueDate.title=\u622a\u6b62\u65e5\u671f +dl_datalistmodel.property.dl_todoPriority.title=\u4f18\u5148\u7ea7 +dl_datalistmodel.property.dl_todoStatus.title=\u72b6\u6001 +dl_datalistmodel.property.dl_todoNotes.title=\u8bf4\u660e +dl_datalistmodel.association.dl_assignee.title=\u88ab\u6307\u6d3e\u8005 +dl_datalistmodel.association.dl_attachments.title=\u9644\u4ef6 + +# Gantt Aspect +dl_datalistmodel.property.dl_ganttStartDate.title=\u5f00\u59cb\u65e5\u671f +dl_datalistmodel.property.dl_ganttEndDate.title=\u7ed3\u675f\u65e5\u671f +dl_datalistmodel.property.dl_ganttPercentComplete.title=\u5b8c\u6210\u767e\u5206\u6bd4 + +# Task List (Advanced) +dl_datalistmodel.type.dl_task.title=\u4efb\u52a1\u5217\u8868\uff08\u9ad8\u7ea7\uff09 +dl_datalistmodel.type.dl_task.description=\u9ad8\u7ea7\u4efb\u52a1\u5217\u8868\uff0c\u5176\u4e2d\u5305\u542b\u6807\u9898\u3001\u8bf4\u660e\u3001\u5f00\u59cb\u65e5\u671f\u4e0e\u7ed3\u675f\u65e5\u671f\u3001\u4f18\u5148\u7ea7\u3001\u72b6\u6001\u3001\u6ce8\u91ca\u3001\u88ab\u6307\u6d3e\u8005\u4ee5\u53ca\u9644\u4ef6\u3002 +dl_datalistmodel.property.dl_taskPriority.title=\u4f18\u5148\u7ea7 +dl_datalistmodel.property.dl_taskStatus.title=\u72b6\u6001 +dl_datalistmodel.property.dl_taskComments.title=\u8bc4\u8bba +dl_datalistmodel.association.dl_taskAssignee.title=\u88ab\u6307\u6d3e\u8005 + +# Task List (Simple) +dl_datalistmodel.type.dl_simpletask.title=\u4efb\u52a1\u5217\u8868\uff08\u7b80\u5355\uff09 +dl_datalistmodel.type.dl_simpletask.description=\u7b80\u5355\u4efb\u52a1\u5217\u8868\uff0c\u5176\u4e2d\u5305\u542b\u6807\u9898\u3001\u8bf4\u660e\u3001\u622a\u6b62\u65e5\u671f\u3001\u4f18\u5148\u7ea7\u3001\u72b6\u6001\u4ee5\u53ca\u6ce8\u91ca\u3002 +dl_datalistmodel.property.dl_simpletaskDueDate.title=\u622a\u6b62\u65e5\u671f +dl_datalistmodel.property.dl_simpletaskPriority.title=\u4f18\u5148\u7ea7 +dl_datalistmodel.property.dl_simpletaskStatus.title=\u72b6\u6001 +dl_datalistmodel.property.dl_simpletaskComments.title=\u8bc4\u8bba + +# Contact +dl_datalistmodel.type.dl_contact.title=\u8054\u7cfb\u4eba\u5217\u8868 +dl_datalistmodel.type.dl_contact.description=\u8054\u7cfb\u4eba\u5217\u8868\uff0c\u5176\u4e2d\u5305\u542b\u540d\u5b57\u3001\u59d3\u6c0f\u3001\u5168\u540d\u3001\u7535\u5b50\u90ae\u4ef6\u3001\u804c\u4f4d\u3001\u7535\u8bdd\u53f7\u7801\uff08\u529e\u516c\u5ba4\u5ea7\u673a\uff09\u4ee5\u53ca\u7535\u8bdd\u53f7\u7801\uff08\u79fb\u52a8\u7535\u8bdd\uff09\u3002 +dl_datalistmodel.property.dl_contactFirstName.title=\u540d\u5b57 +dl_datalistmodel.property.dl_contactLastName.title=\u59d3\u6c0f +dl_datalistmodel.property.dl_contactEmail.title=\u7535\u5b50\u90ae\u4ef6 +dl_datalistmodel.property.dl_contactCompany.title=\u516c\u53f8 +dl_datalistmodel.property.dl_contactJobTitle.title=\u804c\u4f4d +dl_datalistmodel.property.dl_contactPhoneOffice.title=\u7535\u8bdd\u53f7\u7801\uff08\u529e\u516c\u5ba4\u5ea7\u673a\uff09 +dl_datalistmodel.property.dl_contactPhoneMobile.title=\u7535\u8bdd\u53f7\u7801\uff08\u79fb\u52a8\u7535\u8bdd\uff09 +dl_datalistmodel.property.dl_contactNotes.title=\u8bf4\u660e + +# Issues +dl_datalistmodel.type.dl_issue.title=\u95ee\u9898\u5217\u8868 +dl_datalistmodel.type.dl_issue.description=\u95ee\u9898\u5217\u8868\uff0c\u5176\u4e2d\u5305\u542b ID\u3001\u72b6\u6001\u3001\u4f18\u5148\u7ea7\u3001\u8bf4\u660e\u3001\u622a\u6b62\u65e5\u671f\u3001\u6ce8\u91ca\u3001\u6307\u6d3e\u5bf9\u8c61\u4ee5\u53ca\u76f8\u5173\u95ee\u9898\u3002 +dl_datalistmodel.property.dl_issueID.title=\u95ee\u9898 ID +dl_datalistmodel.property.dl_issueStatus.title=\u72b6\u6001 +dl_datalistmodel.property.dl_issuePriority.title=\u4f18\u5148\u7ea7 +dl_datalistmodel.property.dl_issueDescription.title=\u8bf4\u660e +dl_datalistmodel.property.dl_issueDueDate.title=\u622a\u6b62\u65e5\u671f +dl_datalistmodel.property.dl_issueComments.title=\u8bc4\u8bba +dl_datalistmodel.association.dl_issueAssignedTo.title=\u88ab\u6307\u6d3e\u7ed9 +dl_datalistmodel.property.dl_issueRelatedIssues.title=\u76f8\u5173\u95ee\u9898 + +# Event +dl_datalistmodel.type.dl_event.title=\u4e8b\u4ef6\u5217\u8868 +dl_datalistmodel.type.dl_event.description=\u4e8b\u4ef6\u5217\u8868\uff0c\u5176\u4e2d\u5305\u542b\u6807\u9898\u3001\u8bf4\u660e\u3001\u4f4d\u7f6e\uff0c\u4ee5\u53ca\u5f00\u59cb\u548c\u7ed3\u675f\u65e5\u671f/\u65f6\u95f4\u3002 +dl_datalistmodel.property.dl_eventLocation.title=\u4f4d\u7f6e +dl_datalistmodel.property.dl_eventStartDate.title=\u5f00\u59cb\u65e5\u671f +dl_datalistmodel.property.dl_eventEndDate.title=\u7ed3\u675f\u65e5\u671f +dl_datalistmodel.property.dl_eventRegistrations.title=\u6ce8\u518c +dl_datalistmodel.property.dl_eventNote.title=\u8bf4\u660e + +# Location +dl_datalistmodel.type.dl_location.title=\u4f4d\u7f6e\u5217\u8868 +dl_datalistmodel.type.dl_location.description=\u4f4d\u7f6e/\u5730\u5740\u5217\u8868 +dl_datalistmodel.property.dl_locationAddress1.title=\u5730\u5740\u7b2c 1 \u884c +dl_datalistmodel.property.dl_locationAddress2.title=\u5730\u5740\u7b2c 2 \u884c +dl_datalistmodel.property.dl_locationAddress3.title=\u5730\u5740\u7b2c 3 \u884c +dl_datalistmodel.property.dl_locationZip.title=\u90ae\u653f\u7f16\u7801 +dl_datalistmodel.property.dl_locationState.title=\u7701/\u76f4\u8f96\u5e02/\u81ea\u6cbb\u533a +dl_datalistmodel.property.dl_locationCountry.title=\u56fd\u5bb6/\u5730\u533a +dl_datalistmodel.property.dl_locationNote.title=\u8bf4\u660e + +# Meeting Agenda +dl_datalistmodel.type.dl_meetingAgenda.title=\u4f1a\u8bae\u8bae\u7a0b +dl_datalistmodel.type.dl_meetingAgenda.description=\u7ba1\u7406\u4f1a\u8bae\u8bae\u7a0b\u9879\uff0c\u5176\u4e2d\u5305\u62ec\u8bf4\u660e\u3001\u6240\u6709\u8005\uff0c\u4ee5\u53ca\u5206\u914d\u7684\u65f6\u95f4\u3002 +dl_datalistmodel.property.dl_meetingAgendaRef.title=\u5f15\u7528 +dl_datalistmodel.property.dl_meetingAgendaTime.title=\u65f6\u95f4\uff08\u5206\u949f\uff09 +dl_datalistmodel.property.dl_meetingAgendaOwner.title=\u6240\u6709\u8005 + +# Event Agenda +dl_datalistmodel.type.dl_eventAgenda.title=\u4e8b\u4ef6\u65e5\u7a0b +dl_datalistmodel.type.dl_eventAgenda.description=\u7ba1\u7406\u4e8b\u4ef6\u65e5\u7a0b\uff0c\u5305\u62ec\u4f1a\u8bdd\u540d\u79f0\u3001\u6f14\u793a\u8005\uff0c\u4ee5\u53ca\u5f00\u59cb\u548c\u7ed3\u675f\u65f6\u95f4\u3002 +dl_datalistmodel.property.dl_eventAgendaRef.title=\u5f15\u7528 +dl_datalistmodel.property.dl_eventAgendaStartTime.title=\u5f00\u59cb\u65f6\u95f4 +dl_datalistmodel.property.dl_eventAgendaEndTime.title=\u7ed3\u675f\u65f6\u95f4 +dl_datalistmodel.property.dl_eventAgendaSessionName.title=\u4f1a\u8bdd\u540d\u79f0 +dl_datalistmodel.property.dl_eventAgendaPresenter.title=\u6f14\u793a\u8005 +dl_datalistmodel.property.dl_eventAgendaAudience.title=\u89c2\u4f17 +dl_datalistmodel.property.dl_eventAgendaNotes.title=\u8bf4\u660e + +# List constraint display labels +listconstraint.dl_task_status.Not\ Started=\u672a\u5f00\u59cb +listconstraint.dl_task_status.In\ Progress=\u6b63\u5728\u8fdb\u884c +listconstraint.dl_task_status.On\ Hold=\u4fdd\u5b58\u4e2d +listconstraint.dl_task_status.Complete=\u5b8c\u6210 +listconstraint.dl_priority_value.High=\u9ad8 +listconstraint.dl_priority_value.Normal=\u6b63\u5e38 +listconstraint.dl_priority_value.Low=\u4f4e + diff --git a/amps/share-services/src/main/resources/alfresco/model/datalistModel.xml b/amps/share-services/src/main/resources/alfresco/model/datalistModel.xml new file mode 100644 index 0000000000..f7a2a8ff7f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/model/datalistModel.xml @@ -0,0 +1,494 @@ + + + + + + + + + Alfresco Share Data List Model + Mike Hatfield + 0.1 + + + + + + + + + + + + + + + + + + + + + Not Started + In Progress + Complete + On Hold + + + + + + + High + Normal + Low + + + + + + 0 + + + 100 + + + + + + + + + Data List container type + cm:folder + + + List Item Type + Determines which Data Dictionary type will be used when create new items within the Data List. + d:text + + + + + + + Data List parent type + cm:content + + + + + To Do List + dl:dataListItem + + + Title + d:text + true + + + Due Date + d:datetime + false + + + Priority + d:int + false + + + Status + d:text + Not Started + + + + + + Notes + d:text + false + + + + + Assignee + + false + true + + + cm:person + false + false + + + + Attachments + + false + true + + + cm:cmobject + false + true + + + + + + + + Task List (Simple) + dl:dataListItem + + + Due Date + d:date + false + + + Priority + d:text + Normal + + + + + + Status + d:text + Not Started + + + + + + Comments + d:text + false + + + + cm:titled + + + + + + Task List (Advanced) + dl:dataListItem + + + Priority + d:text + Normal + + + + + + Status + d:text + Not Started + + + + + + Comments + d:text + false + + + + + Assignee + + false + true + + + cm:person + false + true + + + + + cm:titled + cm:attachable + dl:gantt + + + + + + Contacts List + dl:dataListItem + + + First Name + d:text + false + + + Last Name + d:text + false + + + Email Address + d:text + false + + + Company + d:text + false + + + Job Title + d:text + false + + + Phone (Office) + d:text + false + + + Phone (Mobile) + d:text + false + + + Notes + d:text + false + + + + + + + Issues List + dl:dataListItem + + + Issue ID + d:text + + + Status + d:text + Not Started + + + + + + Priority + d:text + Normal + + + + + + Due Date + d:date + false + + + Comments + d:text + false + + + + + Assigned To + + false + true + + + cm:person + false + true + + + + + cm:titled + cm:attachable + + + + + + Event + dl:dataListItem + + + Location + d:text + false + + + Notes + d:text + false + + + Start Date + d:datetime + false + + + End Date + d:datetime + false + + + Registrations + d:text + + + + cm:titled + cm:attachable + + + + + + Location + dl:dataListItem + + + Address 1 + d:text + + + Address 2 + d:text + + + Address 3 + d:text + + + Zip/Post Code + d:text + + + State/County + d:text + + + Country + d:text + + + + cm:titled + cm:attachable + + + + + + Meeting Agenda + dl:dataListItem + + + Reference + d:text + + + Time (Mins) + d:text + + + Owner + d:text + + + + cm:titled + cm:attachable + + + + + + Event Agenda + dl:dataListItem + + + Reference + d:text + + + Start Time + d:text + + + End Timie + d:text + + + Session Name + d:text + + + Presenter + d:text + + + Audience + d:text + + + Notes + d:text + + + + cm:attachable + + + + + + + + + Gantt + + + Start Date + d:date + + + End Date + d:date + + + % Complete + d:int + true + 0 + + + + + + + + + + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.get.desc.xml new file mode 100644 index 0000000000..44c132bf23 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.get.desc.xml @@ -0,0 +1,9 @@ + + Tagging Actions + Add and remove tags to nodes + /collaboration/tagActions + + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.get.html.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.get.html.ftl new file mode 100644 index 0000000000..7ff46f8e78 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.get.html.ftl @@ -0,0 +1,52 @@ + + + + Tagging Test UI + + + +

Tagging Test UI

+
+ +
+ Space nodeRef: +
e.g. "e3741425-35cf-11dc-9762-4b73d0280543"
+
+ +
+ Tag: + +
+ +
+ + +
+ +
+ + + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.post.desc.xml new file mode 100644 index 0000000000..6416523009 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.post.desc.xml @@ -0,0 +1,9 @@ + + Tagging Actions + Add and remove tags to nodes + /collaboration/tagActions + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.post.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.post.js new file mode 100644 index 0000000000..ec5d6d6c40 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.post.js @@ -0,0 +1,150 @@ +model.tagActions = tagActions(args.a, args.n, args.t); + +function tagActions(action, nodeId, tagName) +{ + var resultString = "Action failed"; + var resultCode = false; + var node = null; + var newTag = null; + var newTagNodeRef = ""; + + if ((tagName != "") && (tagName != null)) + { + tagName = tagName.toLowerCase(); + if (action == "add") + { + // Make sure the tag is in the repo + newTag = createTag(tagName); + if (newTag != null) + { + resultString = "Tag added"; + resultCode = true; + newTagNodeRef = newTag.nodeRef.toString(); + } + else + { + resultString = "Tag '" + tagName + "' not indexed"; + } + } + + // Adding/removing the tag to/from a node? + if ((nodeId != "") && (nodeId != null)) + { + var node = search.findNode("workspace://SpacesStore/" + nodeId); + + if (node != null) + { + try + { + var tags; + + if (action == "add") + { + // Must have newTag node + if (newTag != null) + { + resultString = "Already tagged with '" + tagName + "'"; + tags = node.properties["cm:taggable"]; + if (tags == null) + { + tags = new Array(); + } + // Check node doesn't already have this tag + var hasTag = false; + for each (tag in tags) + { + if (tag != null) + { + if (tag.name == tagName) + { + hasTag = true; + break; + } + } + } + if (!hasTag) + { + // Add it to our node + tags.push(newTag); + tagsArray = new Array(); + tagsArray["cm:taggable"] = tags; + node.addAspect("cm:taggable", tagsArray); + + resultString = "Document tagged"; + resultCode = true; + } + } + } + else if (action == "remove") + { + resultString = "Could not remove tag"; + var oldTags = node.properties["cm:taggable"]; + if (oldTags == null) + { + oldTags = new Array(); + } + tags = new Array(); + // Find this tag + for each (tag in oldTags) + { + if (tag != null) + { + if (tag.name != tagName) + { + tags.push(tag); + } + } + } + // Removed tag? + if (oldTags.length > tags.length) + { + tagsArray = new Array(); + tagsArray["cm:taggable"] = tags; + node.addAspect("cm:taggable", tagsArray); + resultString = "Tag removed"; + resultCode = true; + } + else + { + resultString = "Not tagged with '" + tagName + "'"; + } + } + else + { + resultString = "Unknown action"; + } + } + catch(e) + { + resultString = "Action failed due to exception [" + e.toString() + "]"; + } + } + } + } + + var result = + { + "resultString": resultString, + "resultCode": resultCode, + "newTag": newTagNodeRef + }; + return result; +} + +/* + * Create a new tag if the passed-in one doesn't exist + */ +function createTag(tagName) +{ + var existingTags = classification.getRootCategories("cm:taggable"); + for each (existingTag in existingTags) + { + if (existingTag.name == tagName) + { + return existingTag; + } + } + + var tagNode = classification.createRootCategory("cm:taggable", tagName); + return tagNode; +} diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.post.json.ftl new file mode 100644 index 0000000000..ff1785e8a2 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagActions.post.json.ftl @@ -0,0 +1,7 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "statusString": "${tagActions.resultString}", + "statusCode": ${tagActions.resultCode?string}, + "newTag": "${tagActions.newTag?string}" +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagQuery.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagQuery.get.desc.xml new file mode 100644 index 0000000000..2ae903bc01 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagQuery.get.desc.xml @@ -0,0 +1,18 @@ + + Tagging Query + + The following properties may be updated. +
+
nodeRef
nodeRef to anchor tag query from. Defaults to Company Home
+
maxResults
maximum number of results to return. Defaults to all results (limited by Lucene)
+
sortOrder
sort order for results. Possible values are: "name" (default), "count"
+
+ ]]>
+ /collaboration/tagQuery?n={nodeRef?}&m={maxResults?}&s={sortOrder?} + argument + user + required + internal +
diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagQuery.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagQuery.get.js new file mode 100644 index 0000000000..e3de9a5d40 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagQuery.get.js @@ -0,0 +1,173 @@ +function tagQuery() +{ + var rootNode = args.n, + maxResults = args.m, + sortOrder = args.s, + tags = [], + countMin = Number.MAX_VALUE, + countMax = 0; + + /* rootNode input */ + var node = null; + if ((rootNode !== null) && (rootNode !== "")) + { + node = utils.resolveNodeReference(rootNode); + } + if (node === null) + { + node = companyhome; + } + + /* maxResults input */ + if ((maxResults === null) || (maxResults === "")) + { + maxResults = -1; + } + + /* sortOrder input */ + var validSortOrders = + { + "name": true, + "count": true + }; + if (!(sortOrder in validSortOrders)) + { + sortOrder = "name"; + } + + /* Query for tagged node(s) */ + var query = ""; + if (node !== companyhome) + { + query = "PATH:\"" + node.qnamePath; + if (node.isContainer) + { + query += "//*"; + } + query += "\" AND "; + } + query += "ASPECT:\"{http://www.alfresco.org/model/content/1.0}taggable\""; + //MNT-2118 Share inconsistencies when displaying locked files with tags + query += " -ASPECT:\"{http://www.alfresco.org/model/content/1.0}workingcopy\""; + + // MNT-20091 check to prevent cm:taggable with NULL + query += " AND ISNOTNULL:\"{http://www.alfresco.org/model/content/1.0}taggable\""; + + if (search.searchSubsystem.startsWith("solr")) + { + // MNT-11511: use facet search + var queryDef = { + query: query, + language: "lucene", + page: { + // query minimum rows because all usefull info will come with facets + maxItems: 1, + skipCount: 0 + }, + fieldFacets: [ "TAG" ] + }; + var rs = search.queryResultSet(queryDef); + var tagFacets = rs.meta.facets.TAG; + + for(var i=0; i < tagFacets.size(); i++) + { + var tagFacet = tagFacets.get(i); + tag = + { + name: tagFacet.facetValue, + count: tagFacet.hits, + toString: function() + { + return this.name; + } + }; + tags.push(tag); + } + } + else + { + var taggedNodes = search.luceneSearch(query); + + /* Build a hashtable of tags and tag count */ + var tagHash = {}, + count, taggedNode, tag, key; + + for each (taggedNode in taggedNodes) + { + try + { + for each (tag in taggedNode.properties["cm:taggable"]) + { + if (tag !== null) + { + count = tagHash[tag.name]; + tagHash[tag.name] = count ? count+1 : 1; + } + } + } + catch (e) + { + continue; + } + } + + /* Convert the hashtable into an array of objects */ + for (key in tagHash) + { + tag = + { + name: key, + count: tagHash[key], + toString: function() + { + return this.name; + } + }; + tags.push(tag); + } + } + + if (tags.length === 0) + { + countMin = 0; + } + else + { + /* Sort the results by count (descending) */ + tags.sort(sortByCountDesc); + + /* Trim the results to maxResults if specified */ + if (maxResults > -1) + { + tags = tags.slice(0, maxResults); + } + + /* Calculate the min and max tag count values */ + for each (tag in tags) + { + countMin = Math.min(countMin, tag.count); + countMax = Math.max(countMax, tag.count); + } + + if (sortOrder == "name") + { + /* Sort the results by tag name (ascending) */ + tags.sort(); + } + } + + var results = + { + "countMin": countMin, + "countMax": countMax, + "tags": tags + }; + return results; +} + +function sortByCountDesc(a, b) +{ + return (b.count - a.count); +} + +model.tagQuery = tagQuery(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagQuery.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagQuery.get.json.ftl new file mode 100644 index 0000000000..6c946b00fa --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/collaboration/tagQuery.get.json.ftl @@ -0,0 +1,10 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "countMin": ${tagQuery.countMin?c}, + "countMax": ${tagQuery.countMax?c}, + "tags": + [<#list tagQuery.tags as tag> + { "name": "${tag.name}", "count": ${tag.count?c} }<#if tag_has_next>, + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/site/site-share-view-url.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/site/site-share-view-url.get.desc.xml new file mode 100644 index 0000000000..b8bde595f0 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/site/site-share-view-url.get.desc.xml @@ -0,0 +1,9 @@ + + Get the Share URL to View a Site Node + Gets the appropriate Share URL to view a given Site Node within Share + /api/sites/shareUrl + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/site/site-share-view-url.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/site/site-share-view-url.get.json.ftl new file mode 100644 index 0000000000..2c504ff0b2 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/site/site-share-view-url.get.json.ftl @@ -0,0 +1,6 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "site": "${site.getShortName()}", + "url": "${url}" +} + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/activity/create-activity.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/activity/create-activity.post.desc.xml new file mode 100644 index 0000000000..0e9abfa46d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/activity/create-activity.post.desc.xml @@ -0,0 +1,9 @@ + + Create Activity + Share Generic Component - create activity data webscript + /slingshot/activity/create + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/activity/create-activity.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/activity/create-activity.post.json.ftl new file mode 100644 index 0000000000..e37684e04d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/activity/create-activity.post.json.ftl @@ -0,0 +1,3 @@ +{ + "success": true +} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/activity/create-activity.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/activity/create-activity.post.json.js new file mode 100644 index 0000000000..005f3ff524 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/activity/create-activity.post.json.js @@ -0,0 +1,184 @@ +/** + * Share Generic Component: post new activity + */ + +var m_node = null, + m_parentNode = null; + +/* Posts to the activities service after a Share action */ +function postActivity() +{ + var data = {}, + type = null, + siteId = null, + title = null, + appTool = null, + nodeRef = null, + parentNodeRef = null; + + /* + * Activity Type + */ + if (json.has("type")) + { + type = json.get("type"); + } + if (type == null || type.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "Activity 'type' parameter missing when posting activity"); + return; + } + + /* + * Site + */ + if (json.has("site")) + { + siteId = json.get("site"); + } + if (siteId == null || siteId.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "'site' parameter missing when posting activity"); + return; + } + // Check site existence + if (siteService.getSite(siteId) == null) + { + status.setCode(status.STATUS_BAD_REQUEST, "'" + siteId + "' is not a valid site"); + return; + } + + /** + * NodeRef & ParentNodeRef properties (must have at least one) + */ + if (json.has("nodeRef")) + { + nodeRef = json.get("nodeRef"); + data.nodeRef = nodeRef; + m_node = search.findNode(nodeRef); + } + if (json.has("parentNodeRef")) + { + parentNodeRef = json.get("parentNodeRef"); + data.parentNodeRef = parentNodeRef; + m_parentNode = search.findNode(parentNodeRef); + } + if (nodeRef == null && parentNodeRef == null) + { + status.setCode(status.STATUS_BAD_REQUEST, "Must specify either 'nodeRef' or 'parentNodeRef' parameter when posting activity"); + return; + } + + /** + * Title property + */ + if (json.has("title")) + { + title = json.get("title"); + data.title = populateTokens(title); + } + if (title == null || title.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "Activity 'title' parameter missing when posting activity"); + return; + } + + /** + * AppTool (optional) + */ + if (json.has("appTool")) + { + appTool = json.get("appTool"); + } + + /** + * Page and page params (optional) + */ + if (json.has("page")) + { + data.page = populateTokens(json.get("page")); + } + + try + { + // Log to activity service + activities.postActivity(type, siteId, appTool, jsonUtils.toJSONString(data)); + } + catch(e) + { + if (logger.isLoggingEnabled()) + { + logger.log(e); + } + } +} + +/** + * Property token substution. + * Simplified version of YAHOO.lang.substitute() + * + * @method populateTokens + * @param s {string} String containing zero or more tokens of the form {token} + *
+ *    {cm:name} Node's cm:name property
+ *    {cm:name parent} Parent node's cm:name property
+ * 
+ */ +function populateTokens(s) +{ + var i, j, k, key, v, n, meta, saved=[], token, + SPACE = ' ', PARENT = 'parent', LBRACE = '{', RBRACE = '}', + dump, objstr; + + for (;;) + { + i = s.lastIndexOf(LBRACE); + if (i < 0) + { + break; + } + j = s.indexOf(RBRACE, i); + if (i + 1 >= j) + { + break; + } + + // Extract key and meta info + token = s.substring(i + 1, j); + key = token; + meta = null; + k = key.indexOf(SPACE); + if (k > -1) + { + meta = key.substring(k + 1).toLowerCase(); + key = key.substring(0, k); + } + + // Lookup the value + n = meta == PARENT ? m_parentNode : m_node; + v = null; + if (n != null) + { + v = n.properties[key]; + } + + if (v == null) + { + // This {block} has no replace string. Save it for later. + v = "~-" + saved.length + "-~"; + saved[saved.length] = token; + } + + s = s.substring(0, i) + v + s.substring(j + 1); + } + + // restore saved {block}s + for (i = saved.length - 1; i >= 0; i = i - 1) + { + s = s.replace(new RegExp("~-" + i + "-~"), "{" + saved[i] + "}", "g"); + } + + return s; +} + +postActivity(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.config.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.config.xml new file mode 100644 index 0000000000..68f431ff29 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.config.xml @@ -0,0 +1,4 @@ + + 350 + 50 + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.desc.xml new file mode 100644 index 0000000000..c778c1b467 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.desc.xml @@ -0,0 +1,9 @@ + + Logo Upload + Upload logo file content + + admin + required + /slingshot/application/uploadlogo + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.html.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.html.ftl new file mode 100644 index 0000000000..08859a3359 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.html.ftl @@ -0,0 +1,15 @@ + + + Upload Logo Success + + +<#if (args.success!"")?matches("^[\\w\\d\\._]+$")> + + + + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.html.status.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.html.status.ftl new file mode 100644 index 0000000000..57aea85cd7 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.html.status.ftl @@ -0,0 +1,19 @@ + + + Upload Logo Failure + + +<#if (args.failure!"")?matches("^[\\w\\d\\._]+$")> + + + + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.js new file mode 100644 index 0000000000..43693e4389 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.js @@ -0,0 +1,80 @@ +/** + * Application Log Upload method + * + * @method POST + * @param filedata {file} + */ + +function main() +{ + try + { + var filename = null; + var content = null; + + // locate file attributes + for each (field in formdata.fields) + { + if (field.name == "filedata" && field.isFile) + { + filename = field.filename; + content = field.content; + break; + } + } + + // ensure all mandatory attributes have been located + if (filename == undefined || content == undefined) + { + status.code = 400; + status.message = msg.get("error.uploadMissing"); + status.redirect = true; + return; + } + + var sitesNode = companyhome.childrenByXPath("st:sites")[0]; + if (sitesNode == null) + { + status.code = 500; + status.message = msg.get("error.sitesFolder"); + status.redirect = true; + return; + } + + var logoConfig = new XML(config.script); + var widthxheight = logoConfig.width + "x" + logoConfig.height; + + var transformationOptions = "-resize " + widthxheight + "> -background none -gravity center"; + + // create the new image node + var nodeName = new Date().getTime() + "_" + filename; + var tmpFolder = sitesNode.createFolder(nodeName + "_tmp"); + logoNode = sitesNode.createNode(nodeName, "cm:content"); + logoNode.properties.content.write(content); + logoNode.properties.content.guessMimetype(filename); + var resizedImage = logoNode.transformImage(logoNode.properties.content.mimetype, transformationOptions, tmpFolder); + logoNode.properties.content.write(resizedImage.properties.content); + // CLOUD-951, no need to delete the resizedImage, as removing the tmpFolder will remove resizedImage too. + tmpFolder.remove(); + logoNode.save(); + + // save ref to be returned + model.logo = logoNode; + model.name = filename; + } + catch (e) + { + var x = e; + status.code = 500; + status.message = msg.get("error.unexpected"); + if (x.message && x.message.indexOf("org.alfresco.service.cmr.usage.ContentQuotaException") == 0) + { + status.code = 413; + status.message = x.message; + } + status.redirect = true; + return; + } +} + +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.json.ftl new file mode 100644 index 0000000000..97e04553c0 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.json.ftl @@ -0,0 +1,12 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "nodeRef": "${logo.nodeRef}", + "fileName": "${name}", + "status": + { + "code": 200, + "name": "OK", + "description" : "File uploaded successfully" + } +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.properties new file mode 100644 index 0000000000..40b09531f7 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post.properties @@ -0,0 +1,3 @@ +error.unexpected=Unexpected error occurred during upload of new content. +error.uploadMissing=Uploaded file cannot be located in request. +error.sitesFolder=Failed to locate Sites folder. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_de.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_de.properties new file mode 100644 index 0000000000..9b41140af5 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_de.properties @@ -0,0 +1,3 @@ +error.unexpected=Unerwarteter Fehler beim Hochladen von neuem Inhalt. +error.uploadMissing=Hochgeladene Datei nicht in Anfrage gefunden. +error.sitesFolder=Sites-Ordner nicht gefunden. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_es.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_es.properties new file mode 100644 index 0000000000..19246a99f9 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_es.properties @@ -0,0 +1,3 @@ +error.unexpected=Se ha producido un error inesperado durante la carga de nuevo contenido. +error.uploadMissing=El fichero cargado no puede localizarse en los datos enviados al servidor. +error.sitesFolder=No se pudo localizar la carpeta Sitios. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_fr.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_fr.properties new file mode 100644 index 0000000000..8cf984804d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_fr.properties @@ -0,0 +1,3 @@ +error.unexpected=Une erreur inattendue s'est produite lors de l'importation de nouveau contenu. +error.uploadMissing=Impossible de trouver dans les donn\u00e9es le fichier import\u00e9. +error.sitesFolder=Impossible de situer le dossier Sites. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_it.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_it.properties new file mode 100644 index 0000000000..f3e4768be9 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_it.properties @@ -0,0 +1,3 @@ +error.unexpected=Si \u00e8 verificato un errore imprevisto durante il caricamento del nuovo contenuto. +error.uploadMissing=Impossibile trovare il file caricato nei dati inviati al server. +error.sitesFolder=Impossibile individuare la cartella Siti. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_ja.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_ja.properties new file mode 100644 index 0000000000..64544e9dd0 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_ja.properties @@ -0,0 +1,3 @@ +error.unexpected=\u65b0\u3057\u3044\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u4e2d\u306b\u60f3\u5b9a\u5916\u306e\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +error.uploadMissing=\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u304c\u30c7\u30fc\u30bf\u306e\u4e2d\u306b\u3042\u308a\u307e\u305b\u3093\u3002 +error.sitesFolder=[\u30b5\u30a4\u30c8] \u30d5\u30a9\u30eb\u30c0\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_nb.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_nb.properties new file mode 100644 index 0000000000..a11b585b70 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_nb.properties @@ -0,0 +1,3 @@ +error.unexpected=Uventet feil oppstod under opplasting av nytt innhold. +error.uploadMissing=Finner ikke opplastede fil fra foresp\u00f8rselen. +error.sitesFolder=Fant ikke stedsmappe. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_nl.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_nl.properties new file mode 100644 index 0000000000..a2fcb8f615 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_nl.properties @@ -0,0 +1,3 @@ +error.unexpected=Er is een onverwachte fout opgetreden bij het uploaden van nieuwe content. +error.uploadMissing=Kan het ge\u00fcploade bestand niet vinden in de aanvraag. +error.sitesFolder=Kan de map Sites niet vinden. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_pt_BR.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_pt_BR.properties new file mode 100644 index 0000000000..1a63e14ca2 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_pt_BR.properties @@ -0,0 +1,3 @@ +error.unexpected=Um erro inesperado ocorreu durante o carregamento do novo conte\u00fado. +error.uploadMissing=O arquivo carregado n\u00e3o pode ser localizado no pedido. +error.sitesFolder=Falha na localiza\u00e7\u00e3o da pasta Locais. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_ru.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_ru.properties new file mode 100644 index 0000000000..f2f5ecfc1b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_ru.properties @@ -0,0 +1,3 @@ +error.unexpected=\u0412 \u0445\u043e\u0434\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430. +error.uploadMissing=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0435. +error.sitesFolder=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442 \u043f\u0430\u043f\u043a\u0443 \u0421\u0430\u0439\u0442\u044b. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_zh_CN.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_zh_CN.properties new file mode 100644 index 0000000000..bd3fb55db8 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/application/logo.post_zh_CN.properties @@ -0,0 +1,3 @@ +error.unexpected=\u4e0a\u4f20\u65b0\u5185\u5bb9\u671f\u95f4\u53d1\u751f\u610f\u5916\u9519\u8bef\u3002 +error.uploadMissing=\u65e0\u6cd5\u5728\u8bf7\u6c42\u6570\u636e\u4e2d\u627e\u5230\u4e0a\u4f20\u7684\u6587\u4ef6\u3002 +error.sitesFolder=\u65e0\u6cd5\u627e\u5230\u7ad9\u70b9\u6587\u4ef6\u5939\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-contents.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-contents.get.desc.xml new file mode 100644 index 0000000000..d9a5178bf0 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-contents.get.desc.xml @@ -0,0 +1,9 @@ + + my-content + Content I'm Editing Dashlet Data Webscript + /slingshot/dashlets/my-contents + + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-contents.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-contents.get.js new file mode 100644 index 0000000000..1336bd5a9a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-contents.get.js @@ -0,0 +1,46 @@ + + +/** + * Fetches all posts of the given blog + */ +function getDraftBlogPostList() +{ + var q = " +TYPE:\"{http://www.alfresco.org/model/content/1.0}content\"" + + " +PATH:\"/app:company_home/st:sites/*/cm:blog/*\"" + + " -ISNOTNULL:\"{http://www.alfresco.org/model/content/1.0}published\"" + + " +@cm\\:creator:\"" + person.properties.userName + '"'; + + nodes = search.luceneSearch(q, '@cm:modified', false, 3); + + return processResults(nodes, 3); +} + +function getWikiPages() +{ + var q = " +TYPE:\"{http://www.alfresco.org/model/content/1.0}content\"" + + " +PATH:\"/app:company_home/st:sites/*/cm:wiki/*\"" + + " +@cm\\:modifier:\"" + person.properties.userName + '"'; + + nodes = search.luceneSearch(q, '@cm:modified', false, 3); + + return processResults(nodes, 3); +} + +function getDiscussions() +{ + var q = " +TYPE:\"{http://www.alfresco.org/model/forum/1.0}post\"" + + " +PATH:\"/app:company_home/st:sites/*/cm:discussions/*/*\"" + + " +@cm\\:creator:\"" + person.properties.userName + '"'; + + // NOTE: pull back all posts as first reply on each post will also find the root post + // the posts will be discarded until the root post for each topic is found. + nodes = search.luceneSearch(q, '@cm:modified', false); + + return processResults(nodes, 3); +} + +model.data = {}; + +model.data.blogPosts = getDraftBlogPostList(); +model.data.wikiPages = getWikiPages(); +model.data.discussions = getDiscussions(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-contents.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-contents.get.json.ftl new file mode 100644 index 0000000000..62463d4db0 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-contents.get.json.ftl @@ -0,0 +1,59 @@ +<#macro dateFormat date>${xmldate(date)} +<#macro renderItem item> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "name":"${item.name}", + "nodeRef": "${item.nodeRef}", + "type": "${item.type}", + "displayName": "${item.displayName!''}", + "description": "${item.description!''}", + "createdOn": "<@dateFormat item.createdOn />", + "createdBy": "${item.createdBy!''}", + "createdByUser": "${item.createdByUser!''}", + "modifiedOn": "<@dateFormat item.modifiedOn />", + "modifiedByUser": "${item.modifiedByUser}", + "modifiedBy": "${item.modifiedBy}", + "size": ${item.size?c}, + "tags": [<#list item.tags as tag>"${tag}"<#if tag_has_next>,], + <#if item.site??> + "site": + { + "shortName": "${item.site.shortName}", + "title": "${item.site.title}" + }, + "container": "${item.container}" + +} + + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "blogPosts": + { + "items": + [ + <#list data.blogPosts.items as item> + <@renderItem item /><#if item_has_next>, + + ] + }, + "wikiPages": + { + "items": + [ + <#list data.wikiPages.items as item> + <@renderItem item /><#if item_has_next>, + + ] + }, + "forumPosts": + { + "items": + [ + <#list data.discussions.items as item> + <@renderItem item /><#if item_has_next>, + + ] + } +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-tasks.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-tasks.get.desc.xml new file mode 100644 index 0000000000..2f040e0152 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-tasks.get.desc.xml @@ -0,0 +1,9 @@ + + my-tasks + My Tasks Dashlet Data Webscript + /slingshot/dashlets/my-tasks?filter={filter?}&date={date?} + + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-tasks.get.html.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-tasks.get.html.ftl new file mode 100644 index 0000000000..35860a983c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-tasks.get.html.ftl @@ -0,0 +1 @@ +<#include "my-tasks.get.json.ftl"> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-tasks.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-tasks.get.js new file mode 100644 index 0000000000..84d89b326e --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-tasks.get.js @@ -0,0 +1,22 @@ +var today = new Date(); + +var tomorrow = new Date(); +tomorrow.setDate(today.getDate() + 1); + +var lastSunday = new Date(); +lastSunday.setDate(today.getDate() - today.getDay()); + +var sunday = new Date(); +sunday.setDate(lastSunday.getDate() + 7); + +var nextSunday = new Date(); +nextSunday.setDate(sunday.getDate() + 7); + +var future = new Date(); +future.setYear(9999); + +model.tomorrow = tomorrow; +model.lastSunday = lastSunday; +model.sunday = sunday; +model.nextSunday = nextSunday; +model.future = future; \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-tasks.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-tasks.get.json.ftl new file mode 100644 index 0000000000..abaab1f373 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/dashlets/my-tasks.get.json.ftl @@ -0,0 +1,223 @@ +<#assign workingCopyLabel = " " + message("coci_service.working_copy_label")> +<#assign inviteWorkflowDefinitionNames = ["jbpm$imwf:invitation-moderated", "jbpm$inwf:invitation-nominated"]> +<#assign filter = args["filter"]!"all"> +<#-- + Resolve site, container and path +--> +<#macro location doc> + <#assign qnamePaths = doc.qnamePath?split("/")> + <#assign displayPaths = doc.displayPath?split("/") + [""]> + <#if ((qnamePaths?size > 5) && (qnamePaths[2] == "st:sites"))> + "site": "${displayPaths[3]}", + "container": "${displayPaths[4]}", + "path": "<#list displayPaths[5..] as path><#if path_has_next>/${path}" + + +<#-- + Render a task +--> +<#macro dateFormat date>${date?datetime?string("yyyy-MM-dd HH:mm:ss 'GMT'Z '('zzz')'")!""} +<#macro taskDetail task> + { + "id": "${task.id}", + "description": "${(task.description!"")?j_string}", + "dueDate": "<#if task.properties["bpm:dueDate"]?exists><@dateFormat task.properties["bpm:dueDate"] /><#else><@dateFormat future />", + "status": "${task.properties["bpm:status"]}", + "priority": "${task.properties["bpm:priority"]}", + "startDate": "<@dateFormat task.startDate />", + "type": "${task.type}", + "completeness": "${task.properties["bpm:percentComplete"]}", + "resources": + [ +<#list task.packageResources as resource> + { + "nodeRef": "${resource.nodeRef}", + "fileName": "${resource.name}", + "displayName": "${resource.name?replace(workingCopyLabel, "")}", + "location": + { + <@location resource /> + }, + "icon": "${resource.icon16}" + }<#if resource_has_next>, + + ], + "transitions": + [ +<#list task.transitions as transition> + { + "id": "${transition.id!""}", + "label": "${transition.label!""}" + }<#if transition_has_next>, + + ] + } + + +<#macro invitationModeratedTaskDetail task> + <#assign theSite = site.getSiteInfo(task.properties["imwf:resourceName"])> + <#assign theUser = people.getPerson(task.properties["imwf:inviteeUserName"])> + { + "id": "${task.id}", + "description": "${(task.description!"")?j_string}", + "dueDate": "<#if task.properties["bpm:dueDate"]?exists><@dateFormat task.properties["bpm:dueDate"] /><#else><@dateFormat future />", + "status": "${task.properties["bpm:status"]}", + "priority": "${task.properties["bpm:priority"]}", + "startDate": "<@dateFormat task.startDate />", + "type": "${task.type}", + "completeness": "${task.properties["bpm:percentComplete"]}", + "invitation": + { + "type": "moderated", + "site": + { + "id": "${theSite.shortName}", + "title": "${theSite.title!""}", + "description": "${theSite.description!""}" + }, + "invitee": + { + "fullName": "${(theUser.properties.firstName!"" + " " + theUser.properties.lastName!"")?trim}", + <#if theUser.assocs["cm:avatar"]??> + "avatarRef": "${theUser.assocs["cm:avatar"][0].nodeRef?string}", + + "userName": "${theUser.properties.userName}" + }, + "inviteeRole": "${task.properties["imwf:inviteeRole"]}" + }, + "transitions": + [ +<#list task.transitions as transition> + { + "id": "${transition.id!""}", + "label": "${transition.label!""}" + }<#if transition_has_next>, + + ] + } + + +<#macro invitationNominatedTaskDetail task> + { + "id": "${task.id}", + "description": "${(task.description!"")?j_string}", + "dueDate": "<#if task.properties["bpm:dueDate"]?exists><@dateFormat task.properties["bpm:dueDate"] /><#else><@dateFormat future />", + "status": "${task.properties["bpm:status"]}", + "priority": "${task.properties["bpm:priority"]}", + "startDate": "<@dateFormat task.startDate />", + "type": "${task.type}", + "completeness": "${task.properties["bpm:percentComplete"]}", + <#if task.properties["inwf:resourceName"]?exists> + <#assign theInviter = people.getPerson(task.properties["inwf:inviterUserName"])> + "invitation": + { + "type": "nominated", + "site": + { + "id": "${task.properties["inwf:resourceName"]!""}", + "title": "${jsonUtils.encodeJSONString(task.properties["inwf:resourceTitle"]!"")}", + "description": "${jsonUtils.encodeJSONString(task.properties["inwf:resourceDescription"]!"")}" + }, + "inviter": + { + "fullName": "${(theInviter.properties.firstName!"" + " " + theInviter.properties.lastName!"")?trim}", + <#if theInviter.assocs["cm:avatar"]??> + "avatarRef": "${theInviter.assocs["cm:avatar"][0].nodeRef?string}", + + "userName": "${theInviter.properties.userName}" + }, + "inviteeRole": "${task.properties["inwf:inviteeRole"]}" + }, + + "transitions": + [ +<#list task.transitions as transition> + { + "id": "${transition.id!""}", + "label": "${transition.label!""}" + }<#if transition_has_next>, + + ] + } + + + +<#-- + Filter task list +--> +<#assign filteredTasks = []> +<#assign unfilteredTasks = workflow.assignedTasks + workflow.pooledTasks> +<#list unfilteredTasks as task> + <#assign hasDate = task.properties["bpm:dueDate"]?exists> + <#assign dueDate><#if task.properties["bpm:dueDate"]?exists>${task.properties["bpm:dueDate"]?date!""}<#else>${future?date} + <#switch filter> + <#case "all"> + <#assign filteredTasks = filteredTasks + [task]> + <#break> + + <#case "today"> + <#if (dateCompare(date?date, dueDate?date, 0, "==") == 1)> + <#assign filteredTasks = filteredTasks + [task]> + + <#break> + + <#case "tomorrow"> + <#if (dateCompare(tomorrow?date, dueDate?date, 0, "==") == 1)> + <#assign filteredTasks = filteredTasks + [task]> + + <#break> + + <#case "this-week"> + <#if ((dateCompare(lastSunday?date, dueDate?date) == 0) && (dateCompare(sunday?date, dueDate?date) == 1))> + <#assign filteredTasks = filteredTasks + [task]> + + <#break> + + <#case "next-week"> + <#if ((dateCompare(sunday?date, dueDate?date) == 0) && (dateCompare(nextSunday?date, dueDate?date) == 1))> + <#assign filteredTasks = filteredTasks + [task]> + + <#break> + + <#case "overdue"> + <#if (dateCompare(date?date, dueDate?date) == 1)> + <#assign filteredTasks = filteredTasks + [task]> + + <#break> + + <#case "no-due-date"> + <#if !hasDate> + <#assign filteredTasks = filteredTasks + [task]> + + <#break> + + <#case "invites"> + <#if task.properties["bpm:package"]??> + <#if inviteWorkflowDefinitionNames?seq_contains(task.properties["bpm:package"].properties["bpm:workflowDefinitionName"])> + <#assign filteredTasks = filteredTasks + [task]> + + + <#break> + + + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "tasks": + [ +<#list filteredTasks as task> + <#if task.properties["bpm:package"]??> + <#assign packageNode = task.properties["bpm:package"]> + <#if packageNode.properties["bpm:workflowDefinitionName"] == "jbpm$imwf:invitation-moderated"> + <@invitationModeratedTaskDetail task /> + <#elseif packageNode.properties["bpm:workflowDefinitionName"] == "jbpm$inwf:invitation-nominated"> + <@invitationNominatedTaskDetail task /> + <#else> + <@taskDetail task /> + + + <#if task_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/action.lib.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/action.lib.ftl new file mode 100644 index 0000000000..eaa499cdba --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/action.lib.ftl @@ -0,0 +1,25 @@ +<#macro resultsJSON results> + <#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalResults": ${results?size?c}, + "overallSuccess": ${overallSuccess?string}, + "successCount": ${successCount?c}, + "failureCount": ${failureCount?c}, + "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>, + <#else> + "${key}": "${value}"<#if key_has_next>, + + + }<#if r_has_next>, + + ] +} + + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/action.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/action.lib.js new file mode 100644 index 0000000000..ba2c9e2e1c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/action.lib.js @@ -0,0 +1,158 @@ + + +/** + * 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 . + */ + +/** + * 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 = ParseArgs.resolveNode(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; + } + } +} + +/** + * 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); +} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/duplicate.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/duplicate.post.desc.xml new file mode 100644 index 0000000000..080eeac44a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/duplicate.post.desc.xml @@ -0,0 +1,9 @@ + + duplicate + Data List Action - Duplicate single or multiple items + /slingshot/datalists/action/duplicate/node/{store_type}/{store_id}/{id} + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/duplicate.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/duplicate.post.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/duplicate.post.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/duplicate.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/duplicate.post.json.js new file mode 100644 index 0000000000..811c71e60c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/duplicate.post.json.js @@ -0,0 +1,124 @@ + + +/** + * 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 . + */ + +/** + * 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 = [], + parentNode = p_params.rootNode, + items = p_params.items, + index, itemNode, result, nodeRef; + + // Must have parent node and array of items + if (!parentNode) + { + status.setCode(status.STATUS_BAD_REQUEST, "No parent node supplied on URL."); + return; + } + if (!items || items.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "No items supplied in JSON body."); + return; + } + + // Properties to skip when duplicating + var propertiesToSkip = + { + "cm:name": true, + "cm:content": true, + "cm:created": true, + "cm:creator": true, + "cm:modified": true, + "cm:modifier": true + }; + + for (index in items) + { + nodeRef = items[index]; + result = + { + nodeRef: nodeRef, + action: "duplicateItem", + success: false + }; + + try + { + itemNode = search.findNode(nodeRef); + if (itemNode !== null) + { + var duplicateProperties = new Array(), + propNames = itemNode.getPropertyNames(true), + propName; + + // Copy selected properties from the original node + for (var i = 0, ii = propNames.length; i < ii; i++) + { + propName = propNames[i]; + if (propName in propertiesToSkip || propName.indexOf("sys:") == 0) + { + continue; + } + duplicateProperties[propName] = itemNode.properties[propName]; + } + + // Duplicate the node with a new GUID cm:name + var newNode = parentNode.createNode(null, itemNode.type, duplicateProperties); + if (newNode !== null) + { + // Now copy any associations + for (var idxAssoc in itemNode.assocs) + { + var assocs = itemNode.assocs[idxAssoc]; + for (var j = 0, jj = assocs.length; j < jj; j++) + { + newNode.createAssociation(assocs[j], idxAssoc); + } + } + result.nodeRef = newNode.nodeRef.toString(); + result.success = true; + } + } + } + catch (e) + { + result.success = false; + } + + results.push(result); + } + + return results; +} + +/* Bootstrap action script */ +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/items.delete.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/items.delete.desc.xml new file mode 100644 index 0000000000..2aa6054d66 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/items.delete.desc.xml @@ -0,0 +1,9 @@ + + files + Data List Action - Delete single or multiple items + /slingshot/datalists/action/items + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/items.delete.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/items.delete.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/items.delete.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/items.delete.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/items.delete.json.js new file mode 100644 index 0000000000..c31394199d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/action/items.delete.json.js @@ -0,0 +1,77 @@ + + +/** + * 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 . + */ + +/** + * 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 supplied in JSON body."); + 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(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/data.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/data.post.desc.xml new file mode 100644 index 0000000000..7e7c793dbd --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/data.post.desc.xml @@ -0,0 +1,10 @@ + + DataLists - Data retrieval + Data Lists Component - retrieve data within a given list + /slingshot/datalists/data/site/{site}/{container}/{list} + /slingshot/datalists/data/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/data.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/data.post.json.ftl new file mode 100644 index 0000000000..95d7957faf --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/data.post.json.ftl @@ -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>, + + + } + } + + } + }, + "items": + [ + <#list data.items as item> + { + <@itemLib.itemJSON item /> + }<#if item_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/data.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/data.post.json.js new file mode 100644 index 0000000000..6d5cef7664 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/data.post.json.js @@ -0,0 +1,125 @@ + + + + +const REQUEST_MAX = 1000; + +/** + * 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 . + */ + +/** + * 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).replaceFirst("_", ":")); + } + } + + // Try to find a filter query based on the passed-in arguments + var filter = parsedArgs.filter, + allNodes = [], node, + items = []; + + if (filter == null || filter.filterId == "all") + { + // Use non-query method + var parentNode = parsedArgs.listNode; + if (parentNode != null) + { + var pagedResult = parentNode.childFileFolders(true, false, Filters.IGNORED_TYPES.concat(Filters.IGNORED_ASPECTS), -1, -1, REQUEST_MAX, "cm:name", true, null); + allNodes = pagedResult.page; + } + } + else + { + 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) + }); + } + } + + if (allNodes.length > 0) + { + for each (node in allNodes) + { + try + { + items.push(Evaluator.run(node, fields)); + } + catch(e) {} + } + } + + return ( + { + fields: fields, + paging: + { + totalRecords: items.length, + startIndex: 0 + }, + parent: + { + node: parsedArgs.listNode, + userAccess: + { + create: parsedArgs.listNode.hasPermission("CreateChildren") + } + }, + items: items + }); +} + +model.data = getData(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/evaluator.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/evaluator.lib.js new file mode 100644 index 0000000000..6747ca4ecd --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/evaluator.lib.js @@ -0,0 +1,285 @@ +/** + * 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 . + */ + +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 + * + * @method decorateFieldData + * @param objData {object} Object literal containing this field's data + * @param node {ScriptNode} The list item node for this field + * @return {Boolean} false to prevent this field being added to the output stream. + */ + decorateFieldData: function Evaluator_decorateFieldData(objData, node) + { + var value = objData.value, + type = objData.type, + obj; + + if (type == "cm:person") + { + obj = Evaluator.getPersonObject(value); + if (obj == null) + { + return false; + } + objData.displayValue = obj.displayName; + objData.metadata = obj.userName; + } + else if (type == "cm:folder") + { + obj = Evaluator.getContentObject(value); + if (obj == null) + { + return false; + } + objData.displayValue = obj.displayPath.substring(companyhome.name.length() + 1); + objData.metadata = "container"; + } + else if (type == "category") + { + var displayValue = "", + categoryNodeRefs = value.split(","); + for each (nodeRef in categoryNodeRefs) + { + if (displayValue !== "") + { + displayValue += ", " + } + displayValue += Evaluator.getContentObject(nodeRef).properties["cm:name"]; + } + objData.displayValue = displayValue; + } + else if (type.indexOf(":") > 0 && node.isSubType("cm:cmobject")) + { + obj = Evaluator.getContentObject(value); + if (obj == null || !obj.hasPermission("Read")) + { + return false; + } + objData.displayValue = obj.properties["cm:name"]; + objData.metadata = obj.isContainer ? "container" : "document"; + } + return true; + }, + + /** + * Translates a List fieldDefinition + * + * @method translateField + * @param objDef {FieldDefinition} objDef + * @param value {String} default value + */ + translateField: function Evaluator_translateField(objDef, value) + { + if (objDef == null || objDef == "") + { + return null; + } + if (objDef.constraints != null) + { + for ( var i=0, len= objDef.constraints.size(); i 0) + { + values = value.split(","); + nodeData[k] = []; + for each (value in values) + { + var objLoop = + { + type: objData.type, + value: value, + displayValue: value + }; + + if (Evaluator.decorateFieldData(objLoop, node)) + { + nodeData[k].push(objLoop); + } + } + } + } + else + { + objData.value = value; + objData.displayValue = isAssoc ? value : Evaluator.translateField(objDefinitions[k],value); + + if (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 + }); + } +}; diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/filters.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/filters.lib.js new file mode 100644 index 0000000000..eac9dacba6 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/filters.lib.js @@ -0,0 +1,169 @@ +/** + * 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 . + */ + +var Filters = +{ + /** + * Types that we want to suppress from the resultset + */ + IGNORED_TYPES: + [ + "cm:systemfolder", + "fm:forums", + "fm:forum", + "fm:topic", + "fm:post" + ], + + /** + * Aspects that we want to suppress from the resultset + */ + IGNORED_ASPECTS: + [ + "cm:workingcopy" + ], + + /** + * 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: "@cm: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:"' + Filters.IGNORED_TYPES.join('" -TYPE:"') + '"' + ' -ASPECT:"' + Filters.IGNORED_ASPECTS.join('" -ASPECT:"') + '"'; + + switch (String(filter.filterId)) + { + case "recentlyAdded": + case "recentlyModified": + case "recentlyCreatedByMe": + case "recentlyModifiedByMe": + var onlySelf = (filter.filterId.indexOf("ByMe")) > 0 ? true : false, + dateField = (filter.filterId.indexOf("Modified") > 0) ? "modified" : "created", + 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:\"folder\""; + + filterParams.sort = [ + { + column: "@cm:" + 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\\:creator:\"" + person.properties.userName + '"'; + filterQuery += " -TYPE:\"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; + } +}; diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/item.lib.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/item.lib.ftl new file mode 100644 index 0000000000..eaecbe4310 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/item.lib.ftl @@ -0,0 +1,81 @@ +<#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>, + "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}], + "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 item.custom??>"custom": <#noescape>${item.custom}, + "actionLabels": + { + <#if item.actionLabels??> + <#list item.actionLabels?keys as actionLabel> + "${actionLabel?string}": "${item.actionLabels[actionLabel]}"<#if actionLabel_has_next>, + + + }, + "itemData": + { + <#list item.nodeData?keys as key> + <#assign itemData = item.nodeData[key]> + "${key}": + <#if itemData?is_sequence> + [ + <#list itemData as data> + <@renderData data /><#if data_has_next>, + + ] + <#else> + <@renderData itemData /> + <#if key_has_next>, + + } + + + +<#macro renderData data> + <#escape x as jsonUtils.encodeJSONString(x)> +{ + <#if data.value?is_boolean> + "value": ${data.value?string}, + <#elseif data.value?is_number> + "value": ${data.value?c}, + <#else> + "value": "${data.value}", + + <#if data.metadata??> + "metadata": "${data.metadata}", + + <#if data.displayValue?is_boolean> + "displayValue": ${data.displayValue?string} + <#elseif data.displayValue?is_number> + "displayValue": ${data.displayValue?c} + <#else> + "displayValue": "${data.displayValue}" + +} + + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/item.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/item.post.desc.xml new file mode 100644 index 0000000000..216fee2fb3 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/item.post.desc.xml @@ -0,0 +1,9 @@ + + DataLists - Single item data retrieval + Data Lists Component - retrieve data for a single item by nodeRef + /slingshot/datalists/item/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/item.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/item.post.json.ftl new file mode 100644 index 0000000000..0ed6ae7c0b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/item.post.json.ftl @@ -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>, + + + } + } + + } + }, + "item": + { + <@itemLib.itemJSON data.item /> + } +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/item.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/item.post.json.js new file mode 100644 index 0000000000..301dad9458 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/item.post.json.js @@ -0,0 +1,80 @@ + + + +/** + * 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 . + */ + +/** + * 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).replaceFirst("_", ":")); + } + } + + // 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 + }); +} + +model.data = getData(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.delete.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.delete.desc.xml new file mode 100644 index 0000000000..4ac3faf877 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.delete.desc.xml @@ -0,0 +1,9 @@ + + DataLists - Delete a Data List + Data Lists Component - delete a Data List + /slingshot/datalists/list/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.delete.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.delete.js new file mode 100644 index 0000000000..aba2a7ce73 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.delete.js @@ -0,0 +1,51 @@ + + +/** + * 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 . + */ + +/** + * Main entry point: Delete a Data List + * + * @method deleteList + */ +function deleteList() +{ + // Use helper function to get the arguments + var parsedArgs = ParseArgs.getParsedArgs(); + if (parsedArgs === null) + { + return; + } + + try + { + if (!parsedArgs.rootNode.remove()) + { + status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Could not delete."); + return; + } + } + catch(e) + { + status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, e.toString()); + return; + } +} + +deleteList(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.delete.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.delete.json.ftl new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.delete.json.ftl @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.desc.xml new file mode 100644 index 0000000000..03894a1a8c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.desc.xml @@ -0,0 +1,11 @@ + + DataLists - Download a Data List + Data Lists Component - Download a Data List + /slingshot/datalists/list/site/{site}/{container}/{list}/ + /slingshot/datalists/list/site/{site}/{container}/{list} + /slingshot/datalists/list/node/{store_type}/{store_id}/{id} + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.xls.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.xls.ftl new file mode 100644 index 0000000000..87cc72dbdb --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.xls.ftl @@ -0,0 +1 @@ +<#-- Must not have a newline or any other contents! -->${writeExcel.write()} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.xlsx.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.xlsx.ftl new file mode 100644 index 0000000000..87cc72dbdb --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.xlsx.ftl @@ -0,0 +1 @@ +<#-- Must not have a newline or any other contents! -->${writeExcel.write()} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.lib.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.lib.ftl new file mode 100644 index 0000000000..51c2e0b2f2 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.lib.ftl @@ -0,0 +1,16 @@ +<#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"]!""}", + "permissions": + { + "edit": ${list.hasPermission("Write")?string}, + "delete": ${list.hasPermission("Delete")?string} + } +} + + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/lists.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/lists.get.desc.xml new file mode 100644 index 0000000000..54adc3ba1c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/lists.get.desc.xml @@ -0,0 +1,11 @@ + + DataLists + Data Lists Component - retrieve data lists within a given site and container, or by container nodeRef + /slingshot/datalists/lists/site/{site}/{container}/ + /slingshot/datalists/lists/site/{site}/{container} + /slingshot/datalists/lists/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/lists.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/lists.get.js new file mode 100644 index 0000000000..b47295f025 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/lists.get.js @@ -0,0 +1,43 @@ + + +/** + * 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 . + */ + +/** + * 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.childAssocs["cm:contains"] || [] + }); +} + +model.datalists = getDataLists(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/lists.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/lists.get.json.ftl new file mode 100644 index 0000000000..3bdef7ad0a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/lists.get.json.ftl @@ -0,0 +1,16 @@ +<#import "list.lib.ftl" as listLib /> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "container": "${datalists.container.nodeRef?string}", + "permissions": + { + "create": ${datalists.container.hasPermission("CreateChildren")?string} + }, + "datalists": + [ + <#list datalists.lists as list> + <@listLib.listJSON list /><#if list_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/parse-args.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/parse-args.lib.js new file mode 100644 index 0000000000..14f9264764 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/parse-args.lib.js @@ -0,0 +1,234 @@ +/** + * 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 . + */ + +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] != "object") + { + var person = people.getPerson(username); + if (person == null) + { + if (username == "System" || username.match("^System@") == "System@") + { + // special case for the System users + person = + { + properties: + { + userName: "System", + firstName: "System", + lastName: "User" + }, + assocs: {} + }; + } + else + { + // missing person - may have been deleted from the database + person = + { + properties: + { + userName: username, + firstName: "", + lastName: "" + }, + 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.resolveNode(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.createAndSaveContainer(containerId, containerType || "cm:folder", "Data Lists"); + if (rootNode === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Data Lists container '" + containerId + "' not found in '" + siteId + "'. (No permission?)"); + return null; + } + } + 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, + filterData: args.filterData + } + } + else if (typeof json !== "undefined" && json.has("filter")) + { + var filterJSON = json.get("filter"); + if (filterJSON != null) + { + filter = + { + filterId: filterJSON.get("filterId"), + filterData: filterJSON.get("filterData") + } + } + else + { + filter = + { + filterId: "all" + } + } + } + + var objRet = + { + rootNode: rootNode, + listNode: listNode, + nodeRef: String(listNode.nodeRef), + filter: filter + }; + + return objRet; + }, + + /** + * Resolve "virtual" nodeRefs into nodes + * + * @method resolveVirtualNodeRef + * @deprecated for ParseArgs.resolveNode + */ + resolveVirtualNodeRef: function ParseArgs_resolveVirtualNodeRef(nodeRef) + { + if (logger.isLoggingEnabled()) + { + logger.log("WARNING: ParseArgs.resolveVirtualNodeRef is deprecated for ParseArgs.resolveNode"); + } + return ParseArgs.resolveNode(nodeRef); + }, + + /** + * Resolve "virtual" nodeRefs, nodeRefs and xpath expressions into nodes + * + * @method resolveNode + * @param reference {string} "virtual" nodeRef, nodeRef or xpath expressions + * @return {ScriptNode|null} Node corresponding to supplied expression. Returns null if node cannot be resolved. + */ + resolveNode: function ParseArgs_resolveNode(reference) + { + return utils.resolveNodeReference(reference); + } +}; diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.get.desc.xml new file mode 100644 index 0000000000..e0d70c61c8 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.get.desc.xml @@ -0,0 +1,12 @@ + + doclist-v2 + Document List v2 Component - doclist data webscript + /slingshot/doclib2/doclist/{type}/site/{site}/{container}/{path} + /slingshot/doclib2/doclist/{type}/site/{site}/{container} + /slingshot/doclib2/doclist/{type}/node/{store_type}/{store_id}/{id}/{path} + /slingshot/doclib2/doclist/{type}/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.get.js new file mode 100644 index 0000000000..cdf5d045fe --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.get.js @@ -0,0 +1,9 @@ + + + + + +/** + * Document List Component: doclist + */ +model.doclist = doclist_main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.get.json.ftl new file mode 100644 index 0000000000..9903a77eec --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.get.json.ftl @@ -0,0 +1,35 @@ +<#import "item.lib.ftl" as itemLib /> +<#assign workingCopyLabel = " " + message("coci_service.working_copy_label")> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalRecords": ${doclist.paging.totalRecords?c}, + <#if doclist.paging.totalRecordsUpper??> + "totalRecordsUpper": ${doclist.paging.totalRecordsUpper?c}, + + "startIndex": ${doclist.paging.startIndex?c}, + "metadata": + { + "repositoryId": "${server.id}", + <#if (doclist.container.nodeRef)??>"container": "${doclist.container.nodeRef}", + <#if (doclist.parent.nodeJSON)??>"parent": <#noescape>${doclist.parent.nodeJSON}, + <#if doclist.customJSON??>"custom": <#noescape>${doclist.customJSON}, + "onlineEditing": ${doclist.onlineEditing?string}, + "itemCounts": + { + "folders": ${(doclist.itemCount.folders!0)?c}, + "documents": ${(doclist.itemCount.documents!0)?c} + }, + "workingCopyLabel": "${workingCopyLabel}" + }, + "items": + [ + <#list doclist.items as item> + { + "node": <#noescape>${item.nodeJSON}, + <#if item.parent??>"parent": <#noescape>${item.parent.nodeJSON}, + <@itemLib.itemJSON item=item /> + }<#if item_has_next>, + + ] +} + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.lib.js new file mode 100644 index 0000000000..e23552cd98 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/doclist.lib.js @@ -0,0 +1,360 @@ +const REQUEST_MAX = 1000; + +/** + * Method that performs the actual loading of the nodes. + * + * Note! + * Will optimize performance by using ScriptNode.childFileFolders for directory listings + * In other words when the "path" filter is used. + * + * @method doclist_getAllNodes + * @param parsedArgs {Object} + * @param filterParams {Object} + * @param query {String} + * @param totalItemCount {int} + * @return {object} Returns the node and corresponding pagination metadata + * { + * allNodes: {Array} + * totalRecords: {int} + * requestTotalCountMax: {int} + * paged: {boolean} + * query: {String} + * } + */ +function doclist_getAllNodes(parsedArgs, filterParams, query, totalItemCount) +{ + var filter = args.filter, + totalRecords = 0, + requestTotalCountMax = 0, + paged = false, + allNodes = []; + if ((filter || "path") == "path") + { + // TODO also add DB filter by "node" (in addition to "path") + var parentNode = parsedArgs.pathNode; + if (parentNode !== null) + { + var skip = -1, + max = -1; + + if (args.size != null) + { + max = args.size; + + if (args.pos > 0) + { + skip = (args.pos - 1) * max; + } + } + + var sortField = (args.sortField == null ? "cm:name" : args.sortField), + sortAsc = (((args.sortAsc == null) || (args.sortAsc == "true")) ? true : false); + + // Get paged set + requestTotalCountMax = skip + REQUEST_MAX; + var pagedResult = parentNode.childFileFolders( + true, true, filterParams.ignoreTypes.concat(filterParams.ignoreAspects), + skip, max, requestTotalCountMax, sortField, sortAsc, ""); + + allNodes = pagedResult.page; + totalRecords = pagedResult.totalResultCountUpper; + paged = true; + } + } + else + { + // Query the nodes - passing in sort and result limit parameters + if (query !== "") + { + allNodes = search.query( + { + query: query, + language: filterParams.language, + page: + { + maxItems: totalItemCount + }, + sort: filterParams.sort, + templates: filterParams.templates, + namespace: (filterParams.namespace ? filterParams.namespace : null) + }); + + totalRecords = allNodes.length; + } + } + return { + allNodes: allNodes, + totalRecords: totalRecords, + requestTotalCountMax: requestTotalCountMax, + paged: paged, + query: query + }; +} + +/** + * Main entry point: Create collection of documents and folders in the given space + * + * @method doclist_main + */ +function doclist_main() +{ + // Use helper function to get the arguments + var parsedArgs = ParseArgs.getParsedArgs(); + if (parsedArgs === null) + { + return; + } + + var filter = args.filter, + items = []; + + // Try to find a filter query based on the passed-in arguments + var allNodes = [], + totalRecords = 0, + requestTotalCountMax = 0, + paged = false, + favourites = Common.getFavourites(), + filterParams = Filters.getFilterParams(filter, parsedArgs, + { + favourites: favourites + }), + query = filterParams.query, + allSites = (parsedArgs.nodeRef == "alfresco://sites/home"); + + if (logger.isLoggingEnabled()) + logger.log("doclist.lib.js - NodeRef: " + parsedArgs.nodeRef + " Query: " + query); + + var totalItemCount = filterParams.limitResults ? parseInt(filterParams.limitResults, 10) : -1; + // For all sites documentLibrary query we pull in all available results and post filter + if (totalItemCount === 0) totalItemCount = -1; + else if (allSites) totalItemCount = (totalItemCount > 0 ? totalItemCount * 10 : 500); + + + var allNodesResult = doclist_getAllNodes(parsedArgs, filterParams, query, totalItemCount); + allNodes = allNodesResult.allNodes; + totalRecords = allNodesResult.totalRecords; + requestTotalCountMax = allNodesResult.requestTotalCountMax; + paged = allNodesResult.paged; + query = allNodesResult.query; + + + if (logger.isLoggingEnabled()) + logger.log("doclist.lib.js - query results: " + allNodes.length); + // Generate the qname path match regex required for all sites 'documentLibrary' results match + var pathRegex; + if (allSites) + { + // escape the forward slash characters in the qname path + // TODO: replace with java.lang.String regex match for performance + var pathMatch = new String(parsedArgs.rootNode.qnamePath).replace(/\//g, '\\/') + "\\/.*\\/cm:documentLibrary\\/.*"; + pathRegex = new RegExp(pathMatch, "gi"); + if (logger.isLoggingEnabled()) + logger.log("doclist.lib.js - will match results using regex: " + pathMatch); + } + + // Ensure folders and folderlinks appear at the top of the list + var folderNodes = [], + documentNodes = []; + + for each (node in allNodes) + { + if (totalItemCount !== 0) + { + try + { + if (!allSites || node.qnamePath.match(pathRegex)) + { + totalItemCount--; + if (node.isContainer || node.isLinkToContainer) + { + folderNodes.push(node); + } + else + { + documentNodes.push(node); + } + } + } + catch (e) + { + // Possibly an old indexed node - ignore it + } + } else break; + } + + // Node type counts + var folderNodesCount = folderNodes.length, + documentNodesCount = documentNodes.length, + nodes; + + if (parsedArgs.type === "documents") + { + nodes = documentNodes; + totalRecords -= folderNodesCount; + } + else if (parsedArgs.type === "folders") + { + nodes = folderNodes; + totalRecords -= documentNodesCount; + } + else + { + // TODO: Sorting with folders at end -- swap order of concat() + nodes = folderNodes.concat(documentNodes); + } + + if (logger.isLoggingEnabled()) + logger.log("doclist.lib.js - totalRecords: " + totalRecords); + + // Pagination + var pageSize = args.size || nodes.length, + pagePos = args.pos || "1", + startIndex = (pagePos - 1) * pageSize; + + if (!paged) + { + // Trim the nodes array down to the page size + nodes = nodes.slice(startIndex, pagePos * pageSize); + } + + // Common or variable parent container? + var parent = null; + + if (!filterParams.variablePath) + { + // Parent node permissions (and Site role if applicable) + parent = Evaluator.run(parsedArgs.pathNode, true); + } + + var thumbnail = null, + locationNode, + item; + + // Loop through and evaluate each node in this result set + for each (node in nodes) + { + // Get evaluated properties. + item = Evaluator.run(node); + if (item !== null && (filter!=="editingMe" && filter!=="editingOthers" || node.getIsLocked() || item.workingCopy.isWorkingCopy ) ) + { + item.isFavourite = (favourites[item.node.nodeRef] === true || (item.node.properties["smf:actualNodeRef"] && favourites[item.node.properties["smf:actualNodeRef"]] === true)); + item.likes = Common.getLikes(node); + + // Does this collection of nodes have potentially differering paths? + if (filterParams.variablePath || item.isLink) + { + locationNode = item.isLink ? item.linkedNode : item.node; + if (locationNode.isTargetDeleted) + { + // take location of the link node + location = + { + site: parsedArgs.location.site, + siteTitle: parsedArgs.location.siteTitle, + sitePreset: parsedArgs.location.sitePreset, + container: parsedArgs.location.container, + containerType: parsedArgs.location.containerType, + path: parsedArgs.location.path, + repoPath: parsedArgs.location.repoPath, + file: "" + }; + } + else + { + // Ensure we have Read permissions on the destination on the link object + if (!locationNode.hasPermission("Read")) + { + --totalRecords; + continue; + } + location = Common.getLocation(locationNode, parsedArgs.libraryRoot); + } + // Parent node + if (node.parent != null && node.parent.isContainer && node.parent.hasPermission("Read")) + { + item.parent = Evaluator.run(node.parent, true); + } + } + else + { + location = + { + site: parsedArgs.location.site, + siteTitle: parsedArgs.location.siteTitle, + sitePreset: parsedArgs.location.sitePreset, + container: parsedArgs.location.container, + containerType: parsedArgs.location.containerType, + path: parsedArgs.location.path, + repoPath: parsedArgs.location.repoPath, + file: node.name + }; + } + + // Resolved location + item.location = location; + + items.push(item); + } + else + { + --totalRecords; + } + } + + // Array Remove - By John Resig (MIT Licensed) + var fnArrayRemove = function fnArrayRemove(array, from, to) + { + var rest = array.slice((to || from) + 1 || array.length); + array.length = from < 0 ? array.length + from : from; + return array.push.apply(array, rest); + }; + + /** + * De-duplicate orignals for any existing working copies. + * This can't be done in evaluator.lib.js as it has no knowledge of the current filter or UI operation. + * Note: This may result in pages containing less than the configured amount of items (50 by default). + */ + for each (item in items) + { + if (item.workingCopy && item.workingCopy.isWorkingCopy) + { + var workingCopySource = String(item.workingCopy.sourceNodeRef); + for (var i = 0, ii = items.length; i < ii; i++) + { + if (String(items[i].node.nodeRef) == workingCopySource) + { + fnArrayRemove(items, i); + --totalRecords; + break; + } + } + } + } + + var paging = + { + totalRecords: totalRecords, + startIndex: startIndex + }; + + if (paged && (totalRecords == requestTotalCountMax)) + { + paging.totalRecordsUpper = requestTotalCountMax; + } + + return ( + { + luceneQuery: query, + paging: paging, + container: parsedArgs.rootNode, + parent: parent, + onlineEditing: utils.moduleInstalled("org.alfresco.module.vti"), + itemCount: + { + folders: folderNodesCount, + documents: documentNodesCount + }, + items: items, + customJSON: slingshotDocLib.getJSON() + }); +} diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/evaluator.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/evaluator.lib.js new file mode 100644 index 0000000000..2da7db7e87 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/evaluator.lib.js @@ -0,0 +1,137 @@ + +var Evaluator = +{ + /** + * Node Type evaluator + */ + getNodeType: function Evaluator_getNodeType(node) + { + var nodeType = "document"; + if (node.isContainer) + { + nodeType = "folder"; + } + else if (node.isLinkToContainer) + { + nodeType = "folderlink"; + } + else if (node.isLinkToDocument) + { + nodeType = "filelink"; + } + return nodeType; + }, + + /** + * Node Evaluator - main entrypoint + */ + run: function Evaluator_run(node, isParent) + { + var nodeType = Evaluator.getNodeType(node), + workingCopy = {}, + activeWorkflows = [], + isLink = false, + linkedNode = null; + + if (!isParent) + { + // Get relevant actions set + switch (nodeType) + { + /** + * SPECIFIC TO: LINK + */ + case "folderlink": + case "filelink": + isLink = true; + + linkedNode = node.properties.destination; + if (linkedNode == null) + { + linkedNode = { isTargetDeleted: true }; + } + break; + + /** + * SPECIFIC TO: DOCUMENTS + */ + case "document": + // Working Copy? + if (node.hasAspect("{http://www.alfresco.org/model/content/1.0}workingcopy")) + { + var wcLink = node.sourceAssocs["cm:workingcopylink"]; + var isWorkingCopy = wcLink != null; + if (isWorkingCopy) + { + var wcNode = wcLink[0]; + workingCopy["isWorkingCopy"] = true; + workingCopy["sourceNodeRef"] = wcNode.nodeRef; + if (wcNode.hasAspect("{http://www.alfresco.org/model/content/1.0}versionable")) + { + workingCopy["workingCopyVersion"] = wcNode.properties["cm:versionLabel"]; + } + } + else + { + logger.error("Node: " + node.nodeRef +" hasn't \"cm:workingcopylink\" association"); + } + } + // Locked? + else if (node.isLocked && !node.hasAspect("trx:transferred") && node.hasAspect("{http://www.alfresco.org/model/content/1.0}checkedOut")) + { + var srcNode = node.assocs["cm:workingcopylink"][0]; + workingCopy["hasWorkingCopy"] = true; + workingCopy["workingCopyNodeRef"] = srcNode.nodeRef; + } + } + + // Part of an active workflow? Guard against stale worklow tasks. + try + { + for each (activeWorkflow in node.activeWorkflows) + { + activeWorkflows.push(activeWorkflow.id); + } + } + catch (e) {} + } + + var nodeJSON = appUtils.toJSON(node, true); + var stripLinkedNodeProperties = (args["stripLinkedNodeProperties"] == "true" ? true : false); + + if (isLink && stripLinkedNodeProperties) { + var nodeJsonObj = jsonUtils.toObject(nodeJSON); + var linkedPropsInclude = ["cm:taggable","cm:categories"]; + var linkedNodePropsCompact = {}; + + for each (var linkedProp in linkedPropsInclude) { + var prop = nodeJsonObj.linkedNode.properties[linkedProp]; + if (prop) { + linkedNodePropsCompact[linkedProp] = prop; + } + } + + nodeJsonObj.linkedNode.properties = linkedNodePropsCompact; + nodeJSON = jsonUtils.toJSONString(nodeJsonObj); + } + + if (node !== null) + { + return( + { + node: node, + nodeJSON: nodeJSON, + type: nodeType, + isLink: isLink, + linkedNode: linkedNode, + activeWorkflows: activeWorkflows, + workingCopy: workingCopy, + workingCopyJSON: jsonUtils.toJSONString(workingCopy) + }); + } + else + { + return null; + } + } +}; diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/filters.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/filters.lib.js new file mode 100644 index 0000000000..8d1c096d50 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/filters.lib.js @@ -0,0 +1,287 @@ +var Filters = +{ + /** + * Type map to filter required types. + * NOTE: "documents" filter also returns folders to show UI hint about hidden folders. + */ + TYPE_MAP: + { + "documents": '+(TYPE:"content" OR TYPE:"app:filelink" OR TYPE:"folder")', + "folders": '+(TYPE:"folder" OR TYPE:"app:folderlink")', + "images": '+@cm\\:content.mimetype:image/*' + }, + + /** + * Types that we want to suppress from the resultset + */ + IGNORED_TYPES: + [ + "cm:systemfolder", + "fm:forums", + "fm:forum", + "fm:topic", + "fm:post" + ], + + /** + * Aspects ignored from the canned query based resultset + */ + IGNORED_ASPECTS: + [ + "cm:checkedOut" + ], + + /** + * Encode a path with ISO9075 encoding + * + * @method iso9075EncodePath + * @param path {string} Path to be encoded + * @return {string} Encoded path + */ + iso9075EncodePath: function Filter_iso9075EncodePath(path) + { + var parts = path.split("/"); + for (var i = 1, ii = parts.length; i < ii; i++) + { + parts[i] = "cm:" + search.ISO9075Encode(parts[i]); + } + return parts.join("/"); + }, + + /** + * Create filter parameters based on input parameters + * + * @method getFilterParams + * @param filter {string} Required filter + * @param parsedArgs {object} Parsed arguments object literal + * @param optional {object} Optional arguments depending on filter type + * @return {object} Object literal containing parameters to be used in Lucene search + */ + getFilterParams: function Filter_getFilterParams(filter, parsedArgs, optional) + { + var filterParams = + { + query: "+PATH:\"" + parsedArgs.pathNode.qnamePath + "/*\"", + limitResults: null, + sort: [ + { + column: "@cm:name", + ascending: true + }], + language: "lucene", + templates: null, + variablePath: true, + ignoreTypes: Filters.IGNORED_TYPES, + ignoreAspects: Filters.IGNORED_ASPECTS + }; + + optional = optional || {}; + + // Sorting parameters specified? + var sortAscending = args.sortAsc, + sortField = args.sortField; + + if (sortAscending == "false") + { + filterParams.sort[0].ascending = false; + } + if (sortField !== null) + { + filterParams.sort[0].column = (sortField.indexOf(":") != -1 ? "@" : "") + sortField; + } + + // Max returned results specified? + var argMax = args.max; + if ((argMax !== null) && !isNaN(argMax)) + { + filterParams.limitResults = argMax; + } + + var favourites = optional.favourites; + if (typeof favourites == "undefined") + { + favourites = []; + } + + // Create query based on passed-in arguments + var filterData = String(args.filterData), + filterQuery = ""; + + // Common types and aspects to filter from the UI - known subtypes of cm:content and cm:folder + var filterQueryDefaults = ' -TYPE:"' + Filters.IGNORED_TYPES.join('" -TYPE:"') + '"'; + + switch (String(filter)) + { + case "all": + filterQuery = "+PATH:\"" + parsedArgs.rootNode.qnamePath + "//*\""; + filterQuery += " +TYPE:\"cm:content\""; + filterQuery += " -ASPECT:\"cm:checkedOut\""; + filterParams.query = filterQuery + filterQueryDefaults; + break; + + case "recentlyAdded": + case "recentlyModified": + case "recentlyCreatedByMe": + case "recentlyModifiedByMe": + var onlySelf = (filter.indexOf("ByMe")) > 0 ? true : false, + dateField = (filter.indexOf("Modified") > 0) ? "modified" : "created", + 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 = this.constructPathQuery(parsedArgs); + filterQuery += " +@cm\\:" + dateField + ":[" + fromQuery + "T00\\:00\\:00.000 TO " + toQuery + "T23\\:59\\:59.999]"; + if (onlySelf) + { + filterQuery += " +@cm\\:" + ownerField + ":\"" + person.properties.userName + '"'; + } + filterQuery += " +TYPE:\"cm:content\""; + + filterParams.sort = [ + { + column: "@cm:" + dateField, + ascending: false + }]; + filterParams.query = filterQuery + filterQueryDefaults; + break; + + case "editingMe": + filterQuery = this.constructPathQuery(parsedArgs); + filterQuery += " +((+@cm\\:workingCopyOwner:\"" + person.properties.userName + '")'; + filterQuery += " OR (+@cm\\:lockOwner:\"" + person.properties.userName + '"'; + filterQuery += " +@cm\\:lockType:\"WRITE_LOCK\"))"; + filterParams.query = filterQuery; + break; + + case "editingOthers": + filterQuery = this.constructPathQuery(parsedArgs); + filterQuery += " +((+ASPECT:\"workingcopy\""; + filterQuery += " -@cm\\:workingCopyOwner:\"" + person.properties.userName + '")'; + filterQuery += " OR (-@cm\\:lockOwner:\"" + person.properties.userName + '"'; + filterQuery += " +@cm\\:lockType:\"WRITE_LOCK\"))"; + filterParams.query = filterQuery; + break; + + case "favourites": + for (var favourite in favourites) + { + if (filterQuery) + { + filterQuery += " OR "; + } + filterQuery += "ID:\"" + favourite + "\""; + } + + if (filterQuery.length !== 0) + { + filterQuery = "+(" + filterQuery + ")"; + // no need to specify path here for all sites - IDs are exact matches + if (parsedArgs.nodeRef != "alfresco://sites/home" && parsedArgs.nodeRef != "alfresco://company/home") + { + filterQuery += ' +PATH:"' + parsedArgs.rootNode.qnamePath + '//*"'; + } + } + else + { + // empty favourites query + filterQuery = "+ID:\"\""; + } + + filterParams.query = filterQuery; + break; + + case "synced": + filterQuery = this.constructPathQuery(parsedArgs); + filterQuery += " +ASPECT:\"sync:syncSetMemberNode\""; + filterParams.query = filterQuery; + break; + + case "syncedErrors": + filterQuery = this.constructPathQuery(parsedArgs); + filterQuery += " +ASPECT:\"sync:failed\""; + filterParams.query = filterQuery; + break; + + case "node": + filterParams.variablePath = false; + filterParams.query = "+ID:\"" + parsedArgs.nodeRef + "\""; + break; + + case "tag": + // Remove any trailing "/" character + if (filterData.charAt(filterData.length - 1) == "/") + { + filterData = filterData.slice(0, -1); + } + filterQuery = this.constructPathQuery(parsedArgs); + filterParams.query = filterQuery + " +PATH:\"/cm:taggable/cm:" + search.ISO9075Encode(filterData) + "/member\""; + break; + + case "category": + // Remove any trailing "/" character + if (filterData.charAt(filterData.length - 1) == "/") + { + filterData = filterData.slice(0, -1); + } + filterQuery = this.constructPathQuery(parsedArgs); + filterParams.query = filterQuery + " +PATH:\"/cm:generalclassifiable" + Filters.iso9075EncodePath(filterData) + "/member\""; + break; + + case "aspect": + filterQuery = "+PATH:\"" + parsedArgs.rootNode.qnamePath + "//*\""; + filterQuery += "+ASPECT:\"" + args.filterData + "\""; + filterParams.query = filterQuery; + break; + + default: // "path" + filterParams.variablePath = false; + filterQuery = "+PATH:\"" + parsedArgs.pathNode.qnamePath + "/*\""; + filterParams.query = filterQuery + filterQueryDefaults; + break; + } + + // Specialise by passed-in type + if (filterParams.query !== "") + { + filterParams.query += " " + (Filters.TYPE_MAP[parsedArgs.type] || ""); + } + + return filterParams; + }, + + constructPathQuery: function constructPathQuery(parsedArgs) + { + var pathQuery = ""; + if (parsedArgs.libraryRoot != companyhome || parsedArgs.nodeRef != "alfresco://company/home") + { + if (parsedArgs.nodeRef == "alfresco://sites/home") + { + // all sites query - better with //cm:* + pathQuery = '+PATH:"' + parsedArgs.rootNode.qnamePath + '//cm:*"'; + } + else + { + // site specific query - better with //* + pathQuery = '+PATH:"' + parsedArgs.rootNode.qnamePath + '//*"'; + } + } + return pathQuery; + } +}; diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/item.lib.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/item.lib.ftl new file mode 100644 index 0000000000..bb7daf55a7 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/item.lib.ftl @@ -0,0 +1,46 @@ +<#macro itemJSON item> + <#local node = item.node> + <#local version = "1.0"> + <#if node.hasAspect("{http://www.alfresco.org/model/content/1.0}versionable")><#local version = node.properties["cm:versionLabel"]!""> + <#escape x as jsonUtils.encodeJSONString(x)> + "version": "${version}", + "webdavUrl": "${node.webdavUrl}", + <#if item.activeWorkflows?? && (item.activeWorkflows?size > 0)>"activeWorkflows": ${item.activeWorkflows?size?c}, + <#if item.isFavourite??>"isFavourite": ${item.isFavourite?string}, + <#if (item.workingCopyJSON?length > 2)>"workingCopy": <#noescape>${item.workingCopyJSON}, + <#if item.likes??>"likes": + { + "isLiked": ${item.likes.isLiked?string}, + "totalLikes": ${item.likes.totalLikes?c} + }, + "location": + { + "repositoryId": "${(node.properties["trx:repositoryId"])!(server.id)}", + <#if item.location.site??> + "site": + { + "name": "${(item.location.site)!""}", + "title": "${(item.location.siteTitle)!""}", + "preset": "${(item.location.sitePreset)!""}" + }, + + <#if item.location.container??> + "container": + { + "name": "${(item.location.container)!""}", + "type": "${(item.location.containerType)!""}", + "nodeRef": "${(item.location.containerNode.nodeRef)!""}" + }, + + "path": "${(item.location.path)!""}", + "repoPath": "${(item.location.repoPath)!""}", + "file": "${(item.location.file)!""}", + "parent": + { + <#if (item.location.parent.nodeRef)??> + "nodeRef": "${item.location.parent.nodeRef}" + + } + } + + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/mkdir.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/mkdir.post.desc.xml new file mode 100644 index 0000000000..3c570f0cac --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/mkdir.post.desc.xml @@ -0,0 +1,23 @@ + + Create Folder Paths + + Example: +
+      {  
+         "destination": "workspace://SpacesStore/1234567890",
+         "paths": [
+            "/My folder path/here/x/y/z",
+            "/Another folder path/here/a/b",
+            "/Another folder path/here/a/c"
+         ]
+      }
+      
+ ]]>
+ /slingshot/doclib2/mkdir + argument + user + required + internal +
\ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/mkdir.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/mkdir.post.json.ftl new file mode 100644 index 0000000000..66caadd556 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/mkdir.post.json.ftl @@ -0,0 +1,7 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "nodeRefs": [ + <#list nodeRefs as r>"${r}"<#if r_has_next>, + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/mkdir.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/mkdir.post.json.js new file mode 100644 index 0000000000..979cdbb05e --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/mkdir.post.json.js @@ -0,0 +1,51 @@ +/** + * Create folder paths under a node, ignoring existing folders and creating as needed + * Emulating the functionality provided by "mkdir -p" unix command. + * + * @param destination (string) - NodeRef destination for the paths i.e. parent node for all paths + * @param paths (Array) - List of path Strings to be created under the given parent node + * @since 5.2 + */ + +function main() +{ + // get params and check destination nodeRef exists + if (json.has("destination")) + { + var destination = json.get("destination"); + } + if (json.has("paths")) + { + var paths = json.get("paths"); + } + if (!destination || !paths) + { + status.setCode(status.STATUS_BAD_REQUEST, "Required parameters are missing"); + return; + } + var destNode = search.findNode(destination); + if (destNode === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Cannot find specified destination node"); + } + + // process each path and create the folder, store created noderefs + var nodeRefs = []; + + for (var i=0, path; i + node-v2 + Document List v2 Component - node data webscript + /slingshot/doclib2/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.js new file mode 100644 index 0000000000..f87f650899 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.js @@ -0,0 +1,76 @@ + + + +/** + * Main entry point: Return single document or folder given it's nodeRef + * + * @method getDoclist + */ +function getDoclist() +{ + // Use helper function to get the arguments + var parsedArgs = ParseArgs.getParsedArgs(); + if (parsedArgs === null) + { + return; + } + + parsedArgs.pathNode = ParseArgs.resolveNode(parsedArgs.nodeRef); + parsedArgs.location = Common.getLocation(parsedArgs.pathNode, parsedArgs.libraryRoot); + + var favourites = Common.getFavourites(), + node = parsedArgs.pathNode; + + var thumbnail = null, + item = Evaluator.run(node); + + item.isFavourite = (favourites[node.nodeRef] === true); + item.likes = Common.getLikes(node); + item.location = + { + site: parsedArgs.location.site, + siteTitle: parsedArgs.location.siteTitle, + sitePreset: parsedArgs.location.sitePreset, + container: parsedArgs.location.container, + containerType: parsedArgs.location.containerType, + path: parsedArgs.location.path, + repoPath: parsedArgs.location.repoPath, + file: node.name + }; + + item.parent = null; + if (node.parent != null && node.parent.isContainer && node.parent.hasPermission("Read")) + { + item.parent = Evaluator.run(node.parent, true); + } + + // Special case for container and libraryRoot nodes + if ((parsedArgs.location.containerNode && String(parsedArgs.location.containerNode.nodeRef) == String(node.nodeRef)) || + (parsedArgs.libraryRoot && String(parsedArgs.libraryRoot.nodeRef) == String(node.nodeRef))) + { + item.location.file = ""; + } + + var returnObject = { + container: parsedArgs.rootNode, + onlineEditing: utils.moduleInstalled("org.alfresco.module.vti"), + item: item, + customJSON: slingshotDocLib.getJSON() + }; + + // Additionally include the content if requested by request parameter... + if (args["includeContent"] == "true") + { + returnObject.content = item.node.content + } + if (args["includeThumbnails"] == "true") + { + returnObject.thumbnailDefinitions = item.node.thumbnailDefinitions + } + return (returnObject); +} + +/** + * Document List Component: doclist + */ +model.doclist = getDoclist(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.json.ftl new file mode 100644 index 0000000000..7326e90fde --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.json.ftl @@ -0,0 +1,27 @@ +<#import "item.lib.ftl" as itemLib /> +<#assign workingCopyLabel = " " + message("coci_service.working_copy_label")> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "metadata": + { + "repositoryId": "${server.id}", + <#if doclist.parent?? && doclist.parent.nodeJSON??>"parent": <#noescape>${doclist.parent.nodeJSON}, + <#if doclist.customJSON??>"custom": <#noescape>${doclist.customJSON}, + "onlineEditing": ${doclist.onlineEditing?string}, + "workingCopyLabel": "${workingCopyLabel}", + "shareURL": "${site.getShareUrl()}", + "serverURL": "${url.server}" + }, + <#if doclist.content??>"itemContent": "${doclist.content}", + "item": + { + <#if doclist.thumbnailDefinitions??>"thumbnailDefinitions": [<#list doclist.thumbnailDefinitions as thumbnail>"${thumbnail}"<#if thumbnail_has_next>,], + <#if doclist.item??> + <#assign item = doclist.item> + "node": <#noescape>${item.nodeJSON}, + <#if item.parent?? && item.parent.nodeJSON??>"parent": <#noescape>${item.parent.nodeJSON}, + <@itemLib.itemJSON item=item /> + + } +} + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/parse-args.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/parse-args.lib.js new file mode 100644 index 0000000000..8cb9c613c7 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/parse-args.lib.js @@ -0,0 +1,421 @@ +const THUMBNAIL_NAME = "doclib", + TYPE_SITES = "st:sites", + PREF_DOCUMENT_FAVOURITES = "org.alfresco.share.documents.favourites", + PREF_FOLDER_FAVOURITES = "org.alfresco.share.folders.favourites", + LIKES_SCHEME = "likesRatingScheme"; + +var Common = +{ + /** + * Cache for site objects + */ + SiteCache: {}, + + /** + * Gets / caches a site object + * + * @method getSite + * @param siteId {string} Site ID + */ + getSite: function Common_getSite(siteId) + { + if (typeof Common.SiteCache[siteId] != "object") + { + Common.SiteCache[siteId] = siteService.getSite(siteId); + } + return Common.SiteCache[siteId]; + }, + + /** + * Get the user's favourite docs and folders from our slightly eccentric Preferences Service + * + * @method getFavourites + */ + getFavourites: function Common_getFavourites() + { + var prefs = preferenceService.getPreferences(person.properties.userName, PREF_DOCUMENT_FAVOURITES), + favourites = {}, + arrFavs = [], + strFavs, f, ff; + try + { + /** + * Fasten seatbelts... + * An "eval" could be used here, but the Rhino debugger will complain if throws an exception, which gets old very quickly. + * e.g. var strFavs = eval('try{(prefs.' + PREF_DOCUMENT_FAVOURITES + ')}catch(e){}'); + */ + if (prefs && prefs.org && prefs.org.alfresco && prefs.org.alfresco.share && prefs.org.alfresco.share.documents) + { + strFavs = prefs.org.alfresco.share.documents.favourites; + if (typeof strFavs == "string") + { + arrFavs = strFavs.split(","); + for (f = 0, ff = arrFavs.length; f < ff; f++) + { + favourites[arrFavs[f]] = true; + } + } + } + // Same thing but for folders + prefs = preferenceService.getPreferences(person.properties.userName, PREF_FOLDER_FAVOURITES); + if (prefs && prefs.org && prefs.org.alfresco && prefs.org.alfresco.share && prefs.org.alfresco.share.folders) + { + strFavs = prefs.org.alfresco.share.folders.favourites; + if (typeof strFavs == "string") + { + arrFavs = strFavs.split(","); + for (f = 0, ff = arrFavs.length; f < ff; f++) + { + favourites[arrFavs[f]] = true; + } + } + } + } + catch (e) + { + } + + return favourites; + }, + + /** + * Generates a location object literal for a given node. + * Location is Site-relative unless a libraryRoot node is passed in. + * + * @method getLocation + * @param node {ScriptNode} Node to generate location for + * @param libraryRoot {ScriptNode} Optional node to work out relative location from. + * @param parent {ScriptNode} Optional parent to use instead of assuming primary parent path + * @return {object} Location object literal. + */ + getLocation: function Common_getLocation(node, libraryRoot, parent) + { + try + { + var location = null, + siteId = null, + qnamePaths, + displayPaths; + + if (parent) + { + qnamePaths = (parent.qnamePath + "/_").split("/"); + displayPaths = (parent.displayPath + "/" + parent.name).split("/"); + } + else + { + qnamePaths = node.qnamePath.split("/"), + displayPaths = node.displayPath.split("/"); + } + + if (libraryRoot == undefined && qnamePaths[2] != TYPE_SITES) + { + libraryRoot = companyhome; + } + + if (libraryRoot) + { + // Generate the path from the supplied library root + location = + { + site: null, + container: null, + path: "/" + displayPaths.slice(libraryRoot.displayPath.split("/").length + 1, displayPaths.length).join("/"), + file: node.name + }; + } + else if ((qnamePaths.length > 4) && (qnamePaths[2] == TYPE_SITES)) + { + siteId = displayPaths[3]; + var siteNode = siteService.getSiteInfo(siteId), + containerId = qnamePaths[4].substr(3); + + if (siteNode != null) + { + var containerNode = siteNode.getContainer(containerId); + if (containerNode != null) + { + location = + { + site: siteId, + siteNode: siteNode, + siteTitle: siteNode.title, + sitePreset: siteNode.sitePreset, + container: containerId, + containerNode: containerNode, + containerType: containerNode.typeShort, + path: "/" + displayPaths.slice(5, displayPaths.length).join("/"), + file: node.name + }; + } + } + } + + if (location == null) + { + location = + { + site: siteId, + container: null, + path: "/" + displayPaths.slice(2, displayPaths.length).join("/"), + file: node.name + }; + } + // add repository path to response + location.repoPath = "/" + displayPaths.slice(2, displayPaths.length).join("/"); + + return location; + } + catch(e) + { + return null; + } + }, + + /** + * Returns an object literal representing the current "likes" rating for a node + * + * @method getLikes + * @param node {ScriptNode} Node to query + * @return {object} Likes object literal. + */ + getLikes: function Common_getLikes(node) + { + var isLiked = false, + totalLikes = 0; + + try + { + totalLikes = ratingService.getRatingsCount(node, LIKES_SCHEME); + isLiked = totalLikes === 0 ? false : ratingService.getRating(node, LIKES_SCHEME) !== -1; + } + catch (e) {} + + return ( + { + isLiked: isLiked, + totalLikes: totalLikes + }); + } +}; + +var ParseArgs = +{ + /** + * Get and parse arguments + * + * @method getParsedArgs + * @return {array|null} Array containing the validated input parameters + */ + getParsedArgs: function ParseArgs_getParsedArgs(containerType) + { + var type = url.templateArgs.type, + libraryRoot = args.libraryRoot, + rootNode = null, + pathNode = null, + nodeRef = null, + path = "", + location = null; + + // Is this library rooted from a non-site nodeRef? + if (libraryRoot !== null) + { + libraryRoot = ParseArgs.resolveNode(libraryRoot); + } + + 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 = libraryRoot || ParseArgs.resolveNode(nodeRef); + if (rootNode == null) + { + status.setCode(status.STATUS_NOT_FOUND, "Not a valid nodeRef: '" + nodeRef + "'"); + return null; + } + + // Special case: make sure filter picks up correct mode + if (type == null && args.filter == null) + { + args.filter = "node"; + } + } + else + { + /** + * Site and container input + */ + var siteId = url.templateArgs.site, + containerId = url.templateArgs.container, + siteNode = siteService.getSiteInfo(siteId); + + if (siteNode === null) + { + status.setCode(status.STATUS_GONE, "Site not found: '" + siteId + "'"); + return null; + } + + rootNode = siteNode.getContainer(containerId); + if (rootNode === null) + { + rootNode = siteNode.aquireContainer(containerId, containerType || "cm:folder", {"cm:description": "Document Library"}); + if (rootNode === null) + { + status.setCode(status.STATUS_GONE, "Document Library container '" + containerId + "' not found in '" + siteId + "'. (No permission?)"); + return null; + } + } + } + + // Path input? + path = url.templateArgs.path || ""; + pathNode = path.length > 0 ? rootNode.childByNamePath(path) : (pathNode || rootNode); + if (pathNode === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Path not found: '" + path + "'"); + return null; + } + + // Parent location parameter adjustment + var parentNode = null; + if (path.length > 0) + { + var p = path.split("/"); + p.pop(); + parentNode = rootNode.childByNamePath(p.join("/")); + } + location = Common.getLocation(pathNode, libraryRoot, parentNode); + if (location === null) + { + status.setCode(status.STATUS_GONE, "Location is 'null'. (No permission?)"); + return null; + } + // ACE-565 fix. Commenting current line causes the incorrect folder navigation into Document Library + if (path !== "") + { + location.path = ParseArgs.combinePaths(location.path, location.file); + } + if (location.repoPath) + { + location.repoPath = ParseArgs.combinePaths(location.repoPath, location.file); + } + if (args.filter !== "node" && !pathNode.isContainer) + { + location.file = ""; + } + + var objRet = + { + rootNode: rootNode, + pathNode: pathNode, + libraryRoot: libraryRoot, + location: location, + path: path, + nodeRef: nodeRef, + type: type + }; + + // Multiple input files in the JSON body? + var files = ParseArgs.getMultipleInputValues("nodeRefs"); + if (typeof files != "string") + { + objRet.files = files; + } + + return objRet; + }, + + /** + * Resolve "virtual" nodeRefs into nodes + * + * @method resolveVirtualNodeRef + * @deprecated for ParseArgs.resolveNode + */ + resolveVirtualNodeRef: function ParseArgs_resolveVirtualNodeRef(nodeRef) + { + if (logger.isLoggingEnabled()) + { + logger.log("WARNING: ParseArgs.resolveVirtualNodeRef is deprecated for ParseArgs.resolveNode"); + } + return ParseArgs.resolveNode(nodeRef); + }, + + /** + * Resolve "virtual" nodeRefs, nodeRefs and xpath expressions into nodes + * + * @method resolveNode + * @param reference {string} "virtual" nodeRef, nodeRef or xpath expressions + * @return {ScriptNode|null} Node corresponding to supplied expression. Returns null if node cannot be resolved. + */ + resolveNode: function ParseArgs_resolveNode(reference) + { + return utils.resolveNodeReference(reference); + }, + + /** + * Get multiple input files + * + * @method getMultipleInputValues + * @param param {string} Property name containing the files array + * @return {array|string} Array containing the files, or string error + */ + getMultipleInputValues: function ParseArgs_getMultipleInputValues(param) + { + var values = [], + error = null; + + try + { + // Was a JSON parameter list supplied? + if (typeof json == "object") + { + 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); + }, + + /** + * Append multiple parts of a path, ensuring duplicate path separators are removed. + * + * @method combinePaths + * @param path1 {string} First path + * @param path2 {string} Second path + * @param ... + * @param pathN {string} Nth path + * @return {string} A string containing the combined paths + */ + combinePaths: function ParseArgs_combinePaths() + { + var path = "", i, ii; + for (i = 0, ii = arguments.length; i < ii; i++) + { + if (arguments[i] !== null) + { + path += arguments[i] + (arguments[i] !== "/" ? "/" : ""); + } + } + + return path.replace(/\/{2,}/g, "/").replace(/(.)\/$/g, "$1"); + } +}; diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/action.lib.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/action.lib.ftl new file mode 100644 index 0000000000..eaa499cdba --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/action.lib.ftl @@ -0,0 +1,25 @@ +<#macro resultsJSON results> + <#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalResults": ${results?size?c}, + "overallSuccess": ${overallSuccess?string}, + "successCount": ${successCount?c}, + "failureCount": ${failureCount?c}, + "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>, + <#else> + "${key}": "${value}"<#if key_has_next>, + + + }<#if r_has_next>, + + ] +} + + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/action.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/action.lib.js new file mode 100644 index 0000000000..3689ec58c4 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/action.lib.js @@ -0,0 +1,351 @@ + + +/** + * Document List Component: action + * + * For a single-asset action, template paramters address the asset. + * For multi-asset actions, template parameters address the source or destination node, + * and a JSON body addresses the assets involved in the action. + * (note: HTTP DELETE methods must use URI) + * + * @param uri {string} site/{siteId}/{containerId} + * @param uri {string} site/{siteId}/{containerId}/{filepath} + * @param uri {string} node/{store_type}/{store_id}/{id} + * @param uri {string} node/{store_type}/{store_id}/{id}/{filepath} + */ + +/** + * Main script entry point + * @method main + */ +function main() +{ + // Params object contains commonly-used arguments + var params = {}, + files, rootNode; + + if (url.templateArgs.store_type != undefined) + { + params = getNodeRefInputParams(); + } + else if (url.templateArgs.site != undefined) + { + params = getSiteInputParams(); + } + if (typeof params == "string") + { + status.setCode(status.STATUS_BAD_REQUEST, params); + return; + } + + // Resolve parent if available + var parent = getInputParam("parentId"); + if (parent !== null) + { + params.parent = parent; + } + + // Resolve path if available + var path = url.templateArgs.path || ""; + // Fix-up parent path to have no leading or trailing slashes + if (path.length > 0) + { + var aPaths = path.split("/"); + while (aPaths[0] === "") + { + aPaths.shift(); + } + while (aPaths[aPaths.length-1] === "") + { + aPaths.pop(); + } + path = aPaths.join("/"); + } + params.path = path; + params.destNode = getAssetNode(params.rootNode, path); + + // Must have destNode by this point + if (typeof params.destNode == "string") + { + status.setCode(status.STATUS_NOT_FOUND, "Not found: " + url.extension); + return; + } + + // Multiple input files in the JSON body? + files = getMultipleInputValues("nodeRefs"); + if (typeof files != "string") + { + params.files = files; + } + + // 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 !== null) && (results !== undefined)) + { + 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; + } + } +} + + +/** + * Get and check existence of mandatory input parameters (Site-based) + * + * @method getSiteInputParams + * @return {object|string} object literal containing parameters value or string error + */ +function getSiteInputParams() +{ + var params = {}, + error = null, + template = url.template; + + try + { + var siteId, containerId, siteNode, rootNode; + + // Try to get the parameters from the URI + siteId = url.templateArgs.site; + containerId = url.templateArgs.container; + + // SiteId + if (template.indexOf("{site}") != -1) + { + if ((siteId === null) || (siteId.length === 0)) + { + return "'site' parameter is missing."; + } + + // Find the site + siteNode = siteService.getSite(siteId); + if (siteNode === null) + { + return "Site '" + siteId + "' not found."; + } + + // ContainerId + if (template.indexOf("{container}") != -1) + { + if ((containerId === null) || (containerId.length === 0)) + { + return "'container' parameter is missing."; + } + + // Find the component container + var rootNode = siteNode.getContainer(containerId); + if (rootNode === null) + { + rootNode = siteNode.aquireContainer(containerId); + if (rootNode === null) + { + return "Component container '" + containerId + "' not found in '" + siteId + "'."; + } + } + } + + // Populate the return object + params = + { + siteId: siteId, + containerId: containerId, + siteNode: siteNode, + rootNode: rootNode + } + } + } + catch(e) + { + error = e.toString(); + } + + // Return the params object, or the error string if it was set + return (error !== null ? error : params); +} + +/** + * Get and check existence of mandatory input parameters (nodeRef-based) + * + * @method getNodeRefInputParams + * @return {object|string} object literal containing parameters value or string error + */ +function getNodeRefInputParams() +{ + var params = {}, + error = null; + + try + { + // First try to get the parameters from the URI + var storeType = url.templateArgs.store_type, + storeId = url.templateArgs.store_id, + id = url.templateArgs.id; + + var nodeRef = storeType + "://" + storeId + (id == null ? "" : ("/" + id)), + rootNode = ParseArgs.resolveNode(nodeRef); + + if (rootNode === null) + { + return "'" + nodeRef + "' is not a valid nodeRef."; + } + + var rootNodeRef = String(rootNode.nodeRef); + if (rootNodeRef != nodeRef) + { + nodeRef = rootNodeRef; + } + + // Populate the return object + params = + { + nodeRef: nodeRef, + rootNode: rootNode + }; + } + catch(e) + { + error = e.toString(); + } + + // Return the params object, or the error string if it was set + return (error !== null ? error : params); +} + +/** + * Get multiple input values + * + * @method getMultipleInputValues + * @return {array|string} Array containing multiple values, or string error + */ +function getMultipleInputValues(param) +{ + var values = []; + var error = null; + + try + { + // Was a JSON parameter list supplied? + if (typeof json == "object") + { + 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); +} + +function getInputParam(param) +{ + var value = null; + + try + { + if (typeof json == "object") + { + if (!json.isNull(param)) + { + var value = json.get(param); + } + } + } + catch(e) + { + } + + // Return the values array, or the error string if it was set + return value; +} + + +/** + * Obtain the asset node for the given rootNode and filepath + * + * @method getAssetNode + * @param p_rootNode {object} valid repository node + * @param p_assetPath {string} rootNode-relative path to asset + * @return {object|string} valid repository node or string error + */ +function getAssetNode(p_rootNode, p_assetPath) +{ + var assetNode = p_rootNode, + error = null; + + try + { + // make sure asset path is a string + p_assetPath = String(p_assetPath); + + if (p_assetPath && (p_assetPath.length > 0)) + { + assetNode = assetNode.childByNamePath(p_assetPath); + } + else + { + assetNode = p_rootNode; + } + + if (assetNode === null) + { + return "Asset '" + p_assetPath + " not found."; + } + } + catch(e) + { + error = e.toString(); + } + + // Return the node object, or the error string if it was set + return (error !== null ? error : assetNode); +} diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/add-child.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/add-child.post.desc.xml new file mode 100644 index 0000000000..6cf9a0feec --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/add-child.post.desc.xml @@ -0,0 +1,12 @@ + + add-child + Document List Action - Add multiple files as children + /slingshot/doclib/action/add-child/site/{site}/{container}/{path} + /slingshot/doclib/action/add-child/site/{site}/{container} + /slingshot/doclib/action/add-child/node/{store_type}/{store_id}/{id}/{path} + /slingshot/doclib/action/add-child/node/{store_type}/{store_id}/{id} + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/add-child.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/add-child.post.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/add-child.post.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/add-child.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/add-child.post.json.js new file mode 100644 index 0000000000..db1f101a2c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/add-child.post.json.js @@ -0,0 +1,70 @@ + + +/** + * Add multiple files as children action + * @method POST + */ + +/** + * Entrypoint required by action.lib.js + * + * @method runAction + * @param p_params {object} Object literal containing files array + * @return {object|null} object representation of action results + */ +function runAction(p_params) +{ + var results = [], + destNode = p_params.destNode, + files = p_params.files, + file, fileNode, result, nodeRef; + + // Must have array of files + if (!files || files.length == 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "No files."); + return; + } + + for (file in files) + { + nodeRef = files[file]; + result = + { + nodeRef: nodeRef, + action: "addChild", + success: false + } + + try + { + fileNode = search.findNode(nodeRef); + if (fileNode === null) + { + result.id = file; + result.nodeRef = nodeRef; + result.success = false; + } + else + { + result.id = fileNode.name; + result.type = fileNode.isContainer ? "folder" : "document"; + destNode.addNode(fileNode); + result.success = true; + } + } + catch (e) + { + result.id = file; + result.nodeRef = nodeRef; + result.success = false; + } + + results.push(result); + } + + return results; +} + +/* Bootstrap action script */ +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/aspects.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/aspects.post.desc.xml new file mode 100644 index 0000000000..1c6d03608b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/aspects.post.desc.xml @@ -0,0 +1,9 @@ + + aspects + Document List Component - aspects submit + /slingshot/doclib/action/aspects/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/aspects.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/aspects.post.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/aspects.post.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/aspects.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/aspects.post.json.js new file mode 100644 index 0000000000..e888b62ff1 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/aspects.post.json.js @@ -0,0 +1,93 @@ + + +/** + * Add / Remove Aspects action + * @method POST + */ + +function jsonToArray(p_name) +{ + var array = []; + + if (json.has(p_name)) + { + var jsonArray = json.get(p_name); + for (var i = 0, ii = jsonArray.length(); i < ii; i++) + { + array.push(jsonArray.get(i)); + } + } + + return array; +} + + +/** + * Entrypoint required by action.lib.js + * + * @method runAction + * @param p_params {object} Object literal containing files array + * @return {object|null} object representation of action results + */ +function runAction(p_params) +{ + var result, + assetNode = p_params.destNode; + + try + { + result = + { + nodeRef: assetNode.nodeRef.toString(), + action: "manageAspects", + success: false + } + + result.id = assetNode.name; + result.type = assetNode.isContainer ? "folder" : "document"; + + var added = jsonToArray("added"), + removed = jsonToArray("removed"), + isTaggable = false, + i, ii; + + // Aspects to be removed + for (i = 0, ii = removed.length; i < ii; i++) + { + if (assetNode.hasAspect(removed[i])) + { + assetNode.removeAspect(removed[i]); + isTaggable = isTaggable || (removed[i] == "cm:taggable"); + } + } + + // Aspects to be added + for (i = 0, ii = added.length; i < ii; i++) + { + if (!assetNode.hasAspect(added[i])) + { + assetNode.addAspect(added[i]); + isTaggable = isTaggable || (added[i] == "cm:taggable"); + } + } + + // Update the tag scope? This would be nice, but will be quite slow & synchronous. + // We'll send back the flag anyway and the client can fire off a REST API call to do it. + if (isTaggable) + { + // assetNode.tagScope.refresh(); + result.tagScope = true; + } + + result.success = true; + } + catch (e) + { + result.success = false; + } + + return [result]; +} + +/* Bootstrap action script */ +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/cancel-checkout.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/cancel-checkout.post.desc.xml new file mode 100644 index 0000000000..fdd56e6354 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/cancel-checkout.post.desc.xml @@ -0,0 +1,10 @@ + + cancel-checkout + Document List Action - Cancel check-out for file + /slingshot/doclib/action/cancel-checkout/site/{site}/{container}/{path} + /slingshot/doclib/action/cancel-checkout/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/cancel-checkout.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/cancel-checkout.post.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/cancel-checkout.post.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/cancel-checkout.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/cancel-checkout.post.json.js new file mode 100644 index 0000000000..e78f7a740f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/cancel-checkout.post.json.js @@ -0,0 +1,95 @@ + + +/** + * Cancel checkout file action + * @method POST + * @param uri {string} /{siteId}/{containerId}/{filepath} + */ + +/** + * Entrypoint required by action.lib.js + * + * @method runAction + * @param p_params {object} standard action parameters: nodeRef, siteId, containerId, path + * @return {object|null} object representation of action result + */ +function runAction(p_params) +{ + var results, resultId, resultNodeRef; + + try + { + // Initialise to the destNode (will be updated to the original if a working copy)... + var originalDoc = p_params.destNode; + resultId = originalDoc.name; + resultNodeRef = originalDoc.nodeRef.toString(); + + if (p_params.destNode.hasAspect("cm:workingcopy")) + { + // If the node is a working copy then cancel the checkout and set the original... + var checkedoutNode = p_params.destNode.getCheckedOut(); + if (checkedoutNode === null) + { + status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Could not find original node: " + url.extension); + return; + } + + if (checkedoutNode.hasAspect("cm:cmisCreatedCheckedOut")) + { + var parent = checkedoutNode.parent; + resultId = parent.name; + resultNodeRef = parent.nodeRef.toString(); + } + else + { + resultId = checkedoutNode.name; + resultNodeRef = checkedoutNode.nodeRef.toString(); + } + + originalDoc = p_params.destNode.cancelCheckout(); + if (originalDoc === null) + { + status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Could not cancel checkout: " + url.extension); + return; + } + } + else if (p_params.destNode.isLocked && !p_params.destNode.hasAspect("trx:transferred")) + { + var assocs = p_params.destNode.getAssocs(); + if (assocs["{http://www.alfresco.org/model/content/1.0}workingcopylink"] !==null && assocs["{http://www.alfresco.org/model/content/1.0}workingcopylink"][0]) + { + // original document: edit offline case + originalDoc = assocs["{http://www.alfresco.org/model/content/1.0}workingcopylink"][0].cancelCheckout(); + + resultId = originalDoc.name; + resultNodeRef = originalDoc.nodeRef.toString(); + } + else + { + // original document: edit online case + // ...or, if the node is locked then just unlock it... + p_params.destNode.unlock(); + } + } + + // Construct the result object + results = [ + { + id: resultId, + nodeRef: resultNodeRef, + action: "cancelCheckoutAsset", + success: true + }]; + } + catch(e) + { + e.code = status.STATUS_INTERNAL_SERVER_ERROR; + e.message = e.toString(); + throw e; + } + + return results; +} + +/* Bootstrap action script */ +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkin.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkin.post.desc.xml new file mode 100644 index 0000000000..3337af4246 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkin.post.desc.xml @@ -0,0 +1,10 @@ + + checkin + Document List Action - Check-in file + /slingshot/doclib/action/checkin/site/{site}/{container}/{path} + /slingshot/doclib/action/checkin/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkin.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkin.post.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkin.post.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkin.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkin.post.json.js new file mode 100644 index 0000000000..57409a25b6 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkin.post.json.js @@ -0,0 +1,53 @@ + + +/** + * Checkin file action + * @method POST + * @param uri {string} /{siteId}/{containerId}/{filepath} + */ + +/** + * Entrypoint required by action.lib.js + * + * @method runAction + * @param p_params {object} standard action parameters: nodeRef, siteId, containerId, path + * @return {object|null} object representation of action result + */ +function runAction(p_params) +{ + var results; + + try + { + var assetNode = p_params.destNode, + originalDoc = assetNode.checkin(); + if (originalDoc === null) + { + status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Could not checkin: " + p_params.path); + return; + } + + var resultId = originalDoc.name, + resultNodeRef = originalDoc.nodeRef.toString(); + + // Construct the result object + results = [ + { + id: resultId, + nodeRef: resultNodeRef, + action: "checkinAsset", + success: true + }]; + } + catch(e) + { + e.code = status.STATUS_INTERNAL_SERVER_ERROR; + e.message = e.toString(); + throw e; + } + + return results; +} + +/* Bootstrap action script */ +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkout.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkout.post.desc.xml new file mode 100644 index 0000000000..fca4f3a5ba --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkout.post.desc.xml @@ -0,0 +1,10 @@ + + checkout + Document List Action - Check-out file + /slingshot/doclib/action/checkout/site/{site}/{container}/{path} + /slingshot/doclib/action/checkout/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkout.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkout.post.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkout.post.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkout.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkout.post.json.js new file mode 100644 index 0000000000..d3d32f40cc --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/checkout.post.json.js @@ -0,0 +1,66 @@ + + +/** + * Checkout file action + * @method POST + * @param uri {string} /{siteId}/{containerId}/{filepath} + * @param json.destination {string} Optional path to checkout to + */ + +/** + * Entrypoint required by action.lib.js + * + * @method runAction + * @param p_params {object} standard action parameters: nodeRef, siteId, containerId, path + * @return {object|null} object representation of action result + */ +function runAction(p_params) +{ + var results; + + try + { + var assetNode = p_params.destNode; + + // Ensure the file is versionable (autoVersion = true, autoVersionProps = false) + assetNode.ensureVersioningEnabled(true, false); + + // Checkout the asset + var workingCopy = assetNode.checkout(); + if (workingCopy === null) + { + status.setCode(status.STATUS_FORBIDDEN, "Could not checkout: " + p_params.path); + return; + } + + // Extra property to allow the full series of actions via the Explorer client + utils.disableRules(); + workingCopy.properties["cm:workingCopyMode"] = "offlineEditing"; + workingCopy.save(); + utils.enableRules(); + + var resultId = assetNode.name, + resultNodeRef = workingCopy.nodeRef.toString(); + + // Construct the result object + results = [ + { + id: resultId, + nodeRef: resultNodeRef, + downloadUrl: "api/node/content/" + resultNodeRef.replace(":/", "") + "/" + encodeURIComponent(workingCopy.name) + "?a=true", + action: "checkoutAsset", + success: true + }]; + } + catch(e) + { + e.code = status.STATUS_INTERNAL_SERVER_ERROR; + e.message = e.toString(); + throw e; + } + + return results; +} + +/* Bootstrap action script */ +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/copy-to.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/copy-to.post.desc.xml new file mode 100644 index 0000000000..1d487818ff --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/copy-to.post.desc.xml @@ -0,0 +1,13 @@ + + copy-to + Document List Action - Copy multiple files + /slingshot/doclib/action/copy-to/site/{site}/{container}/{path} + /slingshot/doclib/action/copy-to/site/{site}/{container} + /slingshot/doclib/action/copy-to/node/{store_type}/{store_id}/{id}/{path} + /slingshot/doclib/action/copy-to/node/{store_type}/{store_id}/{id} + /slingshot/doclib/action/copy-to/node/{store_type}/{store_id} + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/copy-to.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/copy-to.post.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/copy-to.post.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/copy-to.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/copy-to.post.json.js new file mode 100644 index 0000000000..a91cae1f7a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/copy-to.post.json.js @@ -0,0 +1,94 @@ + + +/** + * Copy multiple files action + * @method POST + */ + +/** + * Entrypoint required by action.lib.js + * + * @method runAction + * @param p_params {object} Object literal containing files array + * @return {object|null} object representation of action results + */ +function runAction(p_params) +{ + var results = [], + destNode = p_params.destNode, + files = p_params.files, + file, fileNode, result, nodeRef, + fromSite, copiedNode; + + // Must have array of files + if (!files || files.length == 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "No files."); + return; + } + + for (file in files) + { + nodeRef = files[file]; + result = + { + nodeRef: nodeRef, + action: "copyFile", + success: false + }; + + try + { + fileNode = search.findNode(nodeRef); + if (fileNode == null) + { + result.id = file; + result.nodeRef = nodeRef; + result.success = false; + } + else + { + result.type = fileNode.isContainer ? "folder" : "document" + + // Retain the name of the site the node is currently in. Null if it's not in a site. + fromSite = fileNode.siteShortName; + + // copy the node (deep copy for containers) + if (fileNode.isContainer) + { + copiedNode = fileNode.copy(destNode, true); + } + else + { + copiedNode = fileNode.copy(destNode); + } + + result.id = copiedNode.name; + result.nodeRef = copiedNode.nodeRef.toString(); + result.success = (result.nodeRef != null); + + if (result.success) + { + // If this was an inter-site copy, we'll need to clean up the permissions on the node + if ((fromSite) && (String(fromSite) != String(copiedNode.siteShortName))) + { + siteService.cleanSitePermissions(copiedNode); + } + } + } + } + catch (e) + { + result.id = file; + result.nodeRef = nodeRef; + result.success = false; + } + + results.push(result); + } + + return results; +} + +/* Bootstrap action script */ +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/file.delete.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/file.delete.desc.xml new file mode 100644 index 0000000000..345b3a412a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/file.delete.desc.xml @@ -0,0 +1,12 @@ + + file + Document List Action - Delete file + /slingshot/doclib/action/file/site/{site}/{container}/{path} + /slingshot/doclib/action/folder/site/{site}/{container}/{path} + /slingshot/doclib/action/file/node/{store_type}/{store_id}/{id} + /slingshot/doclib/action/folder/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/file.delete.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/file.delete.js new file mode 100644 index 0000000000..8a27c60eb8 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/file.delete.js @@ -0,0 +1,52 @@ + + +/** + * Delete file action + * @method DELETE + * @param uri {string} /{siteId}/{containerId}/{filepath} + */ + +/** + * Entrypoint required by action.lib.js + * + * @method runAction + * @param p_params {object} standard action parameters: nodeRef, siteId, containerId, path + * @return {object|null} object representation of action result + */ +function runAction(p_params) +{ + var results; + + try + { + var assetNode = p_params.destNode, + resultId = assetNode.name, + resultNodeRef = assetNode.nodeRef.toString(); + + // Delete the asset + if (!assetNode.remove()) + { + status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Could not delete."); + return; + } + + // Construct the result object + results = [ + { + id: resultId, + nodeRef: resultNodeRef, + action: "deleteFile", + success: true + }]; + } + catch(e) + { + status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, e.toString()); + return; + } + + return results; +} + +/* Bootstrap action script */ +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/file.delete.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/file.delete.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/file.delete.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/files.delete.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/files.delete.desc.xml new file mode 100644 index 0000000000..f154ddb921 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/files.delete.desc.xml @@ -0,0 +1,10 @@ + + files + Document List Action - Delete multiple files + /slingshot/doclib/action/files + /slingshot/doclib/action/folders + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/files.delete.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/files.delete.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/files.delete.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/files.delete.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/files.delete.json.js new file mode 100644 index 0000000000..947d23a8c4 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/files.delete.json.js @@ -0,0 +1,94 @@ + + +/** + * Delete multiple files action + * @method DELETE + */ + +/** + * Entrypoint required by action.lib.js + * + * @method runAction + * @param p_params {object} Object literal containing files array + * @return {object|null} object representation of action results + */ +function runAction(p_params) +{ + var results = [], + files = p_params.files, + linkNodes = [], + nodes = [], + file, nodeRef, node; + + // Must have array of files + if (!files || files.length == 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "No files."); + return; + } + + // Loop through nodes, check the existence of each node and create an array of valid ScriptNodes ready for deletion + for (file in files) + { + try + { + nodeRef = files[file]; + node = search.findNode(nodeRef); + + // Check if the node actually exists, before deleting it + // If it doesn't exist mark the deletion of it as failed + if (node === null) + { + results.push( + { + id: file, + nodeRef: nodeRef, + action: "deleteFile", + success: false + } + ); + continue; + } + else if (node.isLinkToDocument || node.isLinkToContainer) + { + linkNodes.push(node); + } + else + { + nodes.push(node); + } + } + catch (e) + { + results.push( + { + id: file, + nodeRef: nodeRef, + action: "deleteFile", + success: false + } + ); + } + } + + nodes = linkNodes.concat(nodes); + + // Loop through ScriptNodes and delete them, one by one + for (var i = 0; i < nodes.length; i++) + { + results.push( + { + id: nodes[i].name, + nodeRef: nodes[i].nodeRef.toString(), + action: "deleteFile", + type: nodes[i].isContainer ? "folder" : "document", + success: nodes[i].remove(true) + } + ); + } + + return results; +} + +/* Bootstrap action script */ +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/move-to.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/move-to.post.desc.xml new file mode 100644 index 0000000000..37fcbc3fd0 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/move-to.post.desc.xml @@ -0,0 +1,13 @@ + + move-to + Document List Action - Move multiple files + /slingshot/doclib/action/move-to/site/{site}/{container}/{path} + /slingshot/doclib/action/move-to/site/{site}/{container} + /slingshot/doclib/action/move-to/node/{store_type}/{store_id}/{id}/{path} + /slingshot/doclib/action/move-to/node/{store_type}/{store_id}/{id} + /slingshot/doclib/action/move-to/node/{store_type}/{store_id} + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/move-to.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/move-to.post.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/move-to.post.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/move-to.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/move-to.post.json.js new file mode 100644 index 0000000000..0e824c8449 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/move-to.post.json.js @@ -0,0 +1,98 @@ + + +/** + * Move multiple files action + * @method POST + */ + +/** + * Entrypoint required by action.lib.js + * + * @method runAction + * @param p_params {object} Object literal containing files array + * @return {object|null} object representation of action results + */ +function runAction(p_params) +{ + var results = [], + destNode = p_params.destNode, + files = p_params.files, + parent = null, + file, fileNode, result, nodeRef, + fromSite; + + // Must have array of files + if (!files || files.length == 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "No files."); + return; + } + + for (file in files) + { + nodeRef = files[file]; + result = + { + nodeRef: nodeRef, + action: "moveFile", + success: false + } + + try + { + fileNode = search.findNode(nodeRef); + if (fileNode == null) + { + result.id = file; + result.nodeRef = nodeRef; + result.success = false; + } + else + { + if (p_params.parent && p_params.parent != null) + { + parent = search.findNode(p_params.parent); + } + result.id = fileNode.name; + result.type = fileNode.isContainer ? "folder" : "document"; + + // Retain the name of the site the node is currently in. Null if it's not in a site. + fromSite = fileNode.siteShortName; + + // move the node + result.success = fileNode.move(parent, destNode); + + if (result.success) + { + // If this was an inter-site move, we'll need to clean up the permissions on the node + if ((fromSite) && (String(fromSite) !== String(fileNode.siteShortName))) + { + siteService.cleanSitePermissions(fileNode); + } + } + } + } + catch (e) + { + result.id = file; + result.nodeRef = nodeRef; + result.success = false; + + //MNT-7514 Uninformational error message on move when file name conflicts + result.fileExist = false; + + error = e.toString(); + if (error.indexOf("FileExistsException") != -1) + { + result.fileExist = true; + } + } + + results.push(result); + } + + return results; +} + +/* Bootstrap action script */ +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/permissions.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/permissions.post.desc.xml new file mode 100644 index 0000000000..8d6178a712 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/permissions.post.desc.xml @@ -0,0 +1,9 @@ + + permissions + Document List Action - set permissions + /slingshot/doclib/action/permissions/{operation}/site/{site} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/permissions.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/permissions.post.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/permissions.post.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/permissions.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/permissions.post.json.js new file mode 100644 index 0000000000..43077f1ab8 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/permissions.post.json.js @@ -0,0 +1,121 @@ + + +const VALID_OPERATIONS = +{ + "set": true, + "reset-all": true +}; + +/** + * Set Permissions on single/multiple files + * @method POST + */ + +/** + * Entrypoint required by action.lib.js + * + * @method runAction + * @param p_params {object} Object literal containing files array + * @return {object|null} object representation of action results + */ +function runAction(p_params) +{ + var results = [], + files = p_params.files, + i, j, file, fileNode, nodeRef; + + // Must have array of files + if (!files || (files.length == 0)) + { + status.setCode(status.STATUS_BAD_REQUEST, "No files."); + return; + } + + // Which permissions operation? + var operation = url.templateArgs.operation; + if (!operation || !(operation in VALID_OPERATIONS)) + { + status.setCode(status.STATUS_BAD_REQUEST, "Invalid or missing operation ('" + operation + "')."); + return; + } + + // Permissions to set + var jsonPermissions = getMultipleInputValues("permissions"); + + // We need the site node to perform some of the operations + var site = p_params.siteNode; + + // Set permissions on each file + for (file in files) + { + nodeRef = files[file]; + result = + { + nodeRef: nodeRef, + action: operation, + success: false + } + + try + { + fileNode = search.findNode(nodeRef); + if (fileNode === null) + { + result.id = file; + result.nodeRef = nodeRef; + result.success = false; + } + else + { + result.id = fileNode.name; + result.type = fileNode.isContainer ? "folder" : "document"; + + // Execute the operation + switch (operation) + { + case "set": + if (typeof jsonPermissions == "string") + { + status.setCode(status.STATUS_BAD_REQUEST, "Invalid or missing permissions set."); + return; + } + // Convert permissions from JSONArray + var permissions = []; + for (var i = 0, j = jsonPermissions.length; i < j; i++) + { + permissions[jsonPermissions[i].get("group")] = String(jsonPermissions[i].get("role")); + + // It is not allowed for a user to increase access for "All Other Users" on a non-public site. + // This is because the site cannot be accessed until the user has been invited to join the site + if (jsonPermissions[i].get("group") == "GROUP_EVERYONE" && !site.isPublic) + { + result.success = false; + status.setCode(status.STATUS_BAD_REQUEST, "Cannot give permissions to all other users on non-public site"); + return; + } + } + site.setPermissions(fileNode, permissions); + break; + + case "reset-all": + site.resetAllPermissions(fileNode); + break; + } + result.success = true; + } + } + catch (e) + { + result.id = file; + result.nodeRef = nodeRef; + result.success = false; + } + + results.push(result); + } + + return results; +} + +/* Bootstrap action script */ +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.desc.xml new file mode 100644 index 0000000000..0104d5f375 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.desc.xml @@ -0,0 +1,9 @@ + + cancel-checkout + Document List Action - Unlock document + /slingshot/doclib/action/unlock-document/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.json.js new file mode 100644 index 0000000000..c5e909d740 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.json.js @@ -0,0 +1,51 @@ + + +/** + * Cancel checkout file action + * @method POST + * @param uri {string} /{siteId}/{containerId}/{filepath} + */ + +/** + * Entrypoint required by action.lib.js + * + * @method runAction + * @param p_params {object} standard action parameters: nodeRef, siteId, containerId, path + * @return {object|null} object representation of action result + */ +function runAction(p_params) +{ + var results; + + try + { + var originalDoc = p_params.destNode; + if (p_params.destNode.hasAspect("cm:lockable") && !p_params.destNode.hasAspect("trx:transferred")) + { + p_params.destNode.unlock(); + } + + var resultId = originalDoc.name, + resultNodeRef = originalDoc.nodeRef.toString(); + + // Construct the result object + results = [ + { + id: resultId, + nodeRef: resultNodeRef, + action: "unlockDocumentAsset", + success: true + }]; + } + catch(e) + { + e.code = status.STATUS_INTERNAL_SERVER_ERROR; + e.message = e.toString(); + throw e; + } + + return results; +} + +/* Bootstrap action script */ +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unzip-to.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unzip-to.post.desc.xml new file mode 100644 index 0000000000..1a14f67c3a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unzip-to.post.desc.xml @@ -0,0 +1,13 @@ + + unzip-to + Document List Action - Unzip + /slingshot/doclib/action/unzip-to/site/{site}/{container}/{path} + /slingshot/doclib/action/unzip-to/site/{site}/{container} + /slingshot/doclib/action/unzip-to/node/{store_type}/{store_id}/{id}/{path} + /slingshot/doclib/action/unzip-to/node/{store_type}/{store_id}/{id} + /slingshot/doclib/action/unzip-to/node/{store_type}/{store_id} + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unzip-to.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unzip-to.post.json.ftl new file mode 100644 index 0000000000..e2f0e9da9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unzip-to.post.json.ftl @@ -0,0 +1,2 @@ +<#import "action.lib.ftl" as actionLib /> +<@actionLib.resultsJSON results=results /> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unzip-to.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unzip-to.post.json.js new file mode 100644 index 0000000000..ca0a33a309 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unzip-to.post.json.js @@ -0,0 +1,92 @@ + + +/** + * Unzip files action + * @method POST + */ + +/** + * Entrypoint required by action.lib.js + * + * @method runAction + * @param p_params {object} Object literal containing files array + * @return {object|null} object representation of action results + */ +function runAction(p_params) +{ + var results = [], + destNode = p_params.destNode, + files = p_params.files, + parent = null, + file, fileNode, result, nodeRef, + fromSite; + + // Must have array of files + if (!files || files.length == 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "No files."); + return; + } + + for (file in files) + { + nodeRef = files[file]; + result = + { + nodeRef: nodeRef, + action: "unzipFile", + success: false + } + + try + { + fileNode = search.findNode(nodeRef); + if (fileNode == null) + { + result.id = file; + result.nodeRef = nodeRef; + result.success = false; + } + else + { + if (p_params.parent && p_params.parent != null) + { + parent = search.findNode(p_params.parent); + } + result.id = fileNode.name; + result.type = fileNode.isContainer ? "folder" : "document"; + + // Retain the name of the site the node is currently in. Null if it's not in a site. + fromSite = fileNode.siteShortName; + + // unzip the node + var unzipAction = actions.create("import"); + unzipAction.parameters.destination = destNode; + unzipAction.execute(fileNode); + result.success = true; + } + } + catch (e) + { + result.id = file; + result.nodeRef = nodeRef; + result.success = false; + + //MNT-7514 Uninformational error message on move when file name conflicts + result.fileExist = false; + + error = e.toString(); + if (error.indexOf("FileExistsException") != -1) + { + result.fileExist = true; + } + } + + results.push(result); + } + + return results; +} + +/* Bootstrap action script */ +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/activity.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/activity.post.desc.xml new file mode 100644 index 0000000000..11f770fc20 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/activity.post.desc.xml @@ -0,0 +1,9 @@ + + activity + Document List Component - activity data webscript + /slingshot/doclib/activity + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/activity.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/activity.post.json.ftl new file mode 100644 index 0000000000..e37684e04d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/activity.post.json.ftl @@ -0,0 +1,3 @@ +{ + "success": true +} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/activity.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/activity.post.json.js new file mode 100644 index 0000000000..e2b6b87d00 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/activity.post.json.js @@ -0,0 +1,137 @@ +/** + * Document List Component: activity + */ +var _regexNodeRef = new RegExp(/^[^\:^ ]+\:\/\/[^\:^ ]+\/[^ ]+$/); + +postActivity(); + +function isNodeRef(value) +{ + var result = false; + try + { + result = _regexNodeRef.test(String(value)); + } + catch (e) + { + } + return result; +} + +/* Posts to the activities service after a Document Library action */ +function postActivity() +{ + var data = {}; + + /* + * Activity Type + */ + var type = json.get("type"); + if (type == null || type.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "Activity 'type' parameter missing when posting activity"); + return; + } + + /* + * Site + */ + var siteId = json.get("site"); + if (siteId == null || siteId.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "'site' parameter missing when posting activity"); + return; + } + var site = siteService.getSite(siteId); + if (site == null) + { + status.setCode(status.STATUS_BAD_REQUEST, "'" + siteId + "' is not a valid site"); + return; + } + + /* + * Check for known nodeRef values + */ + var nodeRef = null, + parentNodeRef = null; + + if (json.has("nodeRef")) + { + nodeRef = json.get("nodeRef"); + if (!isNodeRef(nodeRef)) + { + status.setCode(status.STATUS_BAD_REQUEST, "'" + nodeRef + "' is not a valid NodeRef"); + return; + } + } + if (json.has("parentNodeRef")) + { + parentNodeRef = json.get("parentNodeRef"); + if (!isNodeRef(parentNodeRef)) + { + status.setCode(status.STATUS_BAD_REQUEST, "'" + parentNodeRef + "' is not a valid parent NodeRef"); + return; + } + } + + var strParams = ""; + + switch (String(type).toLowerCase()) + { + case "file-created": + case "file-added": + case "file-updated": + case "file-liked": + case "file-previewed": + case "file-downloaded": + case "folder-liked": + case "inline-edit": + data.title = json.get("fileName"); + data.nodeRef = nodeRef; + strParams = "?nodeRef=" + nodeRef; + break; + + case "files-added": + case "files-deleted": + case "files-updated": + case "folders-deleted": + data.title = json.get("fileCount"); + strParams = "?path=" + json.get("path"); + if (parentNodeRef != null) + { + data.parentNodeRef = parentNodeRef; + } + break; + + case "file-deleted": + case "folder-added": + case "folder-deleted": + data.title = json.get("fileName"); + data.nodeRef = nodeRef; + strParams = "?path=" + json.get("path"); + if (parentNodeRef != null) + { + data.parentNodeRef = parentNodeRef; + } + break; + + default: + status.setCode(status.STATUS_BAD_REQUEST, "'" + type + "' is not a valid activity type"); + return; + } + + try + { + // Log to activity service + data.page = json.get("page") + strParams; + activities.postActivity("org.alfresco.documentlibrary." + type, siteId, "documentlibrary", jsonUtils.toJSONString(data)); + } + catch(e) + { + if (logger.isLoggingEnabled()) + { + logger.log(e); + } + } + +} diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/aspects.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/aspects.get.desc.xml new file mode 100644 index 0000000000..998402f0b8 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/aspects.get.desc.xml @@ -0,0 +1,9 @@ + + aspects + Document List Component - aspects query + /slingshot/doclib/aspects/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/aspects.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/aspects.get.js new file mode 100644 index 0000000000..e0a80f4b95 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/aspects.get.js @@ -0,0 +1,30 @@ +function main() +{ + // nodeRef input + var storeType = url.templateArgs.store_type, + storeId = url.templateArgs.store_id, + id = url.templateArgs.id, + nodeRef = storeType + "://" + storeId + "/" + id; + + var node = search.findNode(nodeRef); + if (node === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Not a valid nodeRef: '" + nodeRef + "'"); + return null; + } + + var current = [], + currentSet = node.aspectsSet.toArray(); + + for (index in currentSet) + { + current.push(currentSet[index].toString()); + } + + return ( + { + current: current + }); +} + +model.aspects = main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/aspects.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/aspects.get.json.ftl new file mode 100644 index 0000000000..7727922c56 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/aspects.get.json.ftl @@ -0,0 +1,5 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "current": [<#list aspects.current as a>"${shortQName(a)}"<#if a_has_next>,] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.desc.xml new file mode 100644 index 0000000000..2bd6b47e79 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.desc.xml @@ -0,0 +1,10 @@ + + category node + Document List Component - category node data webscript + /slingshot/doclib/categorynode/node/{store_type}/{store_id}/{id}/{path} + /slingshot/doclib/categorynode/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.html.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.html.ftl new file mode 100644 index 0000000000..b8bb640289 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.html.ftl @@ -0,0 +1 @@ +<#include "categorynode.get.json.ftl"> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.js new file mode 100644 index 0000000000..30a5b4eca9 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.js @@ -0,0 +1,74 @@ +/** + * Document List Component: category node + */ +model.categorynode = getCategoryNode(); + +/* Create collection of categories for the given path */ +function getCategoryNode() +{ + try + { + var items = new Array(), + hasSubfolders = true, + evalChildFolders = args["children"] !== "false"; + + var catAspect = (args["aspect"] != null) ? args["aspect"] : "cm:generalclassifiable", + nodeRef = url.templateArgs.store_type + "://" + url.templateArgs.store_id + "/" + url.templateArgs.id, + path = url.templateArgs.path, + categoryResults; + + if (path == null) + { + categoryResults = classification.getRootCategories(catAspect); + } + else + { + var queryPath = "/" + catAspect + "/" + encodePath(path); + categoryResults = search.luceneSearch("+PATH:\"" + queryPath + "/*\" -PATH:\"" + queryPath + "/member\""); + } + + // make each result an object and indicate it is selectable in the UI + for each (item in categoryResults) + { + if (evalChildFolders) + { + hasSubfolders = item.children.length > 0; + } + + items.push( + { + node: item, + hasSubfolders: hasSubfolders + }); + } + + items.sort(sortByName); + + return ( + { + items: items + }); + } + catch(e) + { + status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, e.toString()); + return; + } +} + +/* Get the path as an ISO9075 encoded path */ +function encodePath(path) +{ + var parts = path.split("/"); + for (var i = 0, ii = parts.length; i < ii; i++) + { + parts[i] = "cm:" + search.ISO9075Encode(parts[i]); + } + return parts.join("/"); +} + +/* Sort the results by case-insensitive name */ +function sortByName(a, b) +{ + return (b.node.name.toLowerCase() > a.node.name.toLowerCase() ? -1 : 1); +} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.json.ftl new file mode 100644 index 0000000000..6a802d4d9b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.json.ftl @@ -0,0 +1,23 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalResults": ${categorynode.items?size?c}, + "items": + [ + <#list categorynode.items as item> + <#assign c = item.node> + { + "nodeRef": "${c.nodeRef}", + "name": "${c.name}", + "description": "${(c.properties.description!"")}", + "hasChildren": ${item.hasSubfolders?string}, + "userAccess": + { + "create": ${c.hasPermission("CreateChildren")?string}, + "edit": ${c.hasPermission("Write")?string}, + "delete": ${c.hasPermission("Delete")?string} + } + }<#if item_has_next>, + + ] +} + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/container.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/container.get.desc.xml new file mode 100644 index 0000000000..12a540235b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/container.get.desc.xml @@ -0,0 +1,9 @@ + + container + Document List Component - container query + /slingshot/doclib/container/{site}/{container} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/container.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/container.get.js new file mode 100644 index 0000000000..2834ad9b29 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/container.get.js @@ -0,0 +1,36 @@ +function main() +{ + // site, component container input + var siteId = url.templateArgs.site, + containerId = url.templateArgs.container, + containerType = args.type; + + siteNode = siteService.getSite(siteId); + if (siteNode === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Site not found: '" + siteId + "'"); + return null; + } + + containerNode = siteNode.getContainer(containerId); + if (containerNode === null) + { + if (containerType != null && containerType != "") + { + containerNode = siteNode.createContainer(containerId, containerType); + } + else + { + containerNode = siteNode.createContainer(containerId); + } + if (containerNode === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Document Library container '" + containerId + "' not found in '" + siteId + "'. (No permission?)"); + return null; + } + } + + model.container = containerNode; +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/container.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/container.get.json.ftl new file mode 100644 index 0000000000..478ea41211 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/container.get.json.ftl @@ -0,0 +1,9 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "container": + { + "nodeRef": "${container.nodeRef}", + "type": "${container.typeShort}" + } +} + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/containers.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/containers.get.desc.xml new file mode 100644 index 0000000000..6fa87c4e4f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/containers.get.desc.xml @@ -0,0 +1,9 @@ + + containers + Document List Component - Site Containers query + /slingshot/doclib/containers/{site} + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/containers.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/containers.get.js new file mode 100644 index 0000000000..bc77ca2cd9 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/containers.get.js @@ -0,0 +1,28 @@ +/** + * TODO: This webscript should get all the site's containers and return the Document Library ones. + * Unfortunately, there's no way to distinguish them, so we can only return the one called "documentLibrary". + */ +function main() +{ + // site input + var siteId = url.templateArgs.site; + + siteNode = siteService.getSite(siteId); + if (siteNode === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Site not found: '" + siteId + "'"); + return null; + } + + var containerId = "documentLibrary", + containerNode = siteNode.getContainer(containerId); + if (containerNode === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Document Library container '" + containerId + "' not found in '" + siteId + "'. (No permission?)"); + return null; + } + + model.containers = [containerNode]; +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/containers.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/containers.get.json.ftl new file mode 100644 index 0000000000..10e2278bc4 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/containers.get.json.ftl @@ -0,0 +1,15 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "containers": + [ + <#list containers as container> + { + "name": "${container.name}", + "description": "${container.properties.description!"Document Library"}", + "nodeRef": "${container.nodeRef}", + "type": "${container.typeShort}" + }<#if container_has_next>, + + ] +} + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/doclist.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/doclist.get.desc.xml new file mode 100644 index 0000000000..443e28baae --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/doclist.get.desc.xml @@ -0,0 +1,12 @@ + + doclist + Document List Component - doclist data webscript + /slingshot/doclib/doclist/{type}/site/{site}/{container}/{path} + /slingshot/doclib/doclist/{type}/site/{site}/{container} + /slingshot/doclib/doclist/{type}/node/{store_type}/{store_id}/{id}/{path} + /slingshot/doclib/doclist/{type}/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/doclist.get.html.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/doclist.get.html.ftl new file mode 100644 index 0000000000..b01870225f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/doclist.get.html.ftl @@ -0,0 +1,2 @@ +

${doclist.luceneQuery}

+<#include "doclist.get.json.ftl"> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/doclist.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/doclist.get.js new file mode 100644 index 0000000000..d7269dd3dc --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/doclist.get.js @@ -0,0 +1,308 @@ + + + + +const REQUEST_MAX = 1000; +const SITES_SPACE_QNAME_PATH = "/app:company_home/st:sites/"; + +/** + * Main entry point: Create collection of documents and folders in the given space + * + * @method getDoclist + */ +function getDoclist() +{ + // Use helper function to get the arguments + var parsedArgs = ParseArgs.getParsedArgs(); + if (parsedArgs === null) + { + return; + } + + var filter = args.filter, + items = []; + + // Try to find a filter query based on the passed-in arguments + var allNodes = [], + totalRecords = 0, + requestTotalCountMax = 0, + paged = false, + favourites = Common.getFavourites(), + filterParams = Filters.getFilterParams(filter, parsedArgs, + { + favourites: favourites + }), + query = filterParams.query, + allSites = (parsedArgs.nodeRef == "alfresco://sites/home"); + + if (logger.isLoggingEnabled()) + logger.log("doclist.get.js - NodeRef: " + parsedArgs.nodeRef + " Query: " + query); + + var totalItemCount = filterParams.limitResults ? parseInt(filterParams.limitResults, 10) : -1; + // For all sites documentLibrary query we pull in all available results and post filter + if (totalItemCount === 0) totalItemCount = -1; + else if (allSites) totalItemCount = (totalItemCount > 0 ? totalItemCount * 10 : 500); + + if ((filter || "path") == "path") + { + // TODO also add DB filter by "node" (in addition to "path") + var parentNode = parsedArgs.pathNode; + if (parentNode !== null) + { + var ignoreTypes = Filters.IGNORED_TYPES; + skip = -1, + max = -1; + + if (args.size != null) + { + max = args.size; + + if (args.pos > 0) + { + skip = (args.pos - 1) * max; + } + } + + var sortField = (args.sortField == null ? "cm:name" : args.sortField), + sortAsc = (((args.sortAsc == null) || (args.sortAsc == "true")) ? true : false); + + // Get paged set + requestTotalCountMax = skip + REQUEST_MAX; + var pagedResult = parentNode.childFileFolders(true, true, ignoreTypes, skip, max, requestTotalCountMax, sortField, sortAsc, "TODO"); + + allNodes = pagedResult.page; + totalRecords = pagedResult.totalResultCountUpper; + paged = true; + } + } + else + { + // Query the nodes - passing in sort and result limit parameters + if (query !== "") + { + allNodes = search.query( + { + query: query, + language: filterParams.language, + page: + { + maxItems: totalItemCount + }, + sort: filterParams.sort, + templates: filterParams.templates, + namespace: (filterParams.namespace ? filterParams.namespace : null) + }); + + totalRecords = allNodes.length; + } + } + if (logger.isLoggingEnabled()) + logger.log("doclist.get.js - query results: " + allNodes.length); + + // TODO: replace with java.lang.String regex match for performance + var pathMatch; + if (allSites) + { + // Generate a qname path match regex required for all sites 'documentLibrary' results match + pathMatch = new String(parsedArgs.rootNode.qnamePath).replace(/\//g, '\\/') + "\\/.*\\/cm:documentLibrary\\/.*"; + if (logger.isLoggingEnabled()) + logger.log("doclist.get.js - will match results using regex: " + pathMatch); + } + else if (query) + { + // Generate a qname path match regex required for queries where entire repo has been searched - but don't want results + // from sites which are not documents - i.e. filter wiki, blog etc. from results + pathMatch = new String(SITES_SPACE_QNAME_PATH).replace(/\//g, '\\/') + ".*\\/cm:documentLibrary\\/.*"; + } + var pathRegex = new RegExp(pathMatch, "gi"); + + // Ensure folders and folderlinks appear at the top of the list + var folderNodes = [], + documentNodes = [], + qnamepath; + + for each (node in allNodes) + { + if (totalItemCount !== 0) + { + try + { + var qnamePath = node.qnamePath; + if (!query || (allSites && qnamePath.match(pathRegex)) || qnamePath.indexOf(SITES_SPACE_QNAME_PATH) !== 0 || qnamePath.match(pathRegex)) + { + totalItemCount--; + if (node.isContainer || node.isLinkToContainer) + { + folderNodes.push(node); + } + else + { + documentNodes.push(node); + } + } + } + catch (e) + { + // Possibly an old indexed node - ignore it + } + } else break; + } + + // Node type counts + var folderNodesCount = folderNodes.length, + documentNodesCount = documentNodes.length, + nodes; + + if (parsedArgs.type === "documents") + { + nodes = documentNodes; + totalRecords -= folderNodesCount; + } + else + { + // TODO: Sorting with folders at end -- swap order of concat() + nodes = folderNodes.concat(documentNodes); + } + + if (logger.isLoggingEnabled()) + logger.log("doclist.get.js - totalRecords: " + totalRecords); + + // Pagination + var pageSize = args.size || nodes.length, + pagePos = args.pos || "1", + startIndex = (pagePos - 1) * pageSize; + + if (!paged) + { + // Trim the nodes array down to the page size + nodes = nodes.slice(startIndex, pagePos * pageSize); + } + + // Common or variable parent container? + var parent = null; + + if (!filterParams.variablePath) + { + // Parent node permissions (and Site role if applicable) + parent = + { + node: parsedArgs.pathNode, + userAccess: Evaluator.run(parsedArgs.pathNode, true).actionPermissions + }; + } + + var thumbnail = null, + locationNode, + item; + + // Loop through and evaluate each node in this result set + for each (node in nodes) + { + // Get evaluated properties. + item = Evaluator.run(node); + if (item !== null) + { + item.isFavourite = (favourites[item.node.nodeRef] === true || (item.node.properties["smf:actualNodeRef"] && favourites[item.node.properties["smf:actualNodeRef"]] === true)); + item.likes = Common.getLikes(node); + + // Does this collection of nodes have potentially differering paths? + if (filterParams.variablePath || item.isLink) + { + locationNode = item.isLink ? item.linkedNode : item.node; + // Ensure we have Read permissions on the destination on the link object + if (!locationNode.hasPermission("Read")) + { + --totalRecords; + continue; + } + location = Common.getLocation(locationNode, parsedArgs.libraryRoot); + } + else + { + location = + { + site: parsedArgs.location.site, + siteTitle: parsedArgs.location.siteTitle, + container: parsedArgs.location.container, + path: parsedArgs.location.path, + file: node.name + }; + } + location.parent = {}; + if (node.parent != null && node.parent.isContainer && node.parent.hasPermission("Read")) + { + location.parent.nodeRef = String(node.parent.nodeRef.toString()); + } + + // Resolved location + item.location = location; + + items.push(item); + } + else + { + --totalRecords; + } + } + + // Array Remove - By John Resig (MIT Licensed) + var fnArrayRemove = function fnArrayRemove(array, from, to) + { + var rest = array.slice((to || from) + 1 || array.length); + array.length = from < 0 ? array.length + from : from; + return array.push.apply(array, rest); + }; + + /** + * De-duplicate orignals for any existing working copies. + * This can't be done in evaluator.lib.js as it has no knowledge of the current filter or UI operation. + * Note: This may result in pages containing less than the configured amount of items (50 by default). + */ + for each (item in items) + { + if (item.customObj && item.customObj.isWorkingCopy) + { + var workingCopyOriginal = String(item.customObj.workingCopyOriginal); + for (var i = 0, ii = items.length; i < ii; i++) + { + if (String(items[i].node.nodeRef) == workingCopyOriginal) + { + fnArrayRemove(items, i); + --totalRecords; + break; + } + } + } + } + + var paging = + { + totalRecords: totalRecords, + startIndex: startIndex + }; + + if (paged && (totalRecords == requestTotalCountMax)) + { + paging.totalRecordsUpper = requestTotalCountMax; + } + + return ( + { + luceneQuery: query, + paging: paging, + container: parsedArgs.rootNode, + parent: parent, + onlineEditing: utils.moduleInstalled("org.alfresco.module.vti"), + itemCount: + { + folders: folderNodesCount, + documents: documentNodesCount + }, + items: items + }); +} + +/** + * Document List Component: doclist + */ +model.doclist = getDoclist(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/doclist.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/doclist.get.json.ftl new file mode 100644 index 0000000000..101de008c3 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/doclist.get.json.ftl @@ -0,0 +1,47 @@ +<#import "item.lib.ftl" as itemLib /> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalRecords": ${doclist.paging.totalRecords?c}, + <#if doclist.paging.totalRecordsUpper??> + "totalRecordsUpper": ${doclist.paging.totalRecordsUpper?c}, + + "startIndex": ${doclist.paging.startIndex?c}, + "metadata": + { + "repositoryId": "${server.id}", + <#if doclist.container??>"container": "${doclist.container.nodeRef}", + "parent": + { + <#if doclist.parent??> + <#assign parentNode = doclist.parent.node> + "nodeRef": "${parentNode.nodeRef}", + "permissions": + { + "userAccess": + { + <#list doclist.parent.userAccess?keys as perm> + <#if doclist.parent.userAccess[perm]?is_boolean> + "${perm?string}": ${doclist.parent.userAccess[perm]?string}<#if perm_has_next>, + + + } + } + + }, + "onlineEditing": ${doclist.onlineEditing?string}, + "itemCounts": + { + "folders": ${(doclist.itemCount.folders!0)?c}, + "documents": ${(doclist.itemCount.documents!0)?c} + } + }, + "items": + [ + <#list doclist.items as item> + { + <@itemLib.itemJSON item=item /> + }<#if item_has_next>, + + ] +} + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/evaluator.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/evaluator.lib.js new file mode 100644 index 0000000000..2721aa1c28 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/evaluator.lib.js @@ -0,0 +1,258 @@ + +var Evaluator = +{ + /** + * Node Type evaluator + */ + getNodeType: function Evaluator_getNodeType(node) + { + var nodeType = ""; + if (node.isContainer) + { + nodeType = "folder"; + } + else if (node.typeShort == "app:folderlink") + { + nodeType = "folderlink"; + } + else if (node.typeShort == "app:filelink") + { + nodeType = "filelink"; + } + else + { + nodeType = "document"; + } + return nodeType; + }, + + /** + * Parent container evaluators + */ + parentContainer: function Evaluator_parentContainer(node, permissions) + { + }, + + /** + * Document and Folder common evaluators + */ + documentAndFolder: function Evaluator_documentAndFolder(node, permissions, status, actionLabels) + { + /* Simple Workflow */ + if (node.hasAspect("app:simpleworkflow")) + { + status["simple-workflow"] = true; + if (node.properties["app:approveStep"] != null) + { + permissions["simple-approve"] = true; + actionLabels["onActionSimpleApprove"] = node.properties["app:approveStep"]; + } + if (node.properties["app:rejectStep"] != null) + { + permissions["simple-reject"] = true; + actionLabels["onActionSimpleReject"] = node.properties["app:rejectStep"]; + } + } + }, + + /** + * Node Evaluator - main entrypoint + */ + run: function Evaluator_run(node, isParent) + { + var nodeType = Evaluator.getNodeType(node), + actions = {}, + actionSet = "empty", + permissions = {}, + status = {}, + custom = {}, + actionLabels = {}, + activeWorkflows = [], + createdBy = Common.getPerson(node.properties["cm:creator"]), + modifiedBy = Common.getPerson(node.properties["cm:modifier"]), + isLink = false, + linkedNode = null, + lockedBy = null, + lockOwnerUser = ""; + + /** + * COMMON TO ALL + */ + permissions = + { + "create": node.hasPermission("CreateChildren"), + "edit": node.hasPermission("Write"), + "delete": node.hasPermission("Delete"), + "permissions": node.hasPermission("ChangePermissions"), + "cancel-checkout": node.hasPermission("CancelCheckOut") + }; + + // When evaluating parent container + if (isParent) + { + Evaluator.parentContainer(node, permissions); + } + + // Get relevant actions set + switch (nodeType) + { + /** + * SPECIFIC TO: LINK + */ + case "folderlink": + case "filelink": + actionSet = "link"; + isLink = true; + + /** + * NOTE: After this point, the "node" object will be changed to a link's destination node + * if the original node was a filelink type. + */ + linkedNode = node.properties.destination; + if (linkedNode == null) + { + return null; + } + // Re-evaluate the nodeType based on the link's destination node + nodeType = Evaluator.getNodeType(linkedNode); + break; + + /** + * SPECIFIC TO: FOLDER + */ + case "folder": + actionSet = "folder"; + + /* Document Folder common evaluator */ + Evaluator.documentAndFolder(node, permissions, status, actionLabels); + + /* Rules applied? */ + if (node.hasAspect("rule:rules")) + { + status["rules"] = true; + } + + /* Transferred Nodes */ + if (node.hasAspect("trx:transferred")) + { + status["transferred-node"] = true; + permissions["view-source-repository"] = true; + actionSet = "transferredFolder"; + } + break; + + /** + * SPECIFIC TO: DOCUMENTS + */ + case "document": + actionSet = "document"; + + /* Document Folder common evaluator */ + Evaluator.documentAndFolder(node, permissions, status, actionLabels); + + // Working Copy? + if (node.hasAspect("{http://www.alfresco.org/model/content/1.0}workingcopy")) + { + var wcStatus = ""; + lockedBy = Common.getPerson(node.properties["cm:workingCopyOwner"]); + lockOwnerUser = lockedBy.userName; + if (lockOwnerUser == person.properties.userName) + { + wcStatus = "editing"; + actionSet = "workingCopyOwner"; + } + else + { + wcStatus = "locked " + lockedBy.displayName + "|" + lockedBy.userName; + actionSet = "locked"; + } + var wcNode = node.assocs["cm:original"][0]; + custom["isWorkingCopy"] = true; + custom["workingCopyOriginal"] = wcNode.nodeRef; + if (wcNode.hasAspect("{http://www.alfresco.org/model/content/1.0}versionable")) + { + custom["workingCopyVersion"] = wcNode.properties["cm:versionLabel"]; + } + permissions["view-original"] = true; + + status[wcStatus] = true; + } + // Locked? + else if (node.isLocked && !node.hasAspect("trx:transferred")) + { + var lockStatus = ""; + lockedBy = Common.getPerson(node.properties["{http://www.alfresco.org/model/content/1.0}lockOwner"]); + lockOwnerUser = lockedBy.userName; + if (lockOwnerUser == person.properties.userName) + { + lockStatus = "lock-owner"; + actionSet = "lockOwner"; + } + else + { + lockStatus = "locked " + lockedBy.displayName + "|" + lockedBy.userName; + actionSet = "locked"; + } + if (node.hasAspect("{http://www.alfresco.org/model/content/1.0}checkedOut")) + { + var srcNode = node.assocs["cm:workingcopylink"][0]; + custom["hasWorkingCopy"] = true; + custom["workingCopyNode"] = srcNode.nodeRef; + permissions["view-working-copy"] = true; + } + status[lockStatus] = true; + } + + // Inline editable aspect? + if (node.hasAspect("app:inlineeditable")) + { + permissions["inline-edit"] = true; + } + + /* Transferred Nodes */ + if (node.hasAspect("trx:transferred")) + { + status["transferred-node"] = true; + permissions["view-source-repository"] = true; + actionSet = "transferredDocument"; + } + break; + } + + if (node !== null) + { + // Part of an active workflow? Guard against stale worklow tasks. + try + { + for each (activeWorkflow in node.activeWorkflows) + { + activeWorkflows.push(activeWorkflow.id); + } + } + catch (e) {} + + return( + { + node: node, + type: nodeType, + isLink: isLink, + linkedNode: linkedNode, + status: status, + actionSet: actionSet, + actionPermissions: permissions, + createdBy: createdBy, + modifiedBy: modifiedBy, + lockedBy: lockedBy, + tags: node.tags, + activeWorkflows: activeWorkflows, + custom: jsonUtils.toJSONString(custom), + customObj: custom, + actionLabels: actionLabels + }); + } + else + { + return null; + } + } +}; diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/filters.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/filters.lib.js new file mode 100644 index 0000000000..feb46270f6 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/filters.lib.js @@ -0,0 +1,270 @@ +var Filters = +{ + /** + * Type map to filter required types. + * NOTE: "documents" filter also returns folders to show UI hint about hidden folders. + */ + TYPE_MAP: + { + "documents": '+(TYPE:"content" OR TYPE:"app:filelink" OR TYPE:"folder")', + "folders": '+(TYPE:"folder" OR TYPE:"app:folderlink")', + "images": '+@cm\\:content.mimetype:image/*' + }, + + /** + * Types that we want to suppress from the resultset + */ + IGNORED_TYPES: + [ + "cm:systemfolder", + "fm:forums", + "fm:forum", + "fm:topic", + "fm:post" + ], + + /** + * Encode a path with ISO9075 encoding + * + * @method iso9075EncodePath + * @param path {string} Path to be encoded + * @return {string} Encoded path + */ + iso9075EncodePath: function Filter_iso9075EncodePath(path) + { + var parts = path.split("/"); + for (var i = 1, ii = parts.length; i < ii; i++) + { + parts[i] = "cm:" + search.ISO9075Encode(parts[i]); + } + return parts.join("/"); + }, + + /** + * Create filter parameters based on input parameters + * + * @method getFilterParams + * @param filter {string} Required filter + * @param parsedArgs {object} Parsed arguments object literal + * @param optional {object} Optional arguments depending on filter type + * @return {object} Object literal containing parameters to be used in Lucene search + */ + getFilterParams: function Filter_getFilterParams(filter, parsedArgs, optional) + { + var filterParams = + { + query: "", + limitResults: null, + sort: [ + { + column: "@cm:name", + ascending: true + }], + language: "lucene", + templates: null, + variablePath: true + }; + + optional = optional || {}; + + // Sorting parameters specified? + var sortAscending = args.sortAsc, + sortField = args.sortField; + + if (sortAscending == "false") + { + filterParams.sort[0].ascending = false; + } + if (sortField !== null) + { + filterParams.sort[0].column = (sortField.indexOf(":") != -1 ? "@" : "") + sortField; + } + + // Max returned results specified? + var argMax = args.max; + if ((argMax !== null) && !isNaN(argMax)) + { + filterParams.limitResults = argMax; + } + + var favourites = optional.favourites; + if (typeof favourites == "undefined") + { + favourites = []; + } + + // Create query based on passed-in arguments + var filterData = String(args.filterData), + filterQuery = ""; + + // Common types and aspects to filter from the UI - known subtypes of cm:content and cm:folder + var filterQueryDefaults = ' -TYPE:"' + Filters.IGNORED_TYPES.join('" -TYPE:"') + '"'; + + switch (String(filter)) + { + case "all": + filterQuery = "+PATH:\"" + parsedArgs.rootNode.qnamePath + "//*\""; + filterQuery += " +TYPE:\"cm:content\""; + filterParams.query = filterQuery + filterQueryDefaults; + break; + + case "recentlyAdded": + case "recentlyModified": + case "recentlyCreatedByMe": + case "recentlyModifiedByMe": + var onlySelf = (filter.indexOf("ByMe")) > 0 ? true : false, + dateField = (filter.indexOf("Modified") > 0) ? "modified" : "created", + 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 = this.constructPathQuery(parsedArgs); + filterQuery += " +@cm\\:" + dateField + ":[" + fromQuery + "T00\\:00\\:00.000 TO " + toQuery + "T23\\:59\\:59.999]"; + if (onlySelf) + { + filterQuery += " +@cm\\:" + ownerField + ":\"" + person.properties.userName + '"'; + } + filterQuery += " +TYPE:\"cm:content\""; + + filterParams.sort = [ + { + column: "@cm:" + dateField, + ascending: false + }]; + filterParams.query = filterQuery + filterQueryDefaults; + break; + + case "editingMe": + filterQuery = this.constructPathQuery(parsedArgs); + filterQuery += " +((+@cm\\:workingCopyOwner:\"" + person.properties.userName + '")'; + filterQuery += " OR (+@cm\\:lockOwner:\"" + person.properties.userName + '"'; + filterQuery += " +@cm\\:lockType:\"WRITE_LOCK\"))"; + filterParams.query = filterQuery; + break; + + case "editingOthers": + filterQuery = this.constructPathQuery(parsedArgs); + filterQuery += " +ASPECT:\"workingcopy\""; + filterQuery += " +((-@cm\\:workingCopyOwner:\"" + person.properties.userName + '")'; + filterQuery += " OR (-@cm\\:lockOwner:\"" + person.properties.userName + '"'; + filterQuery += " +@cm\\:lockType:\"WRITE_LOCK\"))"; + filterParams.query = filterQuery; + break; + + case "favourites": + for (var favourite in favourites) + { + if (filterQuery) + { + filterQuery += " OR "; + } + filterQuery += "ID:\"" + favourite + "\""; + } + + if (filterQuery.length !== 0) + { + filterQuery = "+(" + filterQuery + ")"; + // no need to specify path here for all sites - IDs are exact matches + if (parsedArgs.nodeRef != "alfresco://sites/home" && parsedArgs.nodeRef != "alfresco://company/home") + { + filterQuery += ' +PATH:"' + parsedArgs.rootNode.qnamePath + '//*"'; + } + } + else + { + // empty favourites query + filterQuery = "+ID:\"\""; + } + + filterParams.query = filterQuery; + break; + + case "synced": + filterQuery = this.constructPathQuery(parsedArgs); + filterQuery += " +ASPECT:\"sync:syncSetMemberNode\""; + filterParams.query = filterQuery; + break; + + case "syncedErrors": + filterQuery = this.constructPathQuery(parsedArgs); + filterQuery += " +ASPECT:\"sync:failed\""; + filterParams.query = filterQuery; + break; + + case "node": + filterParams.variablePath = false; + filterParams.query = "+ID:\"" + parsedArgs.nodeRef + "\""; + break; + + case "tag": + // Remove any trailing "/" character + if (filterData.charAt(filterData.length - 1) == "/") + { + filterData = filterData.slice(0, -1); + } + filterQuery = this.constructPathQuery(parsedArgs); + filterParams.query = filterQuery + " +PATH:\"/cm:taggable/cm:" + search.ISO9075Encode(filterData) + "/member\""; + break; + + case "category": + // Remove any trailing "/" character + if (filterData.charAt(filterData.length - 1) == "/") + { + filterData = filterData.slice(0, -1); + } + filterQuery = this.constructPathQuery(parsedArgs); + filterParams.query = filterQuery + " +PATH:\"/cm:generalclassifiable" + Filters.iso9075EncodePath(filterData) + "/member\""; + break; + + default: // "path" + filterParams.variablePath = false; + filterQuery = "+PATH:\"" + parsedArgs.pathNode.qnamePath + "/*\""; + filterParams.query = filterQuery + filterQueryDefaults; + break; + } + + // Specialise by passed-in type + if (filterParams.query !== "") + { + filterParams.query += " " + (Filters.TYPE_MAP[parsedArgs.type] || ""); + } + + return filterParams; + }, + + constructPathQuery: function constructPathQuery(parsedArgs) + { + var pathQuery = ""; + if (parsedArgs.nodeRef != "alfresco://company/home") + { + if (parsedArgs.nodeRef == "alfresco://sites/home") + { + // all sites query - better with //cm:* + pathQuery = '+PATH:"' + parsedArgs.rootNode.qnamePath + '//cm:*"'; + } + else + { + // site specific query - better with //* + pathQuery = '+PATH:"' + parsedArgs.rootNode.qnamePath + '//*"'; + } + } + return pathQuery; + } +}; diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.get.desc.xml new file mode 100644 index 0000000000..5733aa7cbe --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.get.desc.xml @@ -0,0 +1,9 @@ + + Create Fodler Templates + Document List Component - Create Folder Templates GET data webscript + /slingshot/doclib/folder-templates + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.get.js new file mode 100644 index 0000000000..db1e8b9801 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.get.js @@ -0,0 +1,10 @@ +/** + * Document List Component: Create New Node - get list of available node templates in the Data Dictionary + */ +function main() +{ + var nodes = search.selectNodes('/app:company_home/app:dictionary/app:space_templates/*[subtypeOf("cm:folder")]'); + return nodes; +} + +model.nodes = main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.get.json.ftl new file mode 100644 index 0000000000..4923d97c59 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.get.json.ftl @@ -0,0 +1,15 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + [ + <#list nodes as node> + { + "nodeRef": "${node.nodeRef}", + "name": "${node.name}", + "title": "${node.properties.title!""}", + "description": "${node.properties.description!""}" + }<#if node_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.post.desc.xml new file mode 100644 index 0000000000..e1ac598df1 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.post.desc.xml @@ -0,0 +1,9 @@ + + Create Folder Templates + Document List Component - Create Folder Templates POST data webscript + /slingshot/doclib/folder-templates + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.post.json.ftl new file mode 100644 index 0000000000..618584f2f2 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.post.json.ftl @@ -0,0 +1,7 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "success": true, + "persistedObject": "${persistedObject}", + "name": "${name}" +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.post.json.js new file mode 100644 index 0000000000..808d19a3cb --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/folder-templates.post.json.js @@ -0,0 +1,37 @@ +/** + * Document List Component: Create New Node - create copy of node template in the Data Dictionary + */ +function main() +{ + // get the arguments - expecting the "sourceNodeRef" and "parentNodeRef" of the source node to copy + // and the parent node to contain the new copy of the source. + var sourceNodeRef = json.get("sourceNodeRef"); + if (sourceNodeRef == null || sourceNodeRef.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "Mandatory 'sourceNodeRef' parameter missing."); + return; + } + var parentNodeRef = json.get("parentNodeRef"); + if (parentNodeRef == null || parentNodeRef.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "Mandatory 'parentNodeRef' parameter missing."); + return; + } + + // get the nodes and perform the copy - permission failures etc. will produce a status code response + var sourceNode = search.findNode(sourceNodeRef), + parentNode = search.findNode(parentNodeRef); + if (sourceNode == null || parentNode == null) + { + status.setCode(status.STATUS_NOT_FOUND, "Source or destination node is missing for copy operation."); + } + var copy = sourceNode.copy(parentNode, true); + copy.setName(json.get("prop_cm_name").toString()); + copy.properties["cm:description"] = json.get("prop_cm_description").toString(); + copy.properties["cm:title"] = json.get("prop_cm_title").toString(); + copy.save(); + model.persistedObject = copy.getNodeRef().toString(); + model.name = json.get("prop_cm_name").toString(); +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/images.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/images.get.desc.xml new file mode 100644 index 0000000000..4de3a7d984 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/images.get.desc.xml @@ -0,0 +1,10 @@ + + images + Images List Component - images data webscript + /slingshot/doclib/images/site/{site}/{container}/{path} + /slingshot/doclib/images/site/{site}/{container} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/images.get.html.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/images.get.html.ftl new file mode 100644 index 0000000000..a6ef0db7bd --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/images.get.html.ftl @@ -0,0 +1,2 @@ +

${images.luceneQuery}

+<#include "images.get.json.ftl"> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/images.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/images.get.js new file mode 100644 index 0000000000..1563ed1703 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/images.get.js @@ -0,0 +1,55 @@ + + + +/** + * Main entry point: Create collection of images in the given space (and its subspaces) + * @method main + */ +function main() +{ + var items = [], + assets, + filterParams, + query; + + // Use helper function to get the arguments + var parsedArgs = ParseArgs.getParsedArgs(); + if (parsedArgs === null) + { + return; + } + + // Build the query specific to image/* mimetypes + filterParams = Filters.getFilterParams("all", parsedArgs); + if (parsedArgs.pathNode === null) + { + query = "+PATH:\"" + parsedArgs.rootNode.qnamePath + "//*\" "; + } + else + { + query = "+PATH:\"" + parsedArgs.pathNode.qnamePath + "//*\" "; + } + query += "+TYPE:\"cm:content\" +@cm\\:content.mimetype:image/*"; + + // Sort the list before trimming to page chunks + assets = search.query( + { + query: query, + page: + { + maxItems: (filterParams.limitResults ? parseInt(filterParams.limitResults, 10) : 0) + }, + sort: filterParams.sort + }); + + return ( + { + luceneQuery: query, + items: assets + }); +} + +/** + * Images List Component: images + */ +model.images = main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/images.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/images.get.json.ftl new file mode 100644 index 0000000000..8dfc6addfa --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/images.get.json.ftl @@ -0,0 +1,17 @@ +<#macro dateFormat date>${xmldate(date)} +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "items": + [ + <#list images.items as item> + { + "name": "${(item.properties.name)}", + "title": "${(item.properties.title!item.name)}", + "modifier": "${(item.properties.modifier)}", + "modifiedOn": "<@dateFormat item.properties.modified />", + "nodeRef": "${item.nodeRef}" + }<#if item_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/item.lib.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/item.lib.ftl new file mode 100644 index 0000000000..3843b4477c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/item.lib.ftl @@ -0,0 +1,168 @@ +<#assign workingCopyLabel = " " + message("coci_service.working_copy_label")> + +<#-- This function is used below to detect numerical property values of Infinity, -Infinity and NaN --> +<#macro renderNumber value=0> +<#if value?is_number> + <#if value?c == '\xfffd' || value?c == '\x221e' || value?c == '-\x221e'>0 + <#else>${value?c} + + + + +<#macro dateFormat date=""><#if date?is_date>${xmldate(date)} + +<#macro itemJSON item> + <#escape x as jsonUtils.encodeJSONString(x)> + <#local node = item.node> + <#local version = "1.0"> + <#if node.hasAspect("{http://www.alfresco.org/model/content/1.0}versionable")><#local version = node.properties["cm:versionLabel"]!""> + <#if item.createdBy??> + <#local createdBy = item.createdBy.displayName> + <#local createdByUser = item.createdBy.userName> + <#else> + <#local createdBy="" createdByUser=""> + + <#if item.modifiedBy??> + <#local modifiedBy = item.modifiedBy.displayName> + <#local modifiedByUser = item.modifiedBy.userName> + <#else> + <#local modifiedBy="" modifiedByUser=""> + + <#if item.lockedBy??> + <#local lockedBy = item.lockedBy.displayName> + <#local lockedByUser = item.lockedBy.userName> + <#else> + <#local lockedBy="" lockedByUser=""> + + <#local tags><#list item.tags as tag>"${tag}"<#if tag_has_next>, + "nodeRef": "${node.nodeRef}", + "nodeType": "${shortQName(node.type)}", + "type": "${item.type}", + "mimetype": "${node.mimetype!""}", + "isFolder": <#if item.linkedNode??>${item.linkedNode.isContainer?string}<#else>${node.isContainer?string}, + "isLink": ${(item.isLink!false)?string}, +<#if item.linkedNode??> + "linkedNodeRef": "${item.linkedNode.nodeRef?string}", + + "fileName": "<#if item.linkedNode??>${item.linkedNode.name}<#else>${node.name}", + "displayName": "<#if item.linkedNode??>${item.linkedNode.name}<#elseif node.hasAspect("{http://www.alfresco.org/model/content/1.0}workingcopy")>${node.name?replace(workingCopyLabel, "")}<#else>${node.name}", + "status": "<#list item.status?keys as s><#if item.status[s]?is_boolean && item.status[s] == true>${s}<#if s_has_next>,", + "title": "${node.properties.title!""}", + "description": "${node.properties.description!""}", + "author": "${node.properties.author!""}", + "createdOn": "<@dateFormat node.properties.created />", + "createdBy": "${createdBy}", + "createdByUser": "${createdByUser}", + "modifiedOn": "<@dateFormat node.properties.modified />", + "modifiedBy": "${modifiedBy}", + "modifiedByUser": "${modifiedByUser}", + <#if node.hasAspect("{http://www.alfresco.org/model/content/1.0}thumbnailModification")> + <#if node.properties.lastThumbnailModification??> + <#list node.properties.lastThumbnailModification as thumbnailMod> + <#if thumbnailMod?contains("doclib")> + "lastThumbnailModification": "${thumbnailMod}", + + + + + "lockedBy": "${lockedBy}", + "lockedByUser": "${lockedByUser}", + "size": "${node.size?c}", + "version": "${version}", + "contentUrl": "api/node/content/${node.storeType}/${node.storeId}/${node.id}/${node.name?url}", + "webdavUrl": "${node.webdavUrl}", + "actionSet": "${item.actionSet}", + "tags": <#noescape>[${tags}], + <#if node.hasAspect("{http://www.alfresco.org/model/content/1.0}generalclassifiable")> + "categories": [<#list node.properties.categories![] as c>["${c.name}", "${c.displayPath?replace("/categories/General","")}"]<#if c_has_next>,], + + <#if item.activeWorkflows??>"activeWorkflows": "<#list item.activeWorkflows as aw>${aw}<#if aw_has_next>,", + <#if item.isFavourite??>"isFavourite": ${item.isFavourite?string}, + "likes":<#if item.likes??> + { + "isLiked": ${item.likes.isLiked?string}, + "totalLikes": ${item.likes.totalLikes?c} + }<#else>null, + "location": + { + "repositoryId": "${(node.properties["trx:repositoryId"])!(server.id)}", + "site": "${item.location.site!""}", + "siteTitle": "${item.location.siteTitle!""}", + "container": "${item.location.container!""}", + "path": "${item.location.path!""}", + "file": "${item.location.file!""}", + "parent": + { + <#if item.location.parent??> + <#if item.location.parent.nodeRef??> + "nodeRef": "${item.location.parent.nodeRef!""}" + + + } + }, + <#if node.hasAspect("{http://www.alfresco.org/model/content/1.0}geographic")>"geolocation": + { + "latitude": <@renderNumber node.properties["cm:latitude"] />, + "longitude": <@renderNumber node.properties["cm:longitude"] /> + }, + <#if node.hasAspect("{http://www.alfresco.org/model/audio/1.0}audio")>"audio": + { + "album": "${node.properties["audio:album"]!""}", + "artist": "${node.properties["audio:artist"]!""}", + "composer": "${node.properties["audio:composer"]!""}", + "engineer": "${node.properties["audio:engineer"]!""}", + "genre": "${node.properties["audio:genre"]!""}", + "trackNumber": <@renderNumber node.properties["audio:trackNumber"] />, + "releaseDate": "<@dateFormat node.properties["audio:releaseDate"] />", + "sampleRate": <@renderNumber node.properties["audio:sampleRate"] />, + "sampleType": "${node.properties["audio:sampleType"]!""}", + "channelType": "${node.properties["audio:channelType"]!""}", + "compressor": "${node.properties["audio:compressor"]!""}" + }, + <#if node.hasAspect("{http://www.alfresco.org/model/exif/1.0}exif")>"exif": + { + "dateTimeOriginal": "<@dateFormat node.properties["exif:dateTimeOriginal"] />", + "pixelXDimension": <@renderNumber node.properties["exif:pixelXDimension"] />, + "pixelYDimension": <@renderNumber node.properties["exif:pixelYDimension"] />, + "exposureTime": <@renderNumber node.properties["exif:exposureTime"] />, + "fNumber": <@renderNumber node.properties["exif:fNumber"] />, + "flash": ${(node.properties["exif:flash"]!false)?string}, + "focalLength": <@renderNumber node.properties["exif:focalLength"] />, + "isoSpeedRatings": "${node.properties["exif:isoSpeedRatings"]!""}", + "manufacturer": "${node.properties["exif:manufacturer"]!""}", + "model": "${node.properties["exif:model"]!""}", + "software": "${node.properties["exif:software"]!""}", + "orientation": <@renderNumber node.properties["exif:orientation"] />, + "xResolution": <@renderNumber node.properties["exif:xResolution"] />, + "yResolution": <@renderNumber node.properties["exif:yResolution"] />, + "resolutionUnit": "${node.properties["exif:resolutionUnit"]!""}" + }, + "permissions": + { + "inherited": ${node.inheritsPermissions?string}, + "roles": + [ + <#list node.fullPermissions as permission> + "${permission?string}"<#if permission_has_next>, + + ], + "userAccess": + { + <#list item.actionPermissions?keys as actionPerm> + <#if item.actionPermissions[actionPerm]?is_boolean> + "${actionPerm?string}": ${item.actionPermissions[actionPerm]?string}<#if actionPerm_has_next>, + + + } + }, + <#if item.custom??>"custom": <#noescape>${item.custom}, + "actionLabels": + { +<#if item.actionLabels??> + <#list item.actionLabels?keys as actionLabel> + "${actionLabel?string}": "${item.actionLabels[actionLabel]}"<#if actionLabel_has_next>, + + + } + + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.desc.xml new file mode 100644 index 0000000000..5cc5522d42 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.desc.xml @@ -0,0 +1,9 @@ + + path + Document List Component - path resolver + /slingshot/doclib/node/{store_type}/{store_id}/{id}/location + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.js new file mode 100644 index 0000000000..795f7e2bab --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.js @@ -0,0 +1,49 @@ + + + +/** + * Main entry point: Return scope of nodeRef + * + * @method resolvePath + */ +function resolveLocations() +{ + // Use helper function to get the arguments + var parsedArgs = ParseArgs.getParsedArgs(); + if (parsedArgs === null) + { + return; + } + var libraryRoot = parsedArgs.libraryRoot ? parsedArgs.libraryRoot : companyhome, + repoLocation = Common.getLocation(parsedArgs.rootNode, libraryRoot), + siteLocation = Common.getLocation(parsedArgs.rootNode, null), + fileName = parsedArgs.rootNode.properties["name"]; + + var locations = + { + repo: + { + path: repoLocation.path, + file: fileName ? fileName : null + } + }; + + if (siteLocation.site) + { + locations.site = + { + path: siteLocation.path, + file: fileName ? fileName : null, + site: siteLocation.site, + siteTitle: siteLocation.siteTitle, + container: siteLocation.container + }; + } + + return locations; +} + +/** + * + */ +model.locations = resolveLocations(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.json.ftl new file mode 100644 index 0000000000..cc6dce2ad9 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.json.ftl @@ -0,0 +1,19 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + <#if (locations.site??)> + <#assign site = locations.site> + "site": + { + "site": "${site.site!""}", + "siteTitle": "${site.siteTitle!""}", + "container": "${site.container!""}", + "path": "${site.path!""}", + "file": "${site.file!""}" + }, + "repo": + { + "path": "${locations.repo.path!""}", + "file": "${locations.repo.file!""}" + } +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.get.desc.xml new file mode 100644 index 0000000000..0a126a030c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.get.desc.xml @@ -0,0 +1,9 @@ + + Create Node Templates + Document List Component - Create Node Templates GET data webscript + /slingshot/doclib/node-templates + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.get.js new file mode 100644 index 0000000000..52502562fd --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.get.js @@ -0,0 +1,10 @@ +/** + * Document List Component: Create New Node - get list of available node templates in the Data Dictionary + */ +function main() +{ + var nodes = search.selectNodes('/app:company_home/app:dictionary/app:node_templates/*[subtypeOf("cm:content")]'); + return nodes; +} + +model.nodes = main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.get.json.ftl new file mode 100644 index 0000000000..4923d97c59 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.get.json.ftl @@ -0,0 +1,15 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + [ + <#list nodes as node> + { + "nodeRef": "${node.nodeRef}", + "name": "${node.name}", + "title": "${node.properties.title!""}", + "description": "${node.properties.description!""}" + }<#if node_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.post.desc.xml new file mode 100644 index 0000000000..04991abaf6 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.post.desc.xml @@ -0,0 +1,9 @@ + + Create Node Templates + Document List Component - Create Node Templates POST data webscript + /slingshot/doclib/node-templates + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.post.json.ftl new file mode 100644 index 0000000000..82e22e050d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.post.json.ftl @@ -0,0 +1,7 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "success": true, + "name": "${result.name}", + "nodeRef": "${result.nodeRef}" +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.post.json.js new file mode 100644 index 0000000000..d08ce460f5 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node-templates.post.json.js @@ -0,0 +1,31 @@ +/** + * Document List Component: Create New Node - create copy of node template in the Data Dictionary + */ +function main() +{ + // get the arguments - expecting the "sourceNodeRef" and "parentNodeRef" of the source node to copy + // and the parent node to contain the new copy of the source. + var sourceNodeRef = json.get("sourceNodeRef"); + if (sourceNodeRef == null || sourceNodeRef.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "Mandatory 'sourceNodeRef' parameter missing."); + return; + } + var parentNodeRef = json.get("parentNodeRef"); + if (parentNodeRef == null || parentNodeRef.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "Mandatory 'parentNodeRef' parameter missing."); + return; + } + + // get the nodes and perform the copy - permission failures etc. will produce a status code response + var sourceNode = search.findNode(sourceNodeRef), + parentNode = search.findNode(parentNodeRef); + if (sourceNode == null || parentNode == null) + { + status.setCode(status.STATUS_NOT_FOUND, "Source or destination node is missing for copy operation."); + } + model.result = sourceNode.copy(parentNode); +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.desc.xml new file mode 100644 index 0000000000..9675060892 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.desc.xml @@ -0,0 +1,9 @@ + + node + Document List Component - node data webscript + /slingshot/doclib/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.js new file mode 100644 index 0000000000..d0be6879fd --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.js @@ -0,0 +1,73 @@ + + + + +/** + * Main entry point: Return single document or folder given it's nodeRef + * + * @method getDoclist + */ +function getDoclist() +{ + // Use helper function to get the arguments + var parsedArgs = ParseArgs.getParsedArgs(); + if (parsedArgs === null) + { + return; + } + + parsedArgs.pathNode = ParseArgs.resolveNode(parsedArgs.nodeRef); + parsedArgs.location = Common.getLocation(parsedArgs.pathNode, parsedArgs.libraryRoot); + + var filter = args.filter, + items = []; + + var favourites = Common.getFavourites(), + node = parsedArgs.pathNode, + parent = null; + + var thumbnail = null, + item = Evaluator.run(node); + + item.isFavourite = (favourites[node.nodeRef] === true); + item.likes = Common.getLikes(node); + + item.location = + { + site: parsedArgs.location.site, + siteTitle: parsedArgs.location.siteTitle, + container: parsedArgs.location.container, + path: parsedArgs.location.path, + file: node.name + }; + + item.location.parent = {}; + if (node.parent != null && node.parent.isContainer && node.parent.hasPermission("Read")) + { + parent = + { + node: node.parent, + userAccess: Evaluator.run(node.parent, true).actionPermissions + }; + item.location.parent.nodeRef = String(node.parent.nodeRef.toString()); + } + + // Special case for container and libraryRoot nodes + if ((parsedArgs.location.containerNode && String(parsedArgs.location.containerNode.nodeRef) == String(node.nodeRef)) || + (parsedArgs.libraryRoot && String(parsedArgs.libraryRoot.nodeRef) == String(node.nodeRef))) + { + item.location.file = ""; + } + + return ( + { + parent: parent, + onlineEditing: utils.moduleInstalled("org.alfresco.module.vti"), + items: [item] + }); +} + +/** + * Document List Component: doclist + */ +model.doclist = getDoclist(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.json.ftl new file mode 100644 index 0000000000..82b75b30c0 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.json.ftl @@ -0,0 +1,31 @@ +<#import "item.lib.ftl" as itemLib /> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "metadata": + { + "parent": + { + <#if doclist.parent??> + <#assign parentNode = doclist.parent.node> + "nodeRef": "${parentNode.nodeRef}", + "permissions": + { + "userAccess": + { + <#list doclist.parent.userAccess?keys as perm> + <#if doclist.parent.userAccess[perm]?is_boolean> + "${perm?string}": ${doclist.parent.userAccess[perm]?string}<#if perm_has_next>, + + + } + } + + }, + "onlineEditing": ${doclist.onlineEditing?string} + }, + "item": + { + <#if doclist.items??><@itemLib.itemJSON item=doclist.items[0] /> + } +} + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/parse-args.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/parse-args.lib.js new file mode 100644 index 0000000000..e4357048b6 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/parse-args.lib.js @@ -0,0 +1,520 @@ +const THUMBNAIL_NAME = "doclib", + TYPE_SITES = "st:sites", + PREF_DOCUMENT_FAVOURITES = "org.alfresco.share.documents.favourites", + PREF_FOLDER_FAVOURITES = "org.alfresco.share.folders.favourites", + LIKES_SCHEME = "likesRatingScheme"; + +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] != "object") + { + var person = people.getPerson(username); + if (person == null) + { + if (username == "System" || username.match("^System@") == "System@") + { + // special case for the System users + person = + { + properties: + { + userName: "System", + firstName: "System", + lastName: "User" + }, + assocs: {} + }; + } + else + { + // missing person - may have been deleted from the database + person = + { + properties: + { + userName: username, + firstName: "", + lastName: "" + }, + 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]; + }, + + /** + * Cache for group objects + */ + GroupCache: {}, + + /** + * Gets / caches a group object + * + * @method getGroup + * @param groupname {string} Group name + */ + getGroup: function Common_getGroup(groupname) + { + if (groupname == null || groupname == "") + { + return null; + } + + if (typeof Common.GroupCache[groupname] != "object") + { + var group = groups.getGroupForFullAuthorityName(groupname); + if (group == null && groupname == "GROUP_EVERYONE") + { + group = + { + fullName: groupname, + shortName: "EVERYONE", + displayName: "EVERYONE" + }; + } + Common.GroupCache[groupname] = group; + } + return Common.GroupCache[groupname]; + }, + + /** + * Cache for site objects + */ + SiteCache: {}, + + /** + * Gets / caches a site object + * + * @method getSite + * @param siteId {string} Site ID + */ + getSite: function Common_getSite(siteId) + { + if (typeof Common.SiteCache[siteId] != "object") + { + Common.SiteCache[siteId] = siteService.getSite(siteId); + } + return Common.SiteCache[siteId]; + }, + + /** + * Get the user's favourite docs and folders from our slightly eccentric Preferences Service + * + * @method getFavourites + */ + getFavourites: function Common_getFavourites() + { + var prefs = preferenceService.getPreferences(person.properties.userName, PREF_DOCUMENT_FAVOURITES), + favourites = {}, + arrFavs = [], + strFavs, f, ff; + try + { + /** + * Fasten seatbelts... + * An "eval" could be used here, but the Rhino debugger will complain if throws an exception, which gets old very quickly. + * e.g. var strFavs = eval('try{(prefs.' + PREF_DOCUMENT_FAVOURITES + ')}catch(e){}'); + */ + if (prefs && prefs.org && prefs.org.alfresco && prefs.org.alfresco.share && prefs.org.alfresco.share.documents) + { + strFavs = prefs.org.alfresco.share.documents.favourites; + if (typeof strFavs == "string") + { + arrFavs = strFavs.split(","); + for (f = 0, ff = arrFavs.length; f < ff; f++) + { + favourites[arrFavs[f]] = true; + } + } + } + // Same thing but for folders + prefs = preferenceService.getPreferences(person.properties.userName, PREF_FOLDER_FAVOURITES); + if (prefs && prefs.org && prefs.org.alfresco && prefs.org.alfresco.share && prefs.org.alfresco.share.folders) + { + strFavs = prefs.org.alfresco.share.folders.favourites; + if (typeof strFavs == "string") + { + arrFavs = strFavs.split(","); + for (f = 0, ff = arrFavs.length; f < ff; f++) + { + favourites[arrFavs[f]] = true; + } + } + } + } + catch (e) + { + } + + return favourites; + }, + + /** + * Returns the number of nested levels at which two paths match. This is used when there are two + * parents to a node and is used to determine the correct one. + * + * @param count + * @param firstNode An array of Strings that are the elements in a path to a node. + * @param secondNode An array of Strings that are the elements in a path to a different node. + */ + getDeepOfPath: function Common_getDeepOfPath(count, firstNode, secondNode) + { + var i = 0; + for (var i = 0; i < count; i++) + { + if (!firstNode[i].equals(secondNode[i])) + { + break; + } + } + return i; + }, + + /** + * Generates a location object literal for a given node. + * Location is Site-relative unless a libraryRoot node is passed in. + * + * @method getLocation + * @param node {ScriptNode} Node to generate location for + * @param libraryRoot {ScriptNode} Optional node to work out relative location from. + * @return {object} Location object literal. + */ + getLocation: function Common_getLocation(node, libraryRoot) + { + var location = null, + qnamePaths = node.qnamePath.split("/"), + displayPaths = node.displayPath.split("/"); + + if (libraryRoot == undefined && qnamePaths[2] != TYPE_SITES) + { + libraryRoot = companyhome; + } + + if (libraryRoot) + { + var libraryRootDisplayPath = libraryRoot.displayPath.split("/"); + var deepLibraryRoot = libraryRootDisplayPath.length; + var deepNode = displayPaths.length; + + var count = (deepNode > deepLibraryRoot) ? Common.getDeepOfPath(deepNode, displayPaths, libraryRootDisplayPath): Common.getDeepOfPath(deepLibraryRoot, libraryRootDisplayPath, displayPaths); + + if (node.qnamePath.contains(libraryRoot.qnamePath)) + { + count++; + } + + // Generate the path from the supplied library root + location = + { + site: null, + siteTitle: null, + container: null, + path: "/" + displayPaths.slice(count, deepNode).join("/"), + file: node.name + }; + } + else if ((qnamePaths.length > 4) && (qnamePaths[2] == TYPE_SITES)) + { + var siteId = displayPaths[3], + siteNode = Common.getSite(siteId), + containerId = qnamePaths[4].substr(3); + + if (siteNode != null) + { + location = + { + site: siteId, + siteNode: siteNode, + siteTitle: siteNode.title, + container: containerId, + containerNode: siteNode.getContainer(containerId), + path: "/" + displayPaths.slice(5, displayPaths.length).join("/"), + file: node.name + }; + } + } + + if (location == null) + { + location = + { + site: null, + siteTitle: null, + container: null, + path: "/" + displayPaths.slice(2, displayPaths.length).join("/"), + file: node.name + }; + } + + return location; + }, + + /** + * Returns an object literal representing the current "likes" rating for a node + * + * @method getLikes + * @param node {ScriptNode} Node to query + * @return {object} Likes object literal. + */ + getLikes: function Common_getLikes(node) + { + var isLiked = false, + totalLikes = 0; + + try + { + totalLikes = ratingService.getRatingsCount(node, LIKES_SCHEME); + isLiked = totalLikes === 0 ? false : ratingService.getRating(node, LIKES_SCHEME) !== -1; + } + catch (e) {} + + return ( + { + isLiked: isLiked, + totalLikes: totalLikes + }); + } +}; + +var ParseArgs = +{ + /** + * Get and parse arguments + * + * @method getParsedArgs + * @return {array|null} Array containing the validated input parameters + */ + getParsedArgs: function ParseArgs_getParsedArgs(containerType) + { + var type = url.templateArgs.type, + libraryRoot = args.libraryRoot, + rootNode = null, + pathNode = null, + nodeRef = null, + path = "", + location = null; + + // Is this library rooted from a non-site nodeRef? + if (libraryRoot !== null) + { + libraryRoot = ParseArgs.resolveNode(libraryRoot); + } + + + 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; + if (url.templateArgs.store_type == "workspace") + { + rootNode = ParseArgs.resolveNode(nodeRef); + } + else + { + rootNode = libraryRoot || ParseArgs.resolveNode(nodeRef); + } + if (rootNode == null) + { + status.setCode(status.STATUS_NOT_FOUND, "Not a valid nodeRef: '" + nodeRef + "'"); + return null; + } + + // Special case: make sure filter picks up correct mode + if (type == null && args.filter == null) + { + args.filter = "node"; + } + } + else + { + /** + * Site and container input + */ + var siteId = url.templateArgs.site, + containerId = url.templateArgs.container, + siteNode = siteService.getSite(siteId); + + if (siteNode === null) + { + status.setCode(status.STATUS_GONE, "Site not found: '" + siteId + "'"); + return null; + } + + rootNode = siteNode.getContainer(containerId); + if (rootNode === null) + { + rootNode = siteNode.aquireContainer(containerId, containerType || "cm:folder", {"cm:description": "Document Library"}); + if (rootNode === null) + { + status.setCode(status.STATUS_GONE, "Document Library container '" + containerId + "' not found in '" + siteId + "'. (No permission?)"); + return null; + } + } + } + + // Path input? + path = url.templateArgs.path || ""; + pathNode = path.length > 0 ? rootNode.childByNamePath(path) : (pathNode || rootNode); + if (pathNode === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Path not found: '" + path + "'"); + return null; + } + + // Parent location parameter adjustment + location = Common.getLocation(pathNode, libraryRoot); + if (path !== "") + { + location.path = ParseArgs.combinePaths(location.path, location.file); + } + if (args.filter !== "node" && !pathNode.isContainer) + { + location.file = ""; + } + + var objRet = + { + rootNode: rootNode, + pathNode: pathNode, + libraryRoot: libraryRoot, + location: location, + path: path, + nodeRef: nodeRef, + type: type + }; + + // Multiple input files in the JSON body? + var files = ParseArgs.getMultipleInputValues("nodeRefs"); + if (typeof files != "string") + { + objRet.files = files; + } + + return objRet; + }, + + /** + * Resolve "virtual" nodeRefs into nodes + * + * @method resolveVirtualNodeRef + * @deprecated for ParseArgs.resolveNode + */ + resolveVirtualNodeRef: function ParseArgs_resolveVirtualNodeRef(nodeRef) + { + if (logger.isLoggingEnabled()) + { + logger.log("WARNING: ParseArgs.resolveVirtualNodeRef is deprecated for ParseArgs.resolveNode"); + } + return ParseArgs.resolveNode(nodeRef); + }, + + /** + * Resolve "virtual" nodeRefs, nodeRefs and xpath expressions into nodes + * + * @method resolveNode + * @param reference {string} "virtual" nodeRef, nodeRef or xpath expressions + * @return {ScriptNode|null} Node corresponding to supplied expression. Returns null if node cannot be resolved. + */ + resolveNode: function ParseArgs_resolveNode(reference) + { + return utils.resolveNodeReference(reference); + }, + + /** + * Get multiple input files + * + * @method getMultipleInputValues + * @param param {string} Property name containing the files array + * @return {array|string} Array containing the files, or string error + */ + getMultipleInputValues: function ParseArgs_getMultipleInputValues(param) + { + var values = [], + error = null; + + try + { + // Was a JSON parameter list supplied? + if (typeof json == "object") + { + 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); + }, + + /** + * Append multiple parts of a path, ensuring duplicate path separators are removed. + * + * @method combinePaths + * @param path1 {string} First path + * @param path2 {string} Second path + * @param ... + * @param pathN {string} Nth path + * @return {string} A string containing the combined paths + */ + combinePaths: function ParseArgs_combinePaths() + { + var path = "", i, ii; + for (i = 0, ii = arguments.length; i < ii; i++) + { + if (arguments[i] !== null) + { + path += arguments[i] + (arguments[i] !== "/" ? "/" : ""); + } + } + + return path.replace(/\/{2,}/g, "/").replace(/(.)\/$/g, "$1"); + } +}; diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.get.desc.xml new file mode 100644 index 0000000000..ec232e5f7a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.get.desc.xml @@ -0,0 +1,9 @@ + + permissions + Document List Component - permissions data webscript + /slingshot/doclib/permissions/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.get.js new file mode 100644 index 0000000000..5e6512861b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.get.js @@ -0,0 +1,134 @@ + + +/** + * Main entry point: Retrieve permissions and associated metadata for given node + * + * @method getPermissions + */ +function getPermissions() +{ + /** + * 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, + node = ParseArgs.resolveNode(nodeRef); + + if (node == null) + { + node = search.findNode(nodeRef); + if (node === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Not a valid nodeRef: '" + nodeRef + "'"); + return null; + } + } + + // Get array of settable permissions + var settable, location = Common.getLocation(node); + + // MNT-12761 + // If this node lives within a Site, then append only the Site-specific roles, else Repository-specific + if (location.siteNode != null) + { + settable = location.siteNode.getNode().getSettablePermissions(); + } + else + { + settable = node.getSettablePermissions(); + } + + // Get full permission set, including inherited + // [ALLOWED|DENIED];[USERNAME|GROUPNAME|ROLE];PERMISSION;[INHERITED|DIRECT] + var isInherited = node.inheritsPermissions(), + nodePermissions = parsePermissions(node.getDirectPermissions(), settable), + inheritedPermissions = [], + canReadInherited = true; + + if (node.parent.hasPermission("ReadPermissions")) + { + inheritedPermissions = parsePermissions(node.parent.getPermissions(), settable); + } + else + { + canReadInherited = false; + } + + return ( + { + inherited: inheritedPermissions, + isInherited: isInherited, + canReadInherited: canReadInherited, + direct: nodePermissions, + settable: settable + }); +} + +function parsePermissions(p_permissions, p_settable) +{ + var results = [], + settable = {}, + tokens, authority, authorityId, role, i, ii; + + // Settable array into object for "x in y" style operations + for (i = 0, ii = p_settable.length; i < ii; i++) + { + if (p_settable[i] !== undefined) + { + settable[p_settable[i]] = true; + } + } + + for (i = 0, ii = p_permissions.length; i < ii; i++) + { + tokens = p_permissions[i].split(";"); + authorityId = tokens[1]; + role = tokens[2]; + + // Only return ALLOWED permissions + if (tokens[0] == "ALLOWED") + { + // Resolve to group or user as appropriate + if (authorityId.indexOf("GROUP_") === 0) + { + authority = Common.getGroup(authorityId); + } + else if (authorityId.indexOf("ROLE_") === 0) + { + authority = + { + avatar: null, + name: authorityId, + displayName: null + }; + nameProperty = "name"; + } + else + { + authority = Common.getPerson(authorityId); + } + + if (authority != null) + { + results.push( + { + authority: + { + avatar: authority.avatar || null, + name: authorityId, + displayName: authority["displayName"] + }, + role: role + }); + } + } + } + return results; +} + +/** + * Document List Component: permissions + */ +model.data = getPermissions(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.get.json.ftl new file mode 100644 index 0000000000..3608d62538 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.get.json.ftl @@ -0,0 +1,32 @@ +<#macro permissionsJSON permissions> + <#escape x as jsonUtils.encodeJSONString(x)> +[ + <#list permissions as perm> + { + "authority": + { + <#if perm.authority.avatar??> + "avatar": "${"api/node/" + perm.authority.avatar.nodeRef?string?replace('://','/') + "/content/thumbnails/avatar"}", + + "name": "${perm.authority.name}", + "displayName": "${perm.authority.displayName!perm.authority.name}" + }, + "role": "${perm.role}" + }<#if perm_has_next>, + +] + + + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "inherited": <@permissionsJSON data.inherited />, + "isInherited": ${data.isInherited?string}, + "canReadInherited": ${data.canReadInherited?string}, + "direct": <@permissionsJSON data.direct />, + "settable": + [ + <#list data.settable as settable>"${settable}"<#if settable_has_next>, + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.post.desc.xml new file mode 100644 index 0000000000..c5d8fdc1fd --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.post.desc.xml @@ -0,0 +1,9 @@ + + set-permissions + Document List Component - set permissions data webscript + /slingshot/doclib/permissions/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.post.json.ftl new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.post.json.ftl @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.post.json.js new file mode 100644 index 0000000000..54e36ff786 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/permissions.post.json.js @@ -0,0 +1,104 @@ + + +/** + * Entry point for rmpermissions POST data webscript. + * Applies supplied permissions to a node. + * + * @method main + */ +function main() +{ + /** + * 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, + node = ParseArgs.resolveNode(nodeRef); + + if (node == null) + { + node = search.findNode(nodeRef); + if (node === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Not a valid nodeRef: '" + nodeRef + "'"); + return null; + } + } + + var location = Common.getLocation (node); + var siteManagerAuthority = "GROUP_site_" + location.site + "_SiteManager"; + + if (json.has("permissions") == false) + { + status.setCode(status.STATUS_BAD_REQUEST, "Permissions value missing from request."); + } + + // Inherited permissions flag + // First set inherit and then modify permissions + // See MNT-11725 + if (json.has("isInherited")) + { + var isInherited = json.getBoolean("isInherited"); + if (location.site != null) + { + if (isInherited == false) + { + // Insure Site Managers can still manage content. + node.setPermission("SiteManager", siteManagerAuthority); + } + } + node.setInheritsPermissions(isInherited); + } + + var permissions = json.getJSONArray("permissions"); + var isInherited = json.getBoolean("isInherited"); + for (var i = 0; i < permissions.length(); i++) + { + var perm = permissions.getJSONObject(i); + + // collect values for the permission setting + var authority = perm.getString("authority"); + + var role = perm.getString("role"); + var remove = false; + if (perm.has("remove")) + { + remove = perm.getBoolean("remove"); + } + + // Apply or remove permission + if (remove) + { + // Prevent the removal of the SiteManager group authority + if (!(role == "SiteManager" && authority == siteManagerAuthority && !isInherited)) + { + node.removePermission(role, authority); + } + } + else + { + var isSpecialAuthority = false; + switch ("" + authority) + { + case "GROUP_EVERYONE": + case "ROLE_ADMINISTRATOR": + case "ROLE_GUEST": + case "ROLE_OWNER": + isSpecialAuthority = true; + break; + } + + if (!isSpecialAuthority && people.getGroup(authority) == null && people.getPerson(authority) == null) + { + // ACE-3280: silently not add non-existent users + return; + } + + node.setPermission(role, authority); + } + } +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/treenode.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/treenode.get.desc.xml new file mode 100644 index 0000000000..a9d3f79aa7 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/treenode.get.desc.xml @@ -0,0 +1,12 @@ + + treenode + Document List Component - treenode data webscript + /slingshot/doclib/treenode/site/{site}/{container}/{path} + /slingshot/doclib/treenode/site/{site}/{container} + /slingshot/doclib/treenode/node/{store_type}/{store_id}/{id}/{path} + /slingshot/doclib/treenode/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/treenode.get.html.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/treenode.get.html.ftl new file mode 100644 index 0000000000..00df6880d2 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/treenode.get.html.ftl @@ -0,0 +1 @@ +<#include "treenode.get.json.ftl"> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/treenode.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/treenode.get.js new file mode 100644 index 0000000000..3f62a2c723 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/treenode.get.js @@ -0,0 +1,82 @@ + + +/** + * Document List Component: treenode + */ +model.treenode = getTreeNode(); + +/* Create collection of folders in the given space */ +function getTreeNode() +{ + try + { + if (url.templateArgs.site != undefined) + { + var siteId = url.templateArgs.site; + var site = siteService.getSite(siteId); + if (site && site.visibility != "PUBLIC" && !site.isMember(person.properties.userName) && !people.isAdmin(person)) + { + status.setCode(status.STATUS_FORBIDDEN, "User is not a member of the " + siteId + " site"); + return null; + } + } + var items = new Array(), + hasSubfolders = true, + ignoredTypes = ['fm:forum','fm:topic'], + evalChildFolders = args["children"] !== "false", + resultsTrimmed = false, + argMax = parseInt(args["max"], 10), + maxItems = isNaN(argMax) ? -1 : argMax, + maxNumChildren = 100; + + // Use helper function to get the arguments + var parsedArgs = ParseArgs.getParsedArgs(); + if (parsedArgs === null) + { + return; + } + + // Look for folders in the pathNode - sort by ascending name + var pagedResult = parsedArgs.pathNode.childFileFolders(false, true, ignoredTypes, 0, maxItems, 0, "cm:name", true, "TODO"); + + if (pagedResult.hasMoreItems() == true) + { + resultsTrimmed = true; + } + + var numChildren = 1; + + for each (item in pagedResult.page) + { + numChildren++; + if (numChildren == maxNumChildren) + { + evalChildFolders = false; + } + + if (evalChildFolders) + { + hasSubfolders = item.childFileFolders(false, true, ignoredTypes, 1).page.length > 0; + } + + items.push( + { + node: item, + hasSubfolders: hasSubfolders, + aspects: item.aspectsShort + }); + } + + return ( + { + parent: parsedArgs.pathNode, + resultsTrimmed: resultsTrimmed, + items: items + }); + } + catch(e) + { + status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, e.toString()); + return; + } +} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/treenode.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/treenode.get.json.ftl new file mode 100644 index 0000000000..67e7d0fa65 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/treenode.get.json.ftl @@ -0,0 +1,41 @@ +<#assign p = treenode.parent> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalResults": ${treenode.items?size?c}, + "resultsTrimmed": ${treenode.resultsTrimmed?string}, + "parent": + { + "nodeRef": "${p.nodeRef}", + "userAccess": + { + "create": ${p.hasPermission("CreateChildren")?string}, + "edit": ${p.hasPermission("Write")?string}, + "delete": ${p.hasPermission("Delete")?string} + } + }, + "items": + [ + <#list treenode.items as item> + <#assign t = item.node> + { + "nodeRef": "${t.nodeRef}", + "name": "${t.name}", + "description": "${(t.properties.description!"")}", + "hasChildren": ${item.hasSubfolders?string}, + "userAccess": + { + "create": ${t.hasPermission("CreateChildren")?string}, + "edit": ${t.hasPermission("Write")?string}, + "delete": ${t.hasPermission("Delete")?string} + }, + "aspects": + [ + <#list item.aspects as aspect> + "${aspect}"<#if aspect_has_next>, + + ] + }<#if item_has_next>, + + ] +} + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/type.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/type.post.desc.xml new file mode 100644 index 0000000000..321a1099d7 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/type.post.desc.xml @@ -0,0 +1,9 @@ + + type + Document List Component - type submit + /slingshot/doclib/type/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/type.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/type.post.json.ftl new file mode 100644 index 0000000000..efdd8b6641 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/type.post.json.ftl @@ -0,0 +1,5 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "current": "${currentType}" +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/type.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/type.post.json.js new file mode 100644 index 0000000000..de45ed720d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/type.post.json.js @@ -0,0 +1,33 @@ +function main() +{ + // nodeRef input + var storeType = url.templateArgs.store_type, + storeId = url.templateArgs.store_id, + id = url.templateArgs.id, + nodeRef = storeType + "://" + storeId + "/" + id; + + var node = search.findNode(nodeRef); + if (node === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Not a valid nodeRef: '" + nodeRef + "'"); + return null; + } + + if (!json.has("type")) + { + status.setCode(status.STATUS_BAD_REQUEST, "'type' parameter not supplied"); + return null; + } + + var type = json.get("type"); + + if (!node.specializeType(type)) + { + status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Could not change type of nodeRef '" + nodeRef + "' to '" + type + "'"); + return null; + } + + model.currentType = type; +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/download.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/download.get.desc.xml new file mode 100644 index 0000000000..bdac35686c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/download.get.desc.xml @@ -0,0 +1,11 @@ + + DownloadContent + Slingshot download content webscript - posts an activity for Site content download then delegates to standard ContentGet implementation + /slingshot/node/content{property}/{store_type}/{store_id}/{id}?a={attach?} + /slingshot/node/{store_type}/{store_id}/{id}/content{property}?a={attach?} + /slingshot/node/{store_type}/{store_id}/{id}/content{property}/{filename}?a={attach?} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/edit-metadata/node-type.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/edit-metadata/node-type.get.desc.xml new file mode 100644 index 0000000000..8333dacbe9 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/edit-metadata/node-type.get.desc.xml @@ -0,0 +1,9 @@ + + contnode-typeainer + Edit Metadata Manager Component - node type + /slingshot/edit-metadata/node/{store_type}/{store_id}/{id} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/edit-metadata/node-type.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/edit-metadata/node-type.get.js new file mode 100644 index 0000000000..fc2aaebd5c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/edit-metadata/node-type.get.js @@ -0,0 +1,30 @@ +function main() +{ + if (url.templateArgs.store_type === null) + { + status.setCode(status.STATUS_BAD_REQUEST, "NodeRef missing"); + return; + } + + // nodeRef input + var storeType = url.templateArgs.store_type, + storeId = url.templateArgs.store_id, + id = url.templateArgs.id, + nodeRef = storeType + "://" + storeId + "/" + id, + node = search.findNode(nodeRef); + + if (node === null) + { + status.setCode(status.STATUS_NOT_FOUND, "Not a valid nodeRef: '" + nodeRef + "'"); + return null; + } + + model.node = node; + + if (node.parent !== null && node.parent.hasPermission("ReadProperties")) + { + model.parent = node.parent; + } +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/edit-metadata/node-type.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/edit-metadata/node-type.get.json.ftl new file mode 100644 index 0000000000..3c40abf083 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/edit-metadata/node-type.get.json.ftl @@ -0,0 +1,15 @@ +<#macro nodeInfo node name> +<#escape x as jsonUtils.encodeJSONString(x)> + "${name}": + { + "nodeRef": "${node.nodeRef}", + "type": "${node.typeShort}", + "isContainer": ${node.isContainer?string}, + "fileName": "${node.name}" + } + + +{ + <#if parent??><@nodeInfo parent "parent" />, + <@nodeInfo node "node" /> +} diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/node-browser/node-browser.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/node-browser/node-browser.get.desc.xml new file mode 100644 index 0000000000..8de44e800c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/node-browser/node-browser.get.desc.xml @@ -0,0 +1,11 @@ + + Node Browser Repository support + Execute searches, return information on a node or return a list of stores + /slingshot/node/{protocol}/{store}/{id} + /slingshot/node/search + /slingshot/node/stores + argument + admin + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/node-browser/node-browser.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/node-browser/node-browser.get.json.ftl new file mode 100644 index 0000000000..46b2343f69 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/node-browser/node-browser.get.json.ftl @@ -0,0 +1,209 @@ +<#macro dateFormat date>${date?string("dd MMM yyyy HH:mm:ss 'GMT'Z '('zzz')'")} +<#escape x as jsonUtils.encodeJSONString(x)> +<#macro printPropertyValue p> + <#if p.value??> + <#if p.value?is_date> + "<@dateFormat p.value />" + <#elseif p.value?is_boolean> + ${p.value?string} + <#elseif p.value?is_number> + ${p.value?c} + <#elseif p.value?is_string> + "${p.value}" + <#elseif p.value?is_hash> + <#assign result = "{"/> + <#assign first = true /> + <#list p.value?keys as key> + <#if first = false> + <#assign result = result + ", "/> + + <#assign result = result + "${key} = ${p.value[key]}" /> + <#assign first = false/> + + <#assign result = result + "}"/> + <#-- output the result --> + "${result}" + + <#else> + null + + +{ + <#if node??> + "nodeRef": "${node.nodeRef}", + "qnamePath": { + "name": "${node.qnamePath}", + "prefixedName": "${node.prefixedQNamePath}" + }, + "name": { + "name": "${node.name}", + "prefixedName": "${node.prefixedName}" + }, + "parentNodeRef": "<#if node.parentNodeRef?exists>${node.parentNodeRef}", + "type": { + "name": "${node.type.name}", + "prefixedName": "${node.type.prefixedName}" + }, + "id": "${node.id}", + "nodeRef": "${node.nodeRef}", + "aspects": [ + <#list aspects as aspect> + { + "name": "${aspect.name}", + "prefixedName": "${aspect.prefixedName}" + } + <#if aspect_has_next>, + + ], + "properties": [ + <#list properties as p> + { + "name": { + "name": "${p.name.name}", + "prefixedName": "${p.name.prefixedName}" + }, + "values": [ + <#list p.values as val> + { + "dataType": "${val.dataType!""}", + "value": <@printPropertyValue val />, + "isContent": ${val.content?string}, + "isNodeRef": ${val.nodeRef?string}, + "isNullValue": ${val.nullValue?string} + }<#if val_has_next>, + + ], + "type": { + "name": "<#if p.typeName??>${p.typeName.name}", + "prefixedName": "<#if p.typeName??>${p.typeName.prefixedName}" + }, + "multiple": ${p.collection?string}, + "residual": ${p.residual?string} + }<#if p_has_next>, + + ], + "children": [ + <#list children as child> + { + "name": { + "name": "${child.name.name}", + "prefixedName": "${child.name.prefixedName}" + }, + "nodeRef": "${child.childRef}", + "type": { + "name": "${child.childTypeName.name}", + "prefixedName": "${child.childTypeName.prefixedName}" + }, + "assocType": { + "name": "${child.typeName.name}", + "prefixedName": "${child.typeName.prefixedName}" + }, + "primary": ${child.primary?string}, + "index": ${child_index?c} + }<#if child_has_next>, + + ], + "parents": [ + <#list parents as p> + { + "name": { + "name": "${p.name.name}", + "prefixedName": "${p.name.prefixedName}" + }, + "nodeRef": "${p.parentRef}", + "type": { + "name": "${p.parentTypeName.name}", + "prefixedName": "${p.parentTypeName.prefixedName}" + }, + "assocType": { + "name": "${p.typeName.name}", + "prefixedName": "${p.typeName.prefixedName}" + }, + "primary": ${p.primary?string} + }<#if p_has_next>, + + ], + "assocs": [ + <#list assocs as assoc> + { + "type": { + "name": "${assoc.targetTypeName.name}", + "prefixedName": "${assoc.targetTypeName.prefixedName}" + }, + "sourceRef": "${assoc.sourceRef}", + "targetRef": "${assoc.targetRef}", + "assocType": { + "name": "${assoc.typeName.name}", + "prefixedName": "${assoc.typeName.prefixedName}" + } + }<#if assoc_has_next>, + + ], + "sourceAssocs": [ + <#if sourceAssocs??> + <#list sourceAssocs as assoc> + { + "type": { + "name": "${assoc.sourceTypeName.name}", + "prefixedName": "${assoc.sourceTypeName.prefixedName}" + }, + "sourceRef": "${assoc.sourceRef}", + "targetRef": "${assoc.targetRef}", + "assocType": { + "name": "${assoc.typeName.name}", + "prefixedName": "${assoc.typeName.prefixedName}" + } + }<#if assoc_has_next>, + + + ], + "permissions": { + "entries": [ + <#list permissions.entries as p> + { + "permission": "${p.permission}", + "authority": "${p.authority}", + "rel": "${p.accessStatus}" + }<#if p_has_next>, + + ], + "masks": [ + <#list permissions.storePermissions as p> + { + "permission": "${p.permission}", + "authority": "${p.authority}", + "rel": "${p.accessStatus}" + }<#if p_has_next>, + + ], + "inherit": ${permissions.inherit?string}, + "owner": "<#if permissions.owner?exists>${permissions.owner}" + } + <#elseif results??> + "numResults": ${results?size?c}, + "results": [ + <#list results as result> + <#assign qnamePath=result.qnamePath /> + { + "nodeRef": "${result.nodeRef}", + "qnamePath": { + "name": "${result.qnamePath}", + "prefixedName": "${result.prefixedQNamePath}" + }, + "name": { + "name": "${result.name}", + "prefixedName": "${result.prefixedName}" + }, + "parentNodeRef": "<#if result.parent??>${result.parent.nodeRef}" + }<#if result_has_next>, + + ], + "searchElapsedTime": ${(searchElapsedTime!0)?c} + <#elseif stores??> + "stores": [ + <#list stores as store>"${store}"<#if store_has_next>, + + ] + +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/avatar.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/avatar.get.desc.xml new file mode 100644 index 0000000000..e20c0bc1bf --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/avatar.get.desc.xml @@ -0,0 +1,16 @@ + + Avatar + + Returns a user avatar image in the format specified by the thumbnailname, or the "avatar" preset if omitted. + + /slingshot/profile/avatar/avatar + /slingshot/profile/avatar/avatar/thumbnail/{thumbnailname} + /slingshot/profile/avatar/{store_type}/{store_id}/{id}/thumbnail/{thumbnailname} + /slingshot/profile/avatar/{store_type}/{store_id}/{id} + /slingshot/profile/avatar/{username}/thumbnail/{thumbnailname} + /slingshot/profile/avatar/{username} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/avatar.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/avatar.get.js new file mode 100644 index 0000000000..f4b278a1aa --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/avatar.get.js @@ -0,0 +1,104 @@ +/** + * User Profile - User avatar GET method + * + * Returns a user avatar image in the format specified by the thumbnailname, or the "avatar" preset if omitted. + * + * @method GET + */ + +function getPlaceholder(thumbnailName) +{ + // Try and get the place holder resource for a png avatar. + var phPath = thumbnailService.getMimeAwarePlaceHolderResourcePath(thumbnailName, "images/png"); + if (phPath == null) + { + // 404 since no thumbnail was found + status.setCode(status.STATUS_NOT_FOUND, "Thumbnail was not found and no place holder resource set for '" + thumbnailName + "'"); + return; + } + + return phPath; +} + +function main() +{ + var userName = url.templateArgs.username, + thumbnailName = url.templateArgs.thumbnailname || "avatar", + avatarNode; + + // If there is no store type, store id or id on the request then this WebScript has most likely been requested + // for a user with no avatar image so we will just return the placeholder image. + if (userName == null && url.templateArgs.store_type == null && url.templateArgs.store_id == null && url.templateArgs.id == null) + { + // If there is no userName or nodeRef data then we want to return the browser cacheable placeholder... + model.contentPath = getPlaceholder(thumbnailName); + model.allowBrowserToCache = "true"; + return; + } + else if (url.templateArgs.store_type == null && url.templateArgs.store_id == null && url.templateArgs.id == null) + { + // There is no nodeRef data but there is a username... this should return the user image that needs revalidation + var person = people.getPerson(userName); + if (person == null) + { + // Stream the placeholder image + model.contentPath = getPlaceholder(thumbnailName); + return; + } + else + { + // Retrieve the avatar NodeRef for this person, if there is one. + var avatarAssoc = person.assocs["cm:avatar"]; + if (avatarAssoc != null) + { + avatarNode = avatarAssoc[0]; + } + } + } + else if (userName == null) + { + // There is no user name but there is nodeREf data... this should return the image that CAN be cached by the browser + model.allowBrowserToCache = "true"; + avatarNode = search.findNode(url.templateArgs.store_type + "://" + url.templateArgs.store_id + "/" + url.templateArgs.id); + if (avatarNode == null) + { + // Stream the placeholder image if the avatar node cannot be found. + model.contentPath = getPlaceholder(thumbnailName); + return; + } + } + + // Get the thumbnail for the avatar... + if (avatarNode != null) + { + // Get the thumbnail + var thumbnail = avatarNode.getThumbnail(thumbnailName); + if (thumbnail == null || thumbnail.size == 0) + { + // Remove broken thumbnail + if (thumbnail != null) + { + thumbnail.remove(); + } + + // Force the creation of the thumbnail + thumbnail = avatarNode.createThumbnail(thumbnailName, false); + if (thumbnail != null) + { + model.contentNode = thumbnail; + return; + } + } + else + { + // Place the details of the thumbnail into the model, this will be used to stream the content to the client + model.contentNode = thumbnail; + return; + } + } + + // Stream the placeholder image + model.contentPath = getPlaceholder(thumbnailName); +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put.desc.xml new file mode 100644 index 0000000000..ce0d21af7a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put.desc.xml @@ -0,0 +1,9 @@ + + Reset Avatar image + User Profile - Reset Avatar image for a user + argument + user + required + /slingshot/profile/resetavatar/{userName} + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put.json.ftl new file mode 100644 index 0000000000..c85c20f2b4 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put.json.ftl @@ -0,0 +1,3 @@ +{ + "success": ${success?string} +} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put.json.js new file mode 100644 index 0000000000..865b3fd075 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put.json.js @@ -0,0 +1,46 @@ +/** + * User Profile - Reset user avatar REST method + * + * Current user can only modify their own settings or an admin can reset all. + * + * @method PUT + */ + +function main() +{ + // Get the person details and ensure they exist for update + var userName = url.extension; + var user = people.getPerson(userName); + if (user == null) + { + status.setCode(status.STATUS_NOT_FOUND, "Person " + userName + " does not exist"); + return; + } + + // ensure we found a valid user and that it is the current user or we are an admin + if (user == null || + (people.isAdmin(person) == false && user.properties.userName != person.properties.userName)) + { + status.code = 500; + status.message = msg.get("error.failed"); + status.redirect = true; + return; + } + + // remove old image child node if we have one + var assocs = user.childAssocs["cm:preferenceImage"]; + if (assocs != null && assocs.length == 1) + { + assocs[0].remove(); + } + // remove 'cm:avatar' target association - backward compatible with JSF web-client avatar + assocs = user.associations["cm:avatar"]; + if (assocs != null && assocs.length == 1) + { + user.removeAssociation(assocs[0], "cm:avatar"); + } + + model.success = true; +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put.properties new file mode 100644 index 0000000000..ac208d39b4 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put.properties @@ -0,0 +1 @@ +error.failed=Failed to locate user to modify or permission denied. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_de.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_de.properties new file mode 100644 index 0000000000..3da6c6f179 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_de.properties @@ -0,0 +1 @@ +error.failed=Zu bearbeitender Benutzer nicht gefunden oder Zugriff verweigert. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_es.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_es.properties new file mode 100644 index 0000000000..9c67ce983f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_es.properties @@ -0,0 +1 @@ +error.failed=No se pudo localizar al usuario a modificar o permiso denegado. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_fr.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_fr.properties new file mode 100644 index 0000000000..18246faca4 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_fr.properties @@ -0,0 +1 @@ +error.failed=Impossible de situer l'utilisateur \u00e0 modifier ou permission refus\u00e9e. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_it.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_it.properties new file mode 100644 index 0000000000..13365a9176 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_it.properties @@ -0,0 +1 @@ +error.failed=Impossibile individuare l'utente da modificare o permesso negato. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_ja.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_ja.properties new file mode 100644 index 0000000000..12aaa6d939 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_ja.properties @@ -0,0 +1 @@ +error.failed=\u5909\u66f4\u3059\u308b\u30e6\u30fc\u30b6\u30fc\u304c\u898b\u3064\u304b\u3089\u306a\u3044\u304b\u3001\u6a29\u9650\u304c\u62d2\u5426\u3055\u308c\u307e\u3057\u305f\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_nb.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_nb.properties new file mode 100644 index 0000000000..55649277a8 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_nb.properties @@ -0,0 +1 @@ +error.failed=Fant ikke bruker som skulle endres eller tillatelse ikke gitt. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_nl.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_nl.properties new file mode 100644 index 0000000000..b931e99a32 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_nl.properties @@ -0,0 +1 @@ +error.failed=Kan de gebruiker die moet worden bijgewerkt niet vinden of toestemming is geweigerd. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_pt_BR.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_pt_BR.properties new file mode 100644 index 0000000000..526edc39ab --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_pt_BR.properties @@ -0,0 +1 @@ +error.failed=Falha ao localizar o usu\u00e1rio para modifica\u00e7\u00e3o ou permiss\u00e3o negada. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_ru.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_ru.properties new file mode 100644 index 0000000000..a898d5ef11 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_ru.properties @@ -0,0 +1 @@ +error.failed=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_zh_CN.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_zh_CN.properties new file mode 100644 index 0000000000..bab2eb1aeb --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/resetavatar.put_zh_CN.properties @@ -0,0 +1 @@ +error.failed=\u65e0\u6cd5\u627e\u5230\u8981\u4fee\u6539\u7684\u7528\u6237\u6216\u6743\u9650\u88ab\u62d2\u7edd\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.desc.xml new file mode 100644 index 0000000000..d6a0280f72 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.desc.xml @@ -0,0 +1,9 @@ + + Avatar Upload + Upload avatar file content and apply to person preferences + + user + required + /slingshot/profile/uploadavatar + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.html.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.html.ftl new file mode 100644 index 0000000000..91cac6770a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.html.ftl @@ -0,0 +1,15 @@ + + + Upload Avatar Success + + +<#if (args.success!"")?matches("^[\\w\\d\\._]+$")> + + + + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.html.status.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.html.status.ftl new file mode 100644 index 0000000000..41659be481 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.html.status.ftl @@ -0,0 +1,19 @@ + + + Upload Avatar Failure + + +<#if (args.failure!"")?matches("^[\\w\\d\\._]+$")> + + + + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.js new file mode 100644 index 0000000000..7ab9363bb8 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.js @@ -0,0 +1,115 @@ +/** + * User Profile Avatar Upload method + * + * @method POST + * @param username {string} + * filedata {file} + */ + +function main() +{ + try + { + var filename = null; + var content = null; + var username = null; + + // locate file attributes + for each (field in formdata.fields) + { + if (field.name == "filedata" && field.isFile) + { + filename = field.filename; + content = field.content; + } + else if (field.name == "username") + { + username = field.value; + } + } + + // ensure all mandatory attributes have been located + if (filename == undefined || content == undefined) + { + status.code = 400; + status.message = msg.get("error.noFile"); + status.redirect = true; + return; + } + if (username == null || username.length == 0) + { + status.code = 500; + status.message = msg.get("error.noUsername"); + status.redirect = true; + return; + } + + var user = people.getPerson(username); + // ensure we found a valid user and that it is the current user or we are an admin + if (user == null || + (people.isAdmin(person) == false && user.properties.userName != person.properties.userName)) + { + status.code = 500; + status.message = msg.get("error.user"); + status.redirect = true; + return; + } + + // ensure cm:person has 'cm:preferences' aspect applied - as we want to add the avatar as + // the child node of the 'cm:preferenceImage' association + if (!user.hasAspect("cm:preferences")) + { + user.addAspect("cm:preferences"); + } + + // remove old image child node if we already have one + var assocs = user.childAssocs["cm:preferenceImage"]; + if (assocs != null && assocs.length == 1) + { + assocs[0].remove(); + } + + // create the new image node + var image = user.createNode(filename, "cm:content", "cm:preferenceImage"); + image.properties.content.write(content); + image.properties.content.guessMimetype(filename); + + if (image.properties.content.getMimetype().indexOf("image/") != 0) + { + user.removeNode(image); + status.code = 500; + status.message = msg.get("error.notImage"); + status.redirect = true; + return; + } + + image.properties.content.encoding = "UTF-8"; + image.save(); + + // wire up 'cm:avatar' target association - backward compatible with JSF web-client avatar + assocs = user.associations["cm:avatar"]; + if (assocs != null && assocs.length == 1) + { + user.removeAssociation(assocs[0], "cm:avatar"); + } + user.createAssociation(image, "cm:avatar"); + + // save ref to be returned + model.image = image; + } + catch (e) + { + var x = e; + status.code = 500; + status.message = msg.get("error.unexpected"); + if(x.message && x.message.indexOf("org.alfresco.service.cmr.usage.ContentQuotaException") == 0) + { + status.code = 413; + status.message = x.message; + } + status.redirect = true; + return; + } +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.json.ftl new file mode 100644 index 0000000000..c12892eb08 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.json.ftl @@ -0,0 +1,12 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "nodeRef": "${image.nodeRef}", + "fileName": "${image.name}", + "status": + { + "code": 200, + "name": "OK", + "description" : "File uploaded successfully" + } +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.properties new file mode 100644 index 0000000000..79a62d98ea --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post.properties @@ -0,0 +1,5 @@ +error.noUsername=Username parameter not supplied. +error.user=Failed to locate user to modify or permission denied. +error.notImage=Only image files are allowed for user avatar. +error.unexpected=Unexpected error occurred during upload of new content. +error.noFile=Uploaded file cannot be located in request. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_de.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_de.properties new file mode 100644 index 0000000000..df035d05b3 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_de.properties @@ -0,0 +1,5 @@ +error.noUsername=Benutzername-Parameter nicht angegeben. +error.user=Zu bearbeitender Benutzer nicht gefunden oder Zugriff verweigert. +error.notImage=Als Benutzeravatar sind nur Bilddateien zul\u00e4ssig. +error.unexpected=Unerwarteter Fehler beim Hochladen von neuem Inhalt. +error.noFile=Hochgeladene Datei nicht in Anfrage gefunden. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_es.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_es.properties new file mode 100644 index 0000000000..14fd5f035e --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_es.properties @@ -0,0 +1,5 @@ +error.noUsername=Par\u00e1metro de usuario no suministrado. +error.user=No se pudo localizar al usuario a modificar o permiso denegado. +error.notImage=Solo se permiten ficheros de im\u00e1genes para el avatar de usuario. +error.unexpected=Se ha producido un error inesperado durante la carga de nuevo contenido. +error.noFile=El fichero cargado no puede localizarse en los datos enviados al servidor. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_fr.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_fr.properties new file mode 100644 index 0000000000..83d2fb7bac --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_fr.properties @@ -0,0 +1,5 @@ +error.noUsername=Le param\u00e8tre de nom d'utilisateur n'a pas \u00e9t\u00e9 fourni. +error.user=Impossible de situer l'utilisateur \u00e0 modifier ou permission refus\u00e9e. +error.notImage=Seuls les fichiers image sont autoris\u00e9s pour l'avatar de l'utilisateur. +error.unexpected=Une erreur inattendue s'est produite lors de l'importation de nouveau contenu. +error.noFile=Impossible de trouver dans les donn\u00e9es le fichier import\u00e9. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_it.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_it.properties new file mode 100644 index 0000000000..6c63970583 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_it.properties @@ -0,0 +1,5 @@ +error.noUsername=Parametro nome utente non specificato. +error.user=Impossibile individuare l'utente da modificare o permesso negato. +error.notImage=Sono ammessi solo file di immagine per l'avatar utente. +error.unexpected=Si \u00e8 verificato un errore imprevisto durante il caricamento del nuovo contenuto. +error.noFile=Impossibile trovare il file caricato nei dati inviati al server. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_ja.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_ja.properties new file mode 100644 index 0000000000..a346bf6f4b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_ja.properties @@ -0,0 +1,5 @@ +error.noUsername=\u30e6\u30fc\u30b6\u30fc\u540d\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +error.user=\u5909\u66f4\u3059\u308b\u30e6\u30fc\u30b6\u30fc\u304c\u898b\u3064\u304b\u3089\u306a\u3044\u304b\u3001\u6a29\u9650\u304c\u62d2\u5426\u3055\u308c\u307e\u3057\u305f\u3002 +error.notImage=\u30e6\u30fc\u30b6\u30fc\u306e\u30a2\u30d0\u30bf\u30fc\u306b\u4f7f\u7528\u3067\u304d\u308b\u306e\u306f\u753b\u50cf\u30d5\u30a1\u30a4\u30eb\u306e\u307f\u3067\u3059\u3002 +error.unexpected=\u65b0\u3057\u3044\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u4e2d\u306b\u60f3\u5b9a\u5916\u306e\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 +error.noFile=\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u304c\u30c7\u30fc\u30bf\u306e\u4e2d\u306b\u3042\u308a\u307e\u305b\u3093\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_nb.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_nb.properties new file mode 100644 index 0000000000..bf5389c288 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_nb.properties @@ -0,0 +1,5 @@ +error.noUsername=Brukernavnparameter ikke oppgitt. +error.user=Fant ikke bruker som skulle endres eller tillatelse ikke gitt. +error.notImage=Kun bildefiler tillates til bruker-avatar. +error.unexpected=Uventet feil oppstod under opplasting av nytt innhold. +error.noFile=Finner ikke opplastede fil fra foresp\u00f8rselen. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_nl.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_nl.properties new file mode 100644 index 0000000000..e7448b271b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_nl.properties @@ -0,0 +1,5 @@ +error.noUsername=Parameter gebruikersnaam is niet opgegeven. +error.user=Kan de gebruiker die moet worden bijgewerkt niet vinden of toestemming is geweigerd. +error.notImage=Alleen afbeeldingsbestanden zijn toegestaan voor de avatar van de gebruiker. +error.unexpected=Er is een onverwachte fout opgetreden bij het uploaden van nieuwe content. +error.noFile=Kan het ge\u00fcploade bestand niet vinden in de aanvraag. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_pt_BR.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_pt_BR.properties new file mode 100644 index 0000000000..c02963597a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_pt_BR.properties @@ -0,0 +1,5 @@ +error.noUsername=Par\u00e2metro do nome de usu\u00e1rio n\u00e3o fornecido. +error.user=Falha ao localizar o usu\u00e1rio para modifica\u00e7\u00e3o ou permiss\u00e3o negada. +error.notImage=Apenas arquivos de imagem s\u00e3o permitidos para o avatar do usu\u00e1rio. +error.unexpected=Um erro inesperado ocorreu durante o carregamento do novo conte\u00fado. +error.noFile=O arquivo carregado n\u00e3o pode ser localizado no pedido. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_ru.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_ru.properties new file mode 100644 index 0000000000..3cd2680f02 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_ru.properties @@ -0,0 +1,5 @@ +error.noUsername=\u041d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0438\u043c\u0435\u043d\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. +error.user=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d. +error.notImage=\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0432\u0430\u0442\u0430\u0440\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0444\u0430\u0439\u043b\u044b \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439. +error.unexpected=\u0412 \u0445\u043e\u0434\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430. +error.noFile=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0435. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_zh_CN.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_zh_CN.properties new file mode 100644 index 0000000000..088d0b9463 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/uploadavatar.post_zh_CN.properties @@ -0,0 +1,5 @@ +error.noUsername=\u672a\u63d0\u4f9b\u7528\u6237\u540d\u53c2\u6570\u3002 +error.user=\u65e0\u6cd5\u627e\u5230\u8981\u4fee\u6539\u7684\u7528\u6237\u6216\u6743\u9650\u88ab\u62d2\u7edd\u3002 +error.notImage=\u4ec5\u56fe\u50cf\u6587\u4ef6\u53ef\u7528\u4f5c\u7528\u6237\u6807\u8bc6\u56fe\u3002 +error.unexpected=\u4e0a\u4f20\u65b0\u5185\u5bb9\u671f\u95f4\u53d1\u751f\u610f\u5916\u9519\u8bef\u3002 +error.noFile=\u65e0\u6cd5\u5728\u8bf7\u6c42\u6570\u636e\u4e2d\u627e\u5230\u4e0a\u4f20\u7684\u6587\u4ef6\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/usercontents.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/usercontents.get.desc.xml new file mode 100644 index 0000000000..a807ece184 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/usercontents.get.desc.xml @@ -0,0 +1,9 @@ + + Last edited user contents + Last edited user contents + + guest + /slingshot/profile/usercontents + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/usercontents.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/usercontents.get.js new file mode 100644 index 0000000000..a491873c6b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/usercontents.get.js @@ -0,0 +1,90 @@ + + +var maxResults = (args.maxResults !== undefined) ? parseInt(args.maxResults, 10) : DEFAULT_MAX_RESULTS; + +function padZeros(number) +{ + return (number < 10) ? '0' + number : number; +} + +function getContents(user, type) +{ + // set range to within last 28 days + var date = new Date(); + var toQuery = date.getFullYear() + "-" + padZeros((date.getMonth()+1)) + "-" + padZeros(date.getDate()); + date.setDate(date.getDate() - 28); + var fromQuery = date.getFullYear() + "-" + padZeros((date.getMonth()+1)) + "-" + padZeros(date.getDate()); + + var userProperty = (type == 'created') ? 'creator' : 'modifier'; + + var getBlogPostsQuery = function getBlogPosts() + { + return 'PATH:"/app:company_home/st:sites/*/cm:blog/*" ' + + 'AND +TYPE:"cm:content" ' + + 'AND +@cm:' + userProperty + ':"' + user + '" ' + + 'AND +@cm:' + type + ':["' + fromQuery + '" TO "' + toQuery + '"]'; + }; + + var getWikiPagesQuery = function getWikiPagesQuery() + { + return 'PATH:"/app:company_home/st:sites/*/cm:wiki/*" ' + + 'AND +TYPE:"cm:content" ' + + 'AND +@cm:' + userProperty + ':"' + user + '" ' + + 'AND +@cm:' + type + ':["' + fromQuery + '" TO "' + toQuery + '"]'; + }; + + var getDiscussionsQuery = function getDiscussionsQuery() + { + return 'PATH:"/app:company_home/st:sites/*/cm:discussions//*" ' + + 'AND +TYPE:"fm:post" ' + + 'AND +@cm:' + userProperty + ':"' + user + '" ' + + 'AND +@cm:' + type + ':["' + fromQuery + '" TO "' + toQuery + '"]'; + }; + + var getDocumentsQuery = function getDocumentsQuery() + { + return 'TYPE:"cm:content" ' + + 'AND +@cm:' + userProperty + ':"' + user + '" ' + + 'AND +@cm:' + type + ':["' + fromQuery + '" TO "' + toQuery + '"] AND -QNAME:comment\\-*'; + }; + + var sortColumns = []; + sortColumns.push( + { + column: "@" + utils.longQName("cm:" + type), + ascending: false + }); + + var queryDef = { + query: "", + language: "fts-alfresco", + page: {maxItems: maxResults}, + onerror: "no-results", + sort: sortColumns + }; + + // perform fts-alfresco language queries + var results; + queryDef.query = getBlogPostsQuery(); + results = search.query(queryDef); + queryDef.query = getWikiPagesQuery(); + results = results.concat(search.query(queryDef)); + queryDef.query = getDiscussionsQuery(); + results = results.concat(search.query(queryDef)); + queryDef.query = getDocumentsQuery(); + results = results.concat(search.query(queryDef)); + + results.sort(function(a, b) + { + var date1 = a.properties[type].getTime(), + date2 = b.properties[type].getTime(); + return (date1 < date2) ? 1 : (date1 > date2) ? -1 : 0; + } + ); + + return processResults(results, maxResults); +} + +model.data = []; +model.data['created'] = getContents(args.user, 'created', maxResults); +model.data['modified'] = getContents(args.user, 'modified', maxResults); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/usercontents.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/usercontents.get.json.ftl new file mode 100644 index 0000000000..5919dfb4ca --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/usercontents.get.json.ftl @@ -0,0 +1,37 @@ +<#macro dateFormat date>${date?string("yyyy-MM-dd'T'HH:mm:ss.SSSZ")} +<#macro formatDataItems data> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "items": + [ + <#list data.items as item> + { + "nodeRef": "${item.nodeRef}", + "type": "${item.type}", + "name": "${item.name!''}", + "displayName": "${item.displayName!''}", + "description": "${item.description!''}", + "createdOn": "<@dateFormat item.createdOn />", + "createdBy": "${item.createdBy!''}", + "createdByUser": "${item.createdByUser!''}", + "modifiedOn": "<@dateFormat item.modifiedOn />", + "modifiedByUser": "${item.modifiedByUser}", + "modifiedBy": "${item.modifiedBy}", + "size": ${item.size?c}, + <#if item.site??>"site": + { + "shortName": "${item.site.shortName}", + "title": "${item.site.title}" + }, + "container": "${item.container!""}", + "tags": [<#list item.tags as tag>"${tag}"<#if tag_has_next>,] + }<#if item_has_next>, + + ] +} + + +{ + "created": <@formatDataItems data['created'] />, + "modified": <@formatDataItems data['modified'] /> +} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userprofile.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userprofile.post.desc.xml new file mode 100644 index 0000000000..9380df58ec --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userprofile.post.desc.xml @@ -0,0 +1,9 @@ + + User Profile + User Profile POST for update + + user + required + /slingshot/profile/userprofile + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userprofile.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userprofile.post.json.ftl new file mode 100644 index 0000000000..c85c20f2b4 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userprofile.post.json.ftl @@ -0,0 +1,3 @@ +{ + "success": ${success?string} +} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userprofile.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userprofile.post.json.js new file mode 100644 index 0000000000..3fc220a1f2 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userprofile.post.json.js @@ -0,0 +1,91 @@ +/** + * User Profile REST Update method + * + * @method POST + * @param json {string} + * { + * username: "username", + * properties: + * { + * "cm:propname": "value" + * ... + * }, + * content: + * { + * "cm:contentpropname": "contentstringvalue" + * ... + * } + * } + */ + +function main() +{ + model.success = false; + var username = json.get("username"); + if (username == null) + { + status.code = 400; + status.message = msg.get("error.noUsername"); + status.redirect = true; + return; + } + + var user = people.getPerson(username); + // ensure we found a valid user and that it is the current user or we are an admin + if (user == null || + (people.isAdmin(person) == false && user.properties.userName != person.properties.userName)) + { + status.code = 500; + status.message = msg.get("error.user"); + status.redirect = true; + return; + } + + var immutableProperties = people.getImmutableProperties(username); + if (json.has("properties")) + { + var props = json.get("properties"); + if (props != null) + { + var names = props.names(); + for (var i=0; i + User Status + User Status POST for update + + user + required + /slingshot/profile/userstatus + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userstatus.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userstatus.post.json.ftl new file mode 100644 index 0000000000..9d91c6a79d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userstatus.post.json.ftl @@ -0,0 +1,9 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ +<#if success> + "userStatus": "${userStatus}", + "userStatusTime": { "iso8601": "${xmldate(userStatusTime)}"}, + + "success": ${success?string} +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userstatus.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userstatus.post.json.js new file mode 100644 index 0000000000..4786d850bc --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/profile/userstatus.post.json.js @@ -0,0 +1,39 @@ +/** + * User Status REST Update method + * + * @method POST + * @param json {string} + * { + * status: "value" + * } + */ + +function main() +{ + model.success = false; + + if (json.has("status")) + { + var newStatus = json.get("status"); + if (newStatus != null) + { + var statusTime = new Date(); + person.properties["cm:userStatus"] = newStatus; + person.properties["cm:userStatusTime"] = statusTime; + person.save(); + + model.success = true; + model.userStatus = newStatus; + model.userStatusTime = statusTime; + + if (newStatus.trim() != "") + { + var activity = {}; + activity.status = newStatus; + activities.postActivity("org.alfresco.profile.status-changed", null, "profile", jsonUtils.toJSONString(activity)); + } + } + } +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post.desc.xml new file mode 100644 index 0000000000..545be00a01 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post.desc.xml @@ -0,0 +1,10 @@ + + Create a Content Application Instance + Creates a new Content Application Instance + Content Applications + /remote-share/content-app-instance + + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post.js new file mode 100644 index 0000000000..3e2669a9fe --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post.js @@ -0,0 +1,100 @@ +function main() { + + // This query should find the Share Resources folder + var alfQuery = 'PATH:"/app:company_home/cm:ContentApps"'; + + var queryDef = { + query: alfQuery, + language: "fts-alfresco", + page: {maxItems: 50}, + templates: [] + }; + + var shareResources, + nodes = search.query(queryDef); + if (nodes.length > 0) + { + shareResources = nodes[0]; + + // Get the page name and JSON definition from the request parameters... + var valid = true; + var name = args.name; + if (name == null || name == "") + { + status.code = 500; + model.errorMessage = "appType.create.error.noNameProvided"; + return false; + } + + // Check to see if the page name is already in use... + alfQuery = 'TYPE:"{http://www.alfresco.org/model/surf/1.0}applicationInstance"' + + ' AND PATH:"/app:company_home/cm:ContentApps//*"' + + ' AND @cm\:name:"' + name + '"'; + queryDef.query = alfQuery; + var existingAppTypes = search.query(queryDef); + if (existingAppTypes.length == 1) + { + status.code = 500; + model.errorMessage = "appType.create.error.nameAlreadyUsed"; + model.errorMessageArg = name; + return false; + } + + var targetAppType = null; + var applicationType = args.applicationType; + if (applicationType == null || applicationType == "") + { + status.code = 500; + model.errorMessage = "appType.create.error.noAppTypeProvided"; + return false; + } + else + { + // Check that the requested application type exists... + alfQuery = 'TYPE:"{http://www.alfresco.org/model/surf/1.0}applicationType"' + + ' AND PATH:"/app:company_home/app:dictionary//*"' + + ' AND @cm\:name:"' + applicationType + '"'; + queryDef.query = alfQuery; + var existingAppTypes = search.query(queryDef); + if (existingAppTypes.length == 0) + { + status.code = 500; + model.errorMessage = "appType.create.error.appTypeDoesNotExist"; + return false; + } + else + { + targetAppType = existingAppTypes[0]; + } + } + + // Get the page name and it's content... + var doc = shareResources.createNode(name, "surf:applicationInstance"); + if (doc == null) + { + status.code = 500; + model.errorMessage = "appType.create.error.couldNotCreate"; + return false; + } + else + { + doc.createAssociation(targetAppType, "surf:applicationType"); + model.nodeRef = doc.nodeRef.toString(); + return true; + } + } + else + { + // The Data Dictionary location for pages hasn't been set up... + status.code = 500; + model.errorMessage = "appType.create.error.noTargetLocation"; + return false; + } + + // Shouldn't get to here - there should be a return at every code path... + model.errorMessage = "appType.create.error.unexpected"; + status.code = 500; + return false; +} + +model.success = main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post.json.ftl new file mode 100644 index 0000000000..02989c8d03 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post.json.ftl @@ -0,0 +1,11 @@ +<#if success!false == true> +{ + "success": "true", + "nodeRef": "${nodeRef!""}" +} +<#else> +{ + "success": "false", + "error": "${msg(errorMessage!"", errorMessageArg!"")?html}" +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post.properties new file mode 100644 index 0000000000..217954ac59 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post.properties @@ -0,0 +1,8 @@ +appType.create.error.nameAlreadyUsed=The name "{0}" has already been used +appType.create.error.noNameProvided=No name was provided for the application type +appType.create.error.noAppTypeProvided=No application type was provided for the application +appType.create.error.appTypeDoesNotExist=Requested application type does not exist +appType.create.error.invalidJson=The application definition was not valid JSON: {0} +appType.create.error.noTargetLocation=The target location for application "ShareResources/ApplicationTypes" has not been created in the Data Dictionary +appType.create.error.couldNotCreate=It was not possible to create the application type. +appType.create.error.unexpected=An unexpected error occurred. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_de.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_de.properties new file mode 100644 index 0000000000..5fc391c46a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_de.properties @@ -0,0 +1,8 @@ +appType.create.error.nameAlreadyUsed=Der Name ''{0}'' wurde bereits verwendet +appType.create.error.noNameProvided=Es wurde kein Name f\u00fcr den Anwendungstyp angegeben +appType.create.error.noAppTypeProvided=Es wurde kein Anwendungstyp f\u00fcr die Anwendung angegeben +appType.create.error.appTypeDoesNotExist=Der angeforderte Anwendungstyp existiert nicht +appType.create.error.invalidJson=Die Anwendungs-Definition war eine ung\u00fcltige JSON: {0} +appType.create.error.noTargetLocation=Der Zielort f\u00fcr die Anwendung ("ShareResources/ApplicationTypes") wurde nicht im Datenverzeichnis erstellt +appType.create.error.couldNotCreate=Der Anwendungstyp konnte nicht erstellt werden. +appType.create.error.unexpected=Es ist ein unerwarteter Fehler aufgetreten. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_es.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_es.properties new file mode 100644 index 0000000000..e659052b49 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_es.properties @@ -0,0 +1,8 @@ +appType.create.error.nameAlreadyUsed=El nombre ''{0}'' ya se ha utilizado +appType.create.error.noNameProvided=No se ha proporcionado un nombre para el tipo de aplicaci\u00f3n +appType.create.error.noAppTypeProvided=No se ha proporcionado un tipo de aplicaci\u00f3n para la aplicaci\u00f3n +appType.create.error.appTypeDoesNotExist=El tipo de aplicaci\u00f3n solicitado no existe +appType.create.error.invalidJson=La definici\u00f3n de la aplicaci\u00f3n era un JSON no v\u00e1lido: {0} +appType.create.error.noTargetLocation=La ubicaci\u00f3n de destino para la aplicaci\u00f3n "ShareResources/ApplicationTypes" no se ha creado en el diccionario de datos +appType.create.error.couldNotCreate=No se ha podido crear el tipo de aplicaci\u00f3n. +appType.create.error.unexpected=Se ha producido un error inesperado. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_fr.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_fr.properties new file mode 100644 index 0000000000..648766e7ad --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_fr.properties @@ -0,0 +1,8 @@ +appType.create.error.nameAlreadyUsed=Le nom ''{0}'' est d\u00e9j\u00e0 utilis\u00e9 +appType.create.error.noNameProvided=Aucun nom fourni pour le type d'application +appType.create.error.noAppTypeProvided=Aucun type d'application fourni pour l'application +appType.create.error.appTypeDoesNotExist=Le type d'application requis n'existe pas +appType.create.error.invalidJson=D\u00e9finition d''application non valide JSON : {0} +appType.create.error.noTargetLocation=L'emplacement de la cible pour l'application "ShareResources/ApplicationTypes" n'a pas \u00e9t\u00e9 cr\u00e9\u00e9 dans le Dictionnaire de donn\u00e9es +appType.create.error.couldNotCreate=Impossible de cr\u00e9er le type d'application. +appType.create.error.unexpected=Une erreur inattendue s'est produite. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_it.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_it.properties new file mode 100644 index 0000000000..134354fa93 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_it.properties @@ -0,0 +1,8 @@ +appType.create.error.nameAlreadyUsed=Il nome ''{0}'' \u00e8 gi\u00e0 in uso +appType.create.error.noNameProvided=Non \u00e8 stato fornito alcun nome per il tipo di applicazione +appType.create.error.noAppTypeProvided=Non \u00e8 stato fornito alcun tipo per l'applicazione +appType.create.error.appTypeDoesNotExist=Il tipo di applicazione richiesto non esiste +appType.create.error.invalidJson=La definizione di applicazione non \u00e8 un file JSON valido: {0} +appType.create.error.noTargetLocation=La posizione di destinazione per l'applicazione "ShareResources/ApplicationTypes" non \u00e8 stata creata nel dizionario dati +appType.create.error.couldNotCreate=Non \u00e8 stato possibile creare il tipo di applicazione. +appType.create.error.unexpected=Si \u00e8 verificato un errore imprevisto. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_ja.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_ja.properties new file mode 100644 index 0000000000..98fa55b9f1 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_ja.properties @@ -0,0 +1,8 @@ +appType.create.error.nameAlreadyUsed=\u540d\u524d "{0}" \u306f\u65e2\u306b\u4f7f\u308f\u308c\u3066\u3044\u307e\u3059 +appType.create.error.noNameProvided=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u7a2e\u985e\u306e\u540d\u524d\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +appType.create.error.noAppTypeProvided=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u7a2e\u985e\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +appType.create.error.appTypeDoesNotExist=\u6307\u5b9a\u3055\u308c\u305f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u7a2e\u985e\u306f\u5b58\u5728\u3057\u307e\u305b\u3093 +appType.create.error.invalidJson=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u5b9a\u7fa9\u306e JSON \u304c\u7121\u52b9\u3067\u3059: {0} +appType.create.error.noTargetLocation=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 "ShareResources/ApplicationTypes" \u306e\u30bf\u30fc\u30b2\u30c3\u30c8\u306e\u5834\u6240\u304c\u30c7\u30fc\u30bf\u30c7\u30a3\u30af\u30b7\u30e7\u30ca\u30ea\u5185\u306b\u4f5c\u6210\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +appType.create.error.couldNotCreate=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u7a2e\u985e\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +appType.create.error.unexpected=\u60f3\u5b9a\u5916\u306e\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_nb.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_nb.properties new file mode 100644 index 0000000000..e5e7369d40 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_nb.properties @@ -0,0 +1,8 @@ +appType.create.error.nameAlreadyUsed=Navnet ''{0}'' er allerede brukt +appType.create.error.noNameProvided=Ingen navn oppgitt for programtypen +appType.create.error.noAppTypeProvided=Ingen programtype oppgitt for programmet +appType.create.error.appTypeDoesNotExist=Programtypen det ble bedt om, finnes ikke +appType.create.error.invalidJson=Programdefinisjonen ikke gyldig JSON: {0} +appType.create.error.noTargetLocation=M\u00e5lstedet for applikasjonen "ShareResources/ApplicationTypes" (Del ressurser / Programtyper) er ikke opprettet i dataordlisten +appType.create.error.couldNotCreate=Det var ikke mulig \u00e5 opprette programtypen. +appType.create.error.unexpected=Det oppstod en uventet feil. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_nl.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_nl.properties new file mode 100644 index 0000000000..be1477e322 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_nl.properties @@ -0,0 +1,8 @@ +appType.create.error.nameAlreadyUsed=De naam ''{0}'' is al gebruikt +appType.create.error.noNameProvided=Er is geen naam opgegeven voor het toepassingstype +appType.create.error.noAppTypeProvided=Er is geen toepassingstype opgegeven voor de toepassing +appType.create.error.appTypeDoesNotExist=Het aangevraagde toepassingstype bestaat niet +appType.create.error.invalidJson=De toepassingsdefinitie is niet geldig (JSON): {0} +appType.create.error.noTargetLocation=De doellocatie voor de toepassing "Resources/toegangstypes delen" is niet gemaakt in de data dictionary +appType.create.error.couldNotCreate=Het toepassingstype kon niet worden gemaakt. +appType.create.error.unexpected=Er is een onverwachte fout opgetreden. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_pt_BR.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_pt_BR.properties new file mode 100644 index 0000000000..9212ce21c2 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_pt_BR.properties @@ -0,0 +1,8 @@ +appType.create.error.nameAlreadyUsed=O nome ''{0}'' j\u00e1 foi usado +appType.create.error.noNameProvided=Nenhum nome foi fornecido para o tipo de aplicativo +appType.create.error.noAppTypeProvided=Nenhum tipo de aplicativo foi fornecido para o aplicativo +appType.create.error.appTypeDoesNotExist=Tipo de aplicativo solicitado n\u00e3o existe +appType.create.error.invalidJson=A defini\u00e7\u00e3o do aplicativo n\u00e3o era um JSON v\u00e1lido: {0} +appType.create.error.noTargetLocation=A localiza\u00e7\u00e3o de destino para aplicativo "ShareResources/ApplicationTypes" n\u00e3o foi criada no Dicion\u00e1rio de dados +appType.create.error.couldNotCreate=N\u00e3o foi poss\u00edvel criar o tipo de aplicativo. +appType.create.error.unexpected=Ocorreu um erro inesperado. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_ru.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_ru.properties new file mode 100644 index 0000000000..14527c4f9d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_ru.properties @@ -0,0 +1,8 @@ +appType.create.error.nameAlreadyUsed=\u0418\u043c\u044f ''{0}'' \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u043e +appType.create.error.noNameProvided=\u0414\u043b\u044f \u0442\u0438\u043f\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u0438\u043c\u044f +appType.create.error.noAppTypeProvided=\u0414\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0442\u0438\u043f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f +appType.create.error.appTypeDoesNotExist=\u0417\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043d\u044b\u0439 \u0442\u0438\u043f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 +appType.create.error.invalidJson=\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u044f\u0432\u043b\u044f\u043b\u043e\u0441\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u043c JSON: {0} +appType.create.error.noTargetLocation=\u0412 \u0441\u043b\u043e\u0432\u0430\u0440\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043e \u0446\u0435\u043b\u0435\u0432\u043e\u0435 \u043c\u0435\u0441\u0442\u043e \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f "ShareResources/ApplicationTypes" +appType.create.error.couldNotCreate=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0438\u043f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. +appType.create.error.unexpected=\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_zh_CN.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_zh_CN.properties new file mode 100644 index 0000000000..7defb57f81 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instance.post_zh_CN.properties @@ -0,0 +1,8 @@ +appType.create.error.nameAlreadyUsed=\u540d\u79f0 "{0}" \u5df2\u4f7f\u7528 +appType.create.error.noNameProvided=\u6ca1\u6709\u4e3a\u5e94\u7528\u7a0b\u5e8f\u7c7b\u578b\u63d0\u4f9b\u540d\u79f0 +appType.create.error.noAppTypeProvided=\u6ca1\u6709\u4e3a\u5e94\u7528\u7a0b\u5e8f\u63d0\u4f9b\u5e94\u7528\u7a0b\u5e8f\u7c7b\u578b +appType.create.error.appTypeDoesNotExist=\u8bf7\u6c42\u7684\u5e94\u7528\u7a0b\u5e8f\u7c7b\u578b\u4e0d\u5b58\u5728 +appType.create.error.invalidJson=\u5e94\u7528\u7a0b\u5e8f\u5b9a\u4e49\u65e0\u6548 JSON\uff1a''{0}'' +appType.create.error.noTargetLocation=\u672a\u5728\u6570\u636e\u5b57\u5178\u4e2d\u521b\u5efa\u5e94\u7528\u7a0b\u5e8f "ShareResources/ApplicationTypes" \u7684\u76ee\u6807\u4f4d\u7f6e +appType.create.error.couldNotCreate=\u65e0\u6cd5\u521b\u5efa\u5e94\u7528\u7a0b\u5e8f\u7c7b\u578b\u3002 +appType.create.error.unexpected=\u53d1\u751f\u4e86\u610f\u5916\u9519\u8bef\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instances.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instances.get.desc.xml new file mode 100644 index 0000000000..244b509475 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instances.get.desc.xml @@ -0,0 +1,10 @@ + + Application Instances + Returns available Application Instances + Content Applications + /remote-share/content-app-instances + + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instances.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instances.get.js new file mode 100644 index 0000000000..d4932e3a91 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instances.get.js @@ -0,0 +1,35 @@ + +var alfQuery = 'TYPE:"{http://www.alfresco.org/model/surf/1.0}applicationInstance"' + + ' AND PATH:"/app:company_home/cm:ContentApps//*"'; + +// if (url.templateArgs.pagename != null) +// { +// alfQuery = alfQuery + ' AND @cm\:name:"' + url.templateArgs.pagename + '"'; +// } + +var queryDef = { + query: alfQuery, + language: "fts-alfresco", + page: {maxItems: 50}, + templates: [] +}; + +// Get article nodes +var pages = [], + item, + nodes = search.query(queryDef); + +for (var i = 0, j = nodes.length; i < j; i++) +{ + // Create core object + node = nodes[i]; + item = + { + nodeRef: node.nodeRef.toString(), + name: node.name + }; + pages.push(item); +} + + +model.data = pages; \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instances.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instances.get.json.ftl new file mode 100644 index 0000000000..5e344992a1 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-instances.get.json.ftl @@ -0,0 +1,19 @@ +<#macro renderItem item> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "nodeRef": "${item.nodeRef}", + "name": "${item.name!''}", + "content": "${item.content!''}" +} + + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "items": + [ + <#list data as item> + <@renderItem item /><#if item_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.desc.xml new file mode 100644 index 0000000000..0b3c92158a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.desc.xml @@ -0,0 +1,10 @@ + + Create a Content Application Type + Creates a new Content Application Type + Content Applications + /remote-share/content-app-type + + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.js new file mode 100644 index 0000000000..83473c921a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.js @@ -0,0 +1,101 @@ +function main() { + + // This query should find the Share Resources folder + var alfQuery = 'PATH:"/app:company_home/app:dictionary/cm:ShareResources/cm:ApplicationTypes"'; + + var queryDef = { + query: alfQuery, + language: "fts-alfresco", + page: {maxItems: 50}, + templates: [] + }; + + var shareResources, + nodes = search.query(queryDef); + if (nodes.length > 0) + { + shareResources = nodes[0]; + + // Get the page name and JSON definition from the request parameters... + var valid = true; + var name = args.name; + if (name == null || name == "") + { + status.code = 500; + model.errorMessage = "appType.create.error.noNameProvided"; + return false; + } + + var rootPage = args.rootPage; + if (rootPage == null || rootPage == "") + { + status.code = 500; + model.errorMessage = "appType.create.error.noRootPageProvided"; + return false; + } + else + { + // Check that the requested root page exists... + alfQuery = 'TYPE:"{http://www.alfresco.org/model/surf/1.0}amdpage"' + + ' AND PATH:"/app:company_home/app:dictionary//*"' + + ' AND @cm\:name:"' + rootPage + '"'; + queryDef.query = alfQuery; + var existingPages = search.query(queryDef); + if (existingPages.length == 0) + { + status.code = 500; + model.errorMessage = "appType.create.error.rootPageDoesNotExist"; + return false; + } + } + + // Check to see if the page name is already in use... + alfQuery = 'TYPE:"{http://www.alfresco.org/model/surf/1.0}applicationType"' + + ' AND PATH:"/app:company_home/app:dictionary//*"' + + ' AND @cm\:name:"' + name + '"'; + queryDef.query = alfQuery; + var existingAppTypes = search.query(queryDef); + if (existingAppTypes.length == 1) + { + status.code = 500; + model.errorMessage = "appType.create.error.nameAlreadyUsed"; + model.errorMessageArg = name; + return false; + } + + // Create the application type... + var properties = new Array(); + properties["surf:rootRage"] = rootPage; + + if (args.groups != null) + { + properties["surf:groupVisibility"] = args.groups; + } + var doc = shareResources.createNode(name, "surf:applicationType", properties); + if (doc == null) + { + status.code = 500; + model.errorMessage = "appType.create.error.couldNotCreate"; + return false; + } + else + { + model.nodeRef = doc.nodeRef.toString(); + return true; + } + } + else + { + // The Data Dictionary location for pages hasn't been set up... + status.code = 500; + model.errorMessage = "appType.create.error.noTargetLocation"; + return false; + } + + // Shouldn't get to here - there should be a return at every code path... + model.errorMessage = "appType.create.error.unexpected"; + status.code = 500; + return false; +} + +model.success = main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.json.ftl new file mode 100644 index 0000000000..02989c8d03 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.json.ftl @@ -0,0 +1,11 @@ +<#if success!false == true> +{ + "success": "true", + "nodeRef": "${nodeRef!""}" +} +<#else> +{ + "success": "false", + "error": "${msg(errorMessage!"", errorMessageArg!"")?html}" +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.json.js new file mode 100644 index 0000000000..ea77ded9da --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.json.js @@ -0,0 +1,95 @@ +function main() { + + // This query should find the Share Resources folder + var alfQuery = 'PATH:"/app:company_home/app:dictionary/cm:ShareResources/cm:ApplicationTypes"'; + + var queryDef = { + query: alfQuery, + language: "fts-alfresco", + page: {maxItems: 50}, + templates: [] + }; + + var shareResources, + nodes = search.query(queryDef); + if (nodes.length > 0) + { + shareResources = nodes[0]; + + // Get the page name and JSON definition from the request parameters... + var valid = true; + var name = json.get("name"); + if (name == null || name == "") + { + status.code = 500; + model.errorMessage = "appType.create.error.noNameProvided"; + return false; + } + + var rootPage = json.get("rootPage"); + if (rootPage == null || rootPage == "") + { + status.code = 500; + model.errorMessage = "appType.create.error.noRootPageProvided"; + return false; + } + else + { + // Check that the requested root page exists... + alfQuery = 'TYPE:"{http://www.alfresco.org/model/surf/1.0}amdpage"' + + ' AND PATH:"/app:company_home/app:dictionary//*"' + + ' AND @cm\:name:"' + rootPage + '"'; + queryDef.query = alfQuery; + var existingPages = search.query(queryDef); + if (existingPages.length == 0) + { + status.code = 500; + model.errorMessage = "appType.create.error.rootPageDoesNotExist"; + return false; + } + } + + // Check to see if the page name is already in use... + alfQuery = 'TYPE:"{http://www.alfresco.org/model/surf/1.0}applicationType"' + + ' AND PATH:"/app:company_home/app:dictionary//*"' + + ' AND @cm\:name:"' + name + '"'; + queryDef.query = alfQuery; + var existingAppTypes = search.query(queryDef); + if (existingAppTypes.length == 1) + { + status.code = 500; + model.errorMessage = "appType.create.error.nameAlreadyUsed"; + model.errorMessageArg = name; + return false; + } + + // Create the application type... + var doc = shareResources.createNode(name, "surf:applicationType"); + if (doc == null) + { + status.code = 500; + model.errorMessage = "appType.create.error.couldNotCreate"; + return false; + } + else + { + doc.properties["surf:rootRage"] = rootPage; + model.nodeRef = doc.nodeRef.toString(); + return true; + } + } + else + { + // The Data Dictionary location for pages hasn't been set up... + status.code = 500; + model.errorMessage = "appType.create.error.noTargetLocation"; + return false; + } + + // Shouldn't get to here - there should be a return at every code path... + model.errorMessage = "appType.create.error.unexpected"; + status.code = 500; + return false; +} + +model.success = main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.properties new file mode 100644 index 0000000000..94766c1c85 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post.properties @@ -0,0 +1,10 @@ +appType.create.error.nameAlreadyUsed=The name "{0}" has already been used +appType.create.error.noNameProvided=No name was provided for the application type +appType.create.error.noRootPageProvided=No root page was provided for the application type +appType.create.error.rootPageDoesNotExist=Requested root page does not exist +appType.create.error.groupDoesNotExist=Requested group does not exist +appType.create.error.noDefProvided=No application definition was provided for the application +appType.create.error.invalidJson=The application definition was not valid JSON: {0} +appType.create.error.noTargetLocation=The target location for application "ShareResources/ApplicationTypes" has not been created in the Data Dictionary +appType.create.error.couldNotCreate=It was not possible to create the application type. +appType.create.error.unexpected=An unexpected error occurred. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_de.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_de.properties new file mode 100644 index 0000000000..5bfc212bbd --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_de.properties @@ -0,0 +1,10 @@ +appType.create.error.nameAlreadyUsed=Der Name ''{0}'' wurde bereits verwendet +appType.create.error.noNameProvided=Es wurde kein Name f\u00fcr den Anwendungstyp angegeben +appType.create.error.noRootPageProvided=Es wurde keine Root-Seite f\u00fcr den Anwendungstyp angegeben +appType.create.error.rootPageDoesNotExist=Die angeforderte Root-Seite existiert nicht +appType.create.error.groupDoesNotExist=Die angeforderte Gruppe existiert nicht +appType.create.error.noDefProvided=Es wurde keine Anwendungs-Definition f\u00fcr die Anwendung angegeben +appType.create.error.invalidJson=Die Anwendungs-Definition war eine ung\u00fcltige JSON: {0} +appType.create.error.noTargetLocation=Der Zielort f\u00fcr die Anwendung ("ShareResources/ApplicationTypes") wurde nicht im Datenverzeichnis erstellt +appType.create.error.couldNotCreate=Der Anwendungstyp konnte nicht erstellt werden. +appType.create.error.unexpected=Es ist ein unerwarteter Fehler aufgetreten. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_es.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_es.properties new file mode 100644 index 0000000000..994a7c05d2 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_es.properties @@ -0,0 +1,10 @@ +appType.create.error.nameAlreadyUsed=El nombre ''{0}'' ya se ha utilizado +appType.create.error.noNameProvided=No se ha proporcionado un nombre para el tipo de aplicaci\u00f3n +appType.create.error.noRootPageProvided=No se ha proporcionado una p\u00e1gina ra\u00edz para el tipo de aplicaci\u00f3n +appType.create.error.rootPageDoesNotExist=La p\u00e1gina ra\u00edz solicitada no existe +appType.create.error.groupDoesNotExist=El grupo solicitado no existe +appType.create.error.noDefProvided=No se ha proporcionado una definici\u00f3n de aplicaci\u00f3n para la aplicaci\u00f3n +appType.create.error.invalidJson=La definici\u00f3n de la aplicaci\u00f3n era un JSON no v\u00e1lido: {0} +appType.create.error.noTargetLocation=La ubicaci\u00f3n de destino para la aplicaci\u00f3n "ShareResources/ApplicationTypes" no se ha creado en el diccionario de datos +appType.create.error.couldNotCreate=No se ha podido crear el tipo de aplicaci\u00f3n. +appType.create.error.unexpected=Se ha producido un error inesperado. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_fr.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_fr.properties new file mode 100644 index 0000000000..4fa891d8ce --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_fr.properties @@ -0,0 +1,10 @@ +appType.create.error.nameAlreadyUsed=Le nom ''{0}'' est d\u00e9j\u00e0 utilis\u00e9 +appType.create.error.noNameProvided=Aucun nom fourni pour le type d'application +appType.create.error.noRootPageProvided=Aucune page racine fournie pour le type d'application +appType.create.error.rootPageDoesNotExist=La page racine requise n'existe pas +appType.create.error.groupDoesNotExist=Le groupe requis n'existe pas +appType.create.error.noDefProvided=Aucune d\u00e9finition d'application fournie pour l'application +appType.create.error.invalidJson=D\u00e9finition d''application non valide JSON : {0} +appType.create.error.noTargetLocation=L'emplacement de la cible pour l'application "ShareResources/ApplicationTypes" n'a pas \u00e9t\u00e9 cr\u00e9\u00e9 dans le Dictionnaire de donn\u00e9es +appType.create.error.couldNotCreate=Impossible de cr\u00e9er le type d'application. +appType.create.error.unexpected=Une erreur inattendue s'est produite. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_it.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_it.properties new file mode 100644 index 0000000000..354b6ddc06 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_it.properties @@ -0,0 +1,10 @@ +appType.create.error.nameAlreadyUsed=Il nome ''{0}'' \u00e8 gi\u00e0 in uso +appType.create.error.noNameProvided=Non \u00e8 stato fornito alcun nome per il tipo di applicazione +appType.create.error.noRootPageProvided=Non \u00e8 stata fornita alcuna pagina d'apertura per il tipo di applicazione +appType.create.error.rootPageDoesNotExist=La pagina d'apertura richiesta non esiste +appType.create.error.groupDoesNotExist=Il gruppo richiesto non esiste +appType.create.error.noDefProvided=Non \u00e8 stata fornita alcuna definizione per l'applicazione +appType.create.error.invalidJson=La definizione di applicazione non \u00e8 un file JSON valido: {0} +appType.create.error.noTargetLocation=La posizione di destinazione per l'applicazione "ShareResources/ApplicationTypes" non \u00e8 stata creata nel dizionario dati +appType.create.error.couldNotCreate=Non \u00e8 stato possibile creare il tipo di applicazione. +appType.create.error.unexpected=Si \u00e8 verificato un errore imprevisto. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_ja.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_ja.properties new file mode 100644 index 0000000000..08e173c4e2 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_ja.properties @@ -0,0 +1,10 @@ +appType.create.error.nameAlreadyUsed=\u540d\u524d "{0}" \u306f\u65e2\u306b\u4f7f\u308f\u308c\u3066\u3044\u307e\u3059 +appType.create.error.noNameProvided=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u7a2e\u985e\u306e\u540d\u524d\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +appType.create.error.noRootPageProvided=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u7a2e\u985e\u306e\u30eb\u30fc\u30c8\u30da\u30fc\u30b8\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +appType.create.error.rootPageDoesNotExist=\u6307\u5b9a\u3057\u305f\u30eb\u30fc\u30c8\u30da\u30fc\u30b8\u306f\u5b58\u5728\u3057\u307e\u305b\u3093 +appType.create.error.groupDoesNotExist=\u6307\u5b9a\u3057\u305f\u30b0\u30eb\u30fc\u30d7\u306f\u5b58\u5728\u3057\u307e\u305b\u3093 +appType.create.error.noDefProvided=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u5b9a\u7fa9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +appType.create.error.invalidJson=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u5b9a\u7fa9\u306e JSON \u304c\u7121\u52b9\u3067\u3059: {0} +appType.create.error.noTargetLocation=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 "ShareResources/ApplicationTypes" \u306e\u30bf\u30fc\u30b2\u30c3\u30c8\u306e\u5834\u6240\u304c\u30c7\u30fc\u30bf\u30c7\u30a3\u30af\u30b7\u30e7\u30ca\u30ea\u5185\u306b\u4f5c\u6210\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +appType.create.error.couldNotCreate=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u7a2e\u985e\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +appType.create.error.unexpected=\u60f3\u5b9a\u5916\u306e\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_nb.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_nb.properties new file mode 100644 index 0000000000..66051e1103 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_nb.properties @@ -0,0 +1,10 @@ +appType.create.error.nameAlreadyUsed=Navnet ''{0}'' er allerede brukt +appType.create.error.noNameProvided=Ingen navn oppgitt for programtypen +appType.create.error.noRootPageProvided=Ingen rotside oppgitt for programtypen +appType.create.error.rootPageDoesNotExist=Rotsiden det ble bedt om, finnes ikke +appType.create.error.groupDoesNotExist=Gruppen det ble bedt om, finnes ikke +appType.create.error.noDefProvided=Ingen programdefinisjon oppgitt for applikasjonen +appType.create.error.invalidJson=Programdefinisjonen ikke gyldig JSON: {0} +appType.create.error.noTargetLocation=M\u00e5lstedet for applikasjonen "ShareResources/ApplicationTypes" (Del ressurser / Programtyper) er ikke opprettet i dataordlisten +appType.create.error.couldNotCreate=Det var ikke mulig \u00e5 opprette programtypen. +appType.create.error.unexpected=Det oppstod en uventet feil. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_nl.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_nl.properties new file mode 100644 index 0000000000..c5f119b696 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_nl.properties @@ -0,0 +1,10 @@ +appType.create.error.nameAlreadyUsed=De naam ''{0}'' is al gebruikt +appType.create.error.noNameProvided=Er is geen naam opgegeven voor het toepassingstype +appType.create.error.noRootPageProvided=Er is geen hoofdpagina opgegeven voor het toepassingstype +appType.create.error.rootPageDoesNotExist=De aangevraagde hoofdpagina bestaat niet +appType.create.error.groupDoesNotExist=De aangevraagde groep bestaat niet +appType.create.error.noDefProvided=Er is geen toepassingsdefinitie opgegeven voor de toepassing +appType.create.error.invalidJson=De toepassingsdefinitie is niet geldig (JSON): {0} +appType.create.error.noTargetLocation=De doellocatie voor de toepassing "Resources/toegangstypes delen" is niet gemaakt in de data dictionary +appType.create.error.couldNotCreate=Het toepassingstype kon niet worden gemaakt. +appType.create.error.unexpected=Er is een onverwachte fout opgetreden. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_pt_BR.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_pt_BR.properties new file mode 100644 index 0000000000..77f6a3c448 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_pt_BR.properties @@ -0,0 +1,10 @@ +appType.create.error.nameAlreadyUsed=O nome ''{0}'' j\u00e1 foi usado +appType.create.error.noNameProvided=Nenhum nome foi fornecido para o tipo de aplicativo +appType.create.error.noRootPageProvided=Nenhua p\u00e1gina raiz foi fornecida para o tipo de aplicativo +appType.create.error.rootPageDoesNotExist=P\u00e1gina raiz solicitada n\u00e3o existe +appType.create.error.groupDoesNotExist=Grupo solicitado n\u00e3o existe +appType.create.error.noDefProvided=Nenhuma defini\u00e7\u00e3o de aplicativo foi fornecida para o aplicativo +appType.create.error.invalidJson=A defini\u00e7\u00e3o do aplicativo n\u00e3o era um JSON v\u00e1lido: {0} +appType.create.error.noTargetLocation=A localiza\u00e7\u00e3o de destino para aplicativo "ShareResources/ApplicationTypes" n\u00e3o foi criada no Dicion\u00e1rio de dados +appType.create.error.couldNotCreate=N\u00e3o foi poss\u00edvel criar o tipo de aplicativo. +appType.create.error.unexpected=Ocorreu um erro inesperado. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_ru.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_ru.properties new file mode 100644 index 0000000000..55c08754c6 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_ru.properties @@ -0,0 +1,10 @@ +appType.create.error.nameAlreadyUsed=\u0418\u043c\u044f ''{0}'' \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u043e +appType.create.error.noNameProvided=\u0414\u043b\u044f \u0442\u0438\u043f\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u0438\u043c\u044f +appType.create.error.noRootPageProvided=\u0414\u043b\u044f \u0442\u0438\u043f\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 \u043a\u043e\u0440\u043d\u0435\u0432\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 +appType.create.error.rootPageDoesNotExist=\u0417\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043d\u0430\u044f \u043a\u043e\u0440\u043d\u0435\u0432\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 +appType.create.error.groupDoesNotExist=\u0417\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043d\u0430\u044f \u0433\u0440\u0443\u043f\u043f\u0430 \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 +appType.create.error.noDefProvided=\u0414\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f +appType.create.error.invalidJson=\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u044f\u0432\u043b\u044f\u043b\u043e\u0441\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u043c JSON: {0} +appType.create.error.noTargetLocation=\u0412 \u0441\u043b\u043e\u0432\u0430\u0440\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043e \u0446\u0435\u043b\u0435\u0432\u043e\u0435 \u043c\u0435\u0441\u0442\u043e \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f "ShareResources/ApplicationTypes" +appType.create.error.couldNotCreate=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0438\u043f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. +appType.create.error.unexpected=\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_zh_CN.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_zh_CN.properties new file mode 100644 index 0000000000..83459418e2 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-type.post_zh_CN.properties @@ -0,0 +1,10 @@ +appType.create.error.nameAlreadyUsed=\u540d\u79f0 "{0}" \u5df2\u4f7f\u7528 +appType.create.error.noNameProvided=\u6ca1\u6709\u4e3a\u5e94\u7528\u7a0b\u5e8f\u7c7b\u578b\u63d0\u4f9b\u540d\u79f0 +appType.create.error.noRootPageProvided=\u6ca1\u6709\u4e3a\u5e94\u7528\u7a0b\u5e8f\u7c7b\u578b\u63d0\u4f9b\u6839\u9875\u9762 +appType.create.error.rootPageDoesNotExist=\u8bf7\u6c42\u7684\u6839\u9875\u9762\u4e0d\u5b58\u5728 +appType.create.error.groupDoesNotExist=\u8bf7\u6c42\u7684\u7ec4\u4e0d\u5b58\u5728 +appType.create.error.noDefProvided=\u6ca1\u6709\u4e3a\u5e94\u7528\u7a0b\u5e8f\u63d0\u4f9b\u5e94\u7528\u7a0b\u5e8f\u5b9a\u4e49 +appType.create.error.invalidJson=\u5e94\u7528\u7a0b\u5e8f\u5b9a\u4e49\u65e0\u6548 JSON\uff1a''{0}'' +appType.create.error.noTargetLocation=\u672a\u5728\u6570\u636e\u5b57\u5178\u4e2d\u521b\u5efa\u5e94\u7528\u7a0b\u5e8f "ShareResources/ApplicationTypes" \u7684\u76ee\u6807\u4f4d\u7f6e +appType.create.error.couldNotCreate=\u65e0\u6cd5\u521b\u5efa\u5e94\u7528\u7a0b\u5e8f\u7c7b\u578b\u3002 +appType.create.error.unexpected=\u53d1\u751f\u4e86\u610f\u5916\u9519\u8bef\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-types.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-types.get.desc.xml new file mode 100644 index 0000000000..21b4f21590 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-types.get.desc.xml @@ -0,0 +1,10 @@ + + Application Types + Returns available Application Types + Content Applications + /remote-share/content-app-types + + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-types.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-types.get.js new file mode 100644 index 0000000000..a3417fb8b1 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-types.get.js @@ -0,0 +1,40 @@ + +var alfQuery = 'TYPE:"{http://www.alfresco.org/model/surf/1.0}applicationType"' + + ' AND PATH:"/app:company_home/app:dictionary//*"'; + +// if (url.templateArgs.pagename != null) +// { +// alfQuery = alfQuery + ' AND @cm\:name:"' + url.templateArgs.pagename + '"'; +// } + +var queryDef = { + query: alfQuery, + language: "fts-alfresco", + page: {maxItems: 50}, + templates: [] +}; + +// Get article nodes +var pages = [], + item, + nodes = search.query(queryDef); + +var includeContent = (nodes.length == 1); +for (var i = 0, j = nodes.length; i < j; i++) +{ + // Create core object + node = nodes[i]; + item = + { + nodeRef: node.nodeRef.toString(), + name: node.name + }; + if (includeContent) + { + item.content = node.content; + } + pages.push(item); +} + + +model.data = pages; \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-types.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-types.get.json.ftl new file mode 100644 index 0000000000..5e344992a1 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/content-apps/application-types.get.json.ftl @@ -0,0 +1,19 @@ +<#macro renderItem item> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "nodeRef": "${item.nodeRef}", + "name": "${item.name!''}", + "content": "${item.content!''}" +} + + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "items": + [ + <#list data as item> + <@renderItem item /><#if item_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post.desc.xml new file mode 100644 index 0000000000..13434176de --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post.desc.xml @@ -0,0 +1,10 @@ + + Create a Page Definition + Creates a Page definition + Remote Share + /remote-share/page-definition + + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post.json.ftl new file mode 100644 index 0000000000..02989c8d03 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post.json.ftl @@ -0,0 +1,11 @@ +<#if success!false == true> +{ + "success": "true", + "nodeRef": "${nodeRef!""}" +} +<#else> +{ + "success": "false", + "error": "${msg(errorMessage!"", errorMessageArg!"")?html}" +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post.json.js new file mode 100644 index 0000000000..cdb4836de7 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post.json.js @@ -0,0 +1,96 @@ +function main() { + + // This query should find the Share Resources folder + var alfQuery = 'PATH:"/app:company_home/app:dictionary/cm:ShareResources/cm:Pages"'; + + var queryDef = { + query: alfQuery, + language: "fts-alfresco", + page: {maxItems: 50}, + templates: [] + }; + + var shareResources, + nodes = search.query(queryDef); + if (nodes.length > 0) + { + shareResources = nodes[0]; + + // Get the page name and JSON definition from the request parameters... + var valid = true; + var name = json.get("name"); + var def = json.get("json"); + if (name == null || name == "") + { + status.code = 500; + model.errorMessage = "page.create.error.noNameProvided"; + return false; + } + + // Check to see if the page name is already in use... + alfQuery = 'TYPE:"{http://www.alfresco.org/model/surf/1.0}amdpage"' + + ' AND PATH:"/app:company_home/app:dictionary//*"' + + ' AND @cm\:name:"' + name + '"'; + queryDef.query = alfQuery; + var existingPages = search.query(queryDef); + if (existingPages.length == 1) + { + status.code = 500; + model.errorMessage = "page.create.error.nameAlreadyUsed"; + model.errorMessageArg = name; + return false; + } + + if (def == null || def == "") + { + status.code = 500; + model.errorMessage = "page.create.error.noDefProvided" + model.errorMessageArg = def; + return false; + } + + try + { + pageDetails = jsonUtils.toObject(def); + } + catch(e) + { + status.code = 500; + model.errorMessage = "page.create.error.invalidJson"; + model.errorMessageArg = def; + return false; + } + + // Get the page name and it's content... + var pageDefinitionName = name; + var pageDefinitionJSON = def; + var doc = shareResources.createNode(pageDefinitionName, "surf:amdpage"); + if (doc == null) + { + status.code = 500; + model.errorMessage = "page.create.error.couldNotCreate"; + return false; + } + else + { + doc.content = pageDefinitionJSON; + doc.mimetype = "application/json"; + model.nodeRef = doc.nodeRef.toString(); + return true; + } + } + else + { + // The Data Dictionary location for pages hasn't been set up... + status.code = 500; + model.errorMessage = "page.create.error.noTargetLocation"; + return false; + } + + // Shouldn't get to here - there should be a return at every code path... + model.errorMessage = "page.create.error.unexpected"; + status.code = 500; + return false; +} + +model.success = main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post.properties new file mode 100644 index 0000000000..c180d75750 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=The name "{0}" has already been used +page.create.error.noNameProvided=No name was provided for the page +page.create.error.noDefProvided=No page definition was provided for the page +page.create.error.invalidJson=The page definition was not valid JSON: {0} +page.create.error.noTargetLocation=The target location for pages "ShareResources/pages" has not been created in the Data Dictionary +page.create.error.couldNotCreate=It was not possible to create the page. +page.create.error.unexpected=An unexpected error occurred. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_de.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_de.properties new file mode 100644 index 0000000000..57c5c43102 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_de.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=Der Name ''{0}'' wurde bereits verwendet +page.create.error.noNameProvided=Es wurde kein Name f\u00fcr die Seite angegeben +page.create.error.noDefProvided=Es wurde keine Seitendefinition f\u00fcr die Seite angegeben +page.create.error.invalidJson=Die Seitendefinition war eine ung\u00fcltige JSON: {0} +page.create.error.noTargetLocation=Der Zielort f\u00fcr Seiten ("ShareResources/pages") wurde nicht im Datenverzeichnis erstellt +page.create.error.couldNotCreate=Die Seite konnte nicht erstellt werden. +page.create.error.unexpected=Es ist ein unerwarteter Fehler aufgetreten. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_es.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_es.properties new file mode 100644 index 0000000000..487f9479fb --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_es.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=El nombre ''{0}'' ya se ha utilizado +page.create.error.noNameProvided=No se ha proporcionado un nombre para la p\u00e1gina +page.create.error.noDefProvided=No se ha proporcionado una definici\u00f3n de p\u00e1gina para la p\u00e1gina +page.create.error.invalidJson=La definici\u00f3n de p\u00e1gina era un JSON no v\u00e1lido: {0} +page.create.error.noTargetLocation=La ubicaci\u00f3n de destino para las p\u00e1ginas "ShareResources/pages" no se ha creado en el diccionario de datos +page.create.error.couldNotCreate=No se ha podido crear la p\u00e1gina. +page.create.error.unexpected=Se ha producido un error inesperado. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_fr.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_fr.properties new file mode 100644 index 0000000000..0707de0296 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_fr.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=Le nom ''{0}'' est d\u00e9j\u00e0 utilis\u00e9 +page.create.error.noNameProvided=Aucun nom fourni pour la page +page.create.error.noDefProvided=Aucune d\u00e9finition de page n'a \u00e9t\u00e9 fournie pour la page +page.create.error.invalidJson=D\u00e9finition de page non valide JSON : {0} +page.create.error.noTargetLocation=L'emplacement de la cible pour les pages "ShareResources/pages" n'a pas \u00e9t\u00e9 cr\u00e9\u00e9 dans le Dictionnaire de donn\u00e9es +page.create.error.couldNotCreate=Impossible de cr\u00e9er la page. +page.create.error.unexpected=Une erreur inattendue s'est produite. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_it.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_it.properties new file mode 100644 index 0000000000..efb6c02f73 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_it.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=Il nome ''{0}'' \u00e8 gi\u00e0 in uso +page.create.error.noNameProvided=Non \u00e8 stato fornito alcun nome per la pagina +page.create.error.noDefProvided=Non \u00e8 stata fornita alcuna definizione di pagina per la pagina +page.create.error.invalidJson=La definizione di pagina non \u00e8 un file JSON valido: {0} +page.create.error.noTargetLocation=La posizione di destinazione per le pagine "ShareResources/pages" non \u00e8 stata creata nel dizionario dati +page.create.error.couldNotCreate=Non \u00e8 stato possibile creare la pagina. +page.create.error.unexpected=Si \u00e8 verificato un errore imprevisto. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_ja.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_ja.properties new file mode 100644 index 0000000000..854f4989e0 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_ja.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=\u540d\u524d "{0}" \u306f\u65e2\u306b\u4f7f\u308f\u308c\u3066\u3044\u307e\u3059 +page.create.error.noNameProvided=\u30da\u30fc\u30b8\u306e\u540d\u524d\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +page.create.error.noDefProvided=\u30da\u30fc\u30b8\u306e\u30da\u30fc\u30b8\u5b9a\u7fa9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +page.create.error.invalidJson=\u30da\u30fc\u30b8\u5b9a\u7fa9\u306e JSON \u304c\u7121\u52b9\u3067\u3059: {0} +page.create.error.noTargetLocation=\u30da\u30fc\u30b8 "ShareResources/pages" \u306e\u30bf\u30fc\u30b2\u30c3\u30c8\u306e\u5834\u6240\u304c\u30c7\u30fc\u30bf\u30c7\u30a3\u30af\u30b7\u30e7\u30ca\u30ea\u5185\u306b\u4f5c\u6210\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +page.create.error.couldNotCreate=\u30da\u30fc\u30b8\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +page.create.error.unexpected=\u60f3\u5b9a\u5916\u306e\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_nb.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_nb.properties new file mode 100644 index 0000000000..aa047e2f4d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_nb.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=Navnet ''{0}'' er allerede brukt +page.create.error.noNameProvided=Intet navn oppgitt for siden +page.create.error.noDefProvided=Ingen sidedefinisjon oppgitt for siden +page.create.error.invalidJson=Sidedefinisjonen ikke gyldig JSON: {0} +page.create.error.noTargetLocation=M\u00e5lstedet for sidene "ShareResources/sider" ikke opprettet i datamappen +page.create.error.couldNotCreate=Det var ikke mulig \u00e5 opprette siden. +page.create.error.unexpected=Det oppstod en uventet feil. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_nl.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_nl.properties new file mode 100644 index 0000000000..74a8b02383 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_nl.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=De naam ''{0}'' is al gebruikt +page.create.error.noNameProvided=Er is geen naam opgegeven voor de pagina +page.create.error.noDefProvided=Er is geen paginadefinitie opgegeven voor de pagina +page.create.error.invalidJson=De paginadefinitie is niet geldig (JSON): {0} +page.create.error.noTargetLocation=De doellocatie voor de pagina's "Resources/pagina's delen" is niet gemaakt in de data dictionary +page.create.error.couldNotCreate=De pagina kon niet worden gemaakt. +page.create.error.unexpected=Er is een onverwachte fout opgetreden. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_pt_BR.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_pt_BR.properties new file mode 100644 index 0000000000..4578027edf --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_pt_BR.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=O nome ''{0}'' j\u00e1 foi usado +page.create.error.noNameProvided=N\u00e3o foi fornecido um nome para a p\u00e1gina +page.create.error.noDefProvided=N\u00e3o foi fornecida uma defini\u00e7\u00e3o para a p\u00e1gina +page.create.error.invalidJson=A defini\u00e7\u00e3o da p\u00e1gina n\u00e3o era um JSON v\u00e1lido: {0} +page.create.error.noTargetLocation=A localiza\u00e7\u00e3o de destino para p\u00e1ginas "ShareResources/pages" n\u00e3o foi criada no Dicion\u00e1rio de dados +page.create.error.couldNotCreate=N\u00e3o foi poss\u00edvel criar a p\u00e1gina. +page.create.error.unexpected=Ocorreu um erro inesperado. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_ru.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_ru.properties new file mode 100644 index 0000000000..326ebb1a88 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_ru.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=\u0418\u043c\u044f ''{0}'' \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u043e +page.create.error.noNameProvided=\u0414\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0438\u043c\u044f +page.create.error.noDefProvided=\u0414\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 +page.create.error.invalidJson=\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u0435 \u044f\u0432\u043b\u044f\u043b\u043e\u0441\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u043c JSON: {0} +page.create.error.noTargetLocation=\u0412 \u0441\u043b\u043e\u0432\u0430\u0440\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043e \u0446\u0435\u043b\u0435\u0432\u043e\u0435 \u043c\u0435\u0441\u0442\u043e \u0434\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446 "ShareResources/pages" +page.create.error.couldNotCreate=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443. +page.create.error.unexpected=\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_zh_CN.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_zh_CN.properties new file mode 100644 index 0000000000..f8157c9379 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_zh_CN.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=\u540d\u79f0 "{0}" \u5df2\u4f7f\u7528 +page.create.error.noNameProvided=\u6ca1\u6709\u4e3a\u9875\u9762\u6307\u5b9a\u540d\u79f0 +page.create.error.noDefProvided=\u6ca1\u6709\u63d0\u4f9b\u9875\u9762\u5b9a\u4e49 +page.create.error.invalidJson=\u9875\u9762\u5b9a\u4e49\u65e0\u6548 JSON\uff1a{0} +page.create.error.noTargetLocation=\u672a\u5728\u6570\u636e\u5b57\u5178\u4e2d\u521b\u5efa\u9875\u9762"ShareResources/pages"\u7684\u76ee\u6807\u4f4d\u7f6e +page.create.error.couldNotCreate=\u65e0\u6cd5\u521b\u5efa\u9875\u9762\u3002 +page.create.error.unexpected=\u53d1\u751f\u4e86\u610f\u5916\u9519\u8bef\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put.desc.xml new file mode 100644 index 0000000000..12bb975f31 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put.desc.xml @@ -0,0 +1,10 @@ + + Update a Page Definition + Updates an existing Page definition + Remote Share + /remote-share/page-definition/{pagename} + + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put.json.ftl new file mode 100644 index 0000000000..02989c8d03 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put.json.ftl @@ -0,0 +1,11 @@ +<#if success!false == true> +{ + "success": "true", + "nodeRef": "${nodeRef!""}" +} +<#else> +{ + "success": "false", + "error": "${msg(errorMessage!"", errorMessageArg!"")?html}" +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put.json.js new file mode 100644 index 0000000000..3331f00b23 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put.json.js @@ -0,0 +1,66 @@ +function main() { + + if (url.templateArgs.pagename != null) + { + var alfQuery = 'TYPE:"{http://www.alfresco.org/model/surf/1.0}amdpage"' + + ' AND PATH:"/app:company_home/app:dictionary//*"' + + ' AND @cm\:name:"' + url.templateArgs.pagename + '"';; + var queryDef = { + query: alfQuery, + language: "fts-alfresco", + page: {maxItems: 50}, + templates: [] + }; + + var pages = [], + targetPage, + nodes = search.query(queryDef); + if (nodes.length > 0) + { + targetPage = nodes[0]; + var def = json.get("json"); + if (def == null || def == "") + { + status.code = 500; + model.errorMessage = "page.update.error.noDefProvided" + model.errorMessageArg = def; + return false; + } + + try + { + pageDetails = jsonUtils.toObject(def); + } + catch(e) + { + status.code = 500; + model.errorMessage = "page.update.error.invalidJson"; + model.errorMessageArg = def; + return false; + } + + targetPage.addAspect("cm:versionable"); + var workingCopy = targetPage.checkout(); + var pageDefinitionJSON = def; + workingCopy.content = pageDefinitionJSON; + targetPage = workingCopy.checkin(); + return true; + } + + } + else + { + // Not provided with anything to update + model.errorMessage = "page.update.error.doesNotExist"; + status.code = 500; + return false; + } + + + // Shouldn't get to here - there should be a return at every code path... + model.errorMessage = "page.update.error.unexpected"; + status.code = 500; + return false; +} + +model.success = main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put.properties new file mode 100644 index 0000000000..da1af0386d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=The name "{0}" has already been used +page.update.error.noNameProvided=No name was provided for the page +page.update.error.noDefProvided=No page definition was provided for the page +page.update.error.invalidJson=The page definition was not valid JSON: {0} +page.update.error.noTargetLocation=The target location for pages "ShareResources/pages" has not been created in the Data Dictionary +page.update.error.couldNotCreate=It was not possible to create the page. +page.update.error.doesNotExist=The requested page to update does not exist +page.update.error.unexpected=An unexpected error occurred. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_de.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_de.properties new file mode 100644 index 0000000000..d8cb058a06 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_de.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=Der Name ''{0}'' wurde bereits verwendet +page.update.error.noNameProvided=Es wurde kein Name f\u00fcr die Seite angegeben +page.update.error.noDefProvided=Es wurde keine Seitendefinition f\u00fcr die Seite angegeben +page.update.error.invalidJson=Die Seitendefinition war eine ung\u00fcltige JSON: {0} +page.update.error.noTargetLocation=Der Zielort f\u00fcr Seiten ("ShareResources/pages") wurde nicht im Datenverzeichnis erstellt +page.update.error.couldNotCreate=Die Seite konnte nicht erstellt werden. +page.update.error.doesNotExist=Die zum Aktualisieren angeforderte Seite gibt es nicht +page.update.error.unexpected=Es ist ein unerwarteter Fehler aufgetreten. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_es.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_es.properties new file mode 100644 index 0000000000..bf2995eff5 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_es.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=El nombre ''{0}'' ya se ha utilizado +page.update.error.noNameProvided=No se ha proporcionado un nombre para la p\u00e1gina +page.update.error.noDefProvided=No se ha proporcionado una definici\u00f3n de p\u00e1gina para la p\u00e1gina +page.update.error.invalidJson=La definici\u00f3n de p\u00e1gina era un JSON no v\u00e1lido: {0} +page.update.error.noTargetLocation=La ubicaci\u00f3n de destino para las p\u00e1ginas "ShareResources/pages" no se ha creado en el diccionario de datos +page.update.error.couldNotCreate=No se ha podido crear la p\u00e1gina. +page.update.error.doesNotExist=La p\u00e1gina solicitada para actualizaci\u00f3n no existe +page.update.error.unexpected=Se ha producido un error inesperado. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_fr.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_fr.properties new file mode 100644 index 0000000000..852cad403e --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_fr.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=Le nom ''{0}'' est d\u00e9j\u00e0 utilis\u00e9 +page.update.error.noNameProvided=Aucun nom fourni pour la page +page.update.error.noDefProvided=Aucune d\u00e9finition de page n'a \u00e9t\u00e9 fournie pour la page +page.update.error.invalidJson=D\u00e9finition de page non valide JSON : {0} +page.update.error.noTargetLocation=L'emplacement de la cible pour les pages "ShareResources/pages" n'a pas \u00e9t\u00e9 cr\u00e9\u00e9 dans le Dictionnaire de donn\u00e9es +page.update.error.couldNotCreate=Impossible de cr\u00e9er la page. +page.update.error.doesNotExist=La page \u00e0 mettre \u00e0 jour n'existe pas +page.update.error.unexpected=Une erreur inattendue s'est produite. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_it.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_it.properties new file mode 100644 index 0000000000..f1944842bc --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_it.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=Il nome ''{0}'' \u00e8 gi\u00e0 in uso +page.update.error.noNameProvided=Non \u00e8 stato fornito alcun nome per la pagina +page.update.error.noDefProvided=Non \u00e8 stata fornita alcuna definizione di pagina per la pagina +page.update.error.invalidJson=La definizione di pagina non \u00e8 un file JSON valido: {0} +page.update.error.noTargetLocation=La posizione di destinazione per le pagine "ShareResources/pages" non \u00e8 stata creata nel dizionario dati +page.update.error.couldNotCreate=Non \u00e8 stato possibile creare la pagina. +page.update.error.doesNotExist=La pagina richiesta da aggiornare non esiste +page.update.error.unexpected=Si \u00e8 verificato un errore imprevisto. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_ja.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_ja.properties new file mode 100644 index 0000000000..7fbd9c0eb3 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_ja.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=\u540d\u524d "{0}" \u306f\u65e2\u306b\u4f7f\u308f\u308c\u3066\u3044\u307e\u3059 +page.update.error.noNameProvided=\u30da\u30fc\u30b8\u306e\u540d\u524d\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +page.update.error.noDefProvided=\u30da\u30fc\u30b8\u306e\u30da\u30fc\u30b8\u5b9a\u7fa9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +page.update.error.invalidJson=\u30da\u30fc\u30b8\u5b9a\u7fa9\u306e JSON \u304c\u7121\u52b9\u3067\u3059: {0} +page.update.error.noTargetLocation=\u30da\u30fc\u30b8 "ShareResources/pages" \u306e\u30bf\u30fc\u30b2\u30c3\u30c8\u306e\u5834\u6240\u304c\u30c7\u30fc\u30bf\u30c7\u30a3\u30af\u30b7\u30e7\u30ca\u30ea\u5185\u306b\u4f5c\u6210\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +page.update.error.couldNotCreate=\u30da\u30fc\u30b8\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +page.update.error.doesNotExist=\u66f4\u65b0\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u30da\u30fc\u30b8\u306f\u5b58\u5728\u3057\u307e\u305b\u3093 +page.update.error.unexpected=\u60f3\u5b9a\u5916\u306e\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_nb.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_nb.properties new file mode 100644 index 0000000000..adb2dd1277 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_nb.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=Navnet ''{0}'' er allerede brukt +page.update.error.noNameProvided=Intet navn oppgitt for siden +page.update.error.noDefProvided=Ingen sidedefinisjon oppgitt for siden +page.update.error.invalidJson=Sidedefinisjonen ikke gyldig JSON: {0} +page.update.error.noTargetLocation=M\u00e5lstedet for sidene "ShareResources/sider" ikke opprettet i datamappen +page.update.error.couldNotCreate=Det var ikke mulig \u00e5 opprette siden. +page.update.error.doesNotExist=Forespurt side som skal oppdateres eksisterer ikke +page.update.error.unexpected=Det oppstod en uventet feil. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_nl.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_nl.properties new file mode 100644 index 0000000000..8aafc38bd4 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_nl.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=De naam ''{0}'' is al gebruikt +page.update.error.noNameProvided=Er is geen naam opgegeven voor de pagina +page.update.error.noDefProvided=Er is geen paginadefinitie opgegeven voor de pagina +page.update.error.invalidJson=De paginadefinitie is niet geldig (JSON): {0} +page.update.error.noTargetLocation=De doellocatie voor de pagina's "Resources/pagina's delen" is niet gemaakt in de data dictionary +page.update.error.couldNotCreate=De pagina kon niet worden gemaakt. +page.update.error.doesNotExist=De opgevraagde pagina die moet worden bijgewerkt bestaat niet +page.update.error.unexpected=Er is een onverwachte fout opgetreden. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_pt_BR.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_pt_BR.properties new file mode 100644 index 0000000000..d853a27a5b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_pt_BR.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=O nome ''{0}'' j\u00e1 foi usado +page.update.error.noNameProvided=N\u00e3o foi fornecido um nome para a p\u00e1gina +page.update.error.noDefProvided=N\u00e3o foi fornecida uma defini\u00e7\u00e3o para a p\u00e1gina +page.update.error.invalidJson=A defini\u00e7\u00e3o da p\u00e1gina n\u00e3o era um JSON v\u00e1lido: {0} +page.update.error.noTargetLocation=A localiza\u00e7\u00e3o de destino para p\u00e1ginas "ShareResources/pages" n\u00e3o foi criada no Dicion\u00e1rio de dados +page.update.error.couldNotCreate=N\u00e3o foi poss\u00edvel criar a p\u00e1gina. +page.update.error.doesNotExist=A p\u00e1gina solicitada para atualizar n\u00e3o existe +page.update.error.unexpected=Ocorreu um erro inesperado. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_ru.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_ru.properties new file mode 100644 index 0000000000..3857942078 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_ru.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=\u0418\u043c\u044f ''{0}'' \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u043e +page.update.error.noNameProvided=\u0414\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0438\u043c\u044f +page.update.error.noDefProvided=\u0414\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 +page.update.error.invalidJson=\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u0435 \u044f\u0432\u043b\u044f\u043b\u043e\u0441\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u043c JSON: {0} +page.update.error.noTargetLocation=\u0412 \u0441\u043b\u043e\u0432\u0430\u0440\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043e \u0446\u0435\u043b\u0435\u0432\u043e\u0435 \u043c\u0435\u0441\u0442\u043e \u0434\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446 "ShareResources/pages" +page.update.error.couldNotCreate=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443. +page.update.error.doesNotExist=\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435, \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 +page.update.error.unexpected=\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430. diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_zh_CN.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_zh_CN.properties new file mode 100644 index 0000000000..d2873564a2 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_zh_CN.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=\u540d\u79f0 "{0}" \u5df2\u4f7f\u7528 +page.update.error.noNameProvided=\u6ca1\u6709\u4e3a\u9875\u9762\u6307\u5b9a\u540d\u79f0 +page.update.error.noDefProvided=\u6ca1\u6709\u63d0\u4f9b\u9875\u9762\u5b9a\u4e49 +page.update.error.invalidJson=\u9875\u9762\u5b9a\u4e49\u65e0\u6548 JSON\uff1a{0} +page.update.error.noTargetLocation=\u672a\u5728\u6570\u636e\u5b57\u5178\u4e2d\u521b\u5efa\u9875\u9762"ShareResources/pages"\u7684\u76ee\u6807\u4f4d\u7f6e +page.update.error.couldNotCreate=\u65e0\u6cd5\u521b\u5efa\u9875\u9762\u3002 +page.update.error.doesNotExist=\u8bf7\u6c42\u66f4\u65b0\u7684\u9875\u9762\u4e0d\u5b58\u5728 +page.update.error.unexpected=\u53d1\u751f\u4e86\u610f\u5916\u9519\u8bef\u3002 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/pages.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/pages.get.desc.xml new file mode 100644 index 0000000000..5717a5424a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/pages.get.desc.xml @@ -0,0 +1,11 @@ + + Remote Pages + Returns available page definitions + Remote Share + /remote-share/pages + /remote-share/pages/name/{pagename} + + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/pages.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/pages.get.js new file mode 100644 index 0000000000..42484dd8d1 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/pages.get.js @@ -0,0 +1,40 @@ + +var alfQuery = 'TYPE:"{http://www.alfresco.org/model/surf/1.0}amdpage"' + + ' AND PATH:"/app:company_home/app:dictionary//*"'; + +if (url.templateArgs.pagename != null) +{ + alfQuery = alfQuery + ' AND @cm\:name:"' + url.templateArgs.pagename + '"'; +} + +var queryDef = { + query: alfQuery, + language: "fts-alfresco", + page: {maxItems: 50}, + templates: [] +}; + +// Get article nodes +var pages = [], + item, + nodes = search.query(queryDef); + +var includeContent = (nodes.length == 1); +for (var i = 0, j = nodes.length; i < j; i++) +{ + // Create core object + node = nodes[i]; + item = + { + nodeRef: node.nodeRef.toString(), + name: node.name + }; + if (includeContent) + { + item.content = node.content; + } + pages.push(item); +} + + +model.data = pages; \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/pages.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/pages.get.json.ftl new file mode 100644 index 0000000000..5e344992a1 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/pages.get.json.ftl @@ -0,0 +1,19 @@ +<#macro renderItem item> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "nodeRef": "${item.nodeRef}", + "name": "${item.name!''}", + "content": "${item.content!''}" +} + + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "items": + [ + <#list data as item> + <@renderItem item /><#if item_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/services.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/services.get.desc.xml new file mode 100644 index 0000000000..39d6756930 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/services.get.desc.xml @@ -0,0 +1,10 @@ + + Remote Services + Returns available services + Remote Share + /remote-share/services + + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/services.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/services.get.js new file mode 100644 index 0000000000..d24fff7653 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/services.get.js @@ -0,0 +1,31 @@ + +var alfQuery = 'ASPECT:"{http://www.alfresco.org/model/surf/1.0}service"' + + ' AND PATH:"/app:company_home/app:dictionary//*"'; + +var queryDef = { + query: alfQuery, + language: "fts-alfresco", + page: {maxItems: 50}, + templates: [] +}; + +// Get article nodes +var services = [], + item, + nodes = search.query(queryDef); + +for (var i = 0, j = nodes.length; i < j; i++) +{ + // Create core object + node = nodes[i]; + item = + { + nodeRef: node.nodeRef.toString(), + mid: node.properties["surf:mid"], + label: node.properties["surf:label"] + }; + services.push(item); +} + + +model.data = services; \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/services.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/services.get.json.ftl new file mode 100644 index 0000000000..50826455d3 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/services.get.json.ftl @@ -0,0 +1,19 @@ +<#macro renderItem item> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "nodeRef": "${item.nodeRef}", + "mid": "${item.mid!''}", + "label": "${item.label!''}" +} + + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "services": + [ + <#list data as item> + <@renderItem item /><#if item_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/widgets.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/widgets.get.desc.xml new file mode 100644 index 0000000000..12922bc20d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/widgets.get.desc.xml @@ -0,0 +1,10 @@ + + Remote Widgets + Returns available widgets + Remote Share + /remote-share/widgets + + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/widgets.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/widgets.get.js new file mode 100644 index 0000000000..7cd9acedff --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/widgets.get.js @@ -0,0 +1,32 @@ + +var alfQuery = 'ASPECT:"{http://www.alfresco.org/model/surf/1.0}widget"' + + ' AND PATH:"/app:company_home/app:dictionary//*"'; + +var queryDef = { + query: alfQuery, + language: "fts-alfresco", + page: {maxItems: 50}, + templates: [] +}; + +// Get article nodes +var widgets = [], + item, + nodes = search.query(queryDef); + +for (var i = 0, j = nodes.length; i < j; i++) +{ + // Create core object + node = nodes[i]; + item = + { + nodeRef: node.nodeRef.toString(), + widgetType: node.properties["surf:widgetType"], + mid: node.properties["surf:mid"], + label: node.properties["surf:label"] + }; + widgets.push(item); +} + + +model.data = widgets; \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/widgets.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/widgets.get.json.ftl new file mode 100644 index 0000000000..00597afa5b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/widgets.get.json.ftl @@ -0,0 +1,20 @@ +<#macro renderItem item> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "nodeRef": "${item.nodeRef}", + "widgetType": "${item.widgetType!''}", + "mid": "${item.mid!''}", + "label": "${item.label!''}" +} + + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "widgets": + [ + <#list data as item> + <@renderItem item /><#if item_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/auto-suggest-search.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/auto-suggest-search.get.desc.xml new file mode 100644 index 0000000000..b14f641c3c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/auto-suggest-search.get.desc.xml @@ -0,0 +1,35 @@ + + Share Auto Suggest + + :/alfresco/service/slingshot/auto-suggest?t={term}&limit={limit?} + + Example response from this web script for the term "tes": + + { + "suggestions" : + [ + { + "weight" : 5, + "term" : "test" + }, + { + "weight" : 1, + "term" : "test call" + }, + { + "weight" : 1, + "term" : "test plan" + } + ] + } + ]]> + + /slingshot/auto-suggest?t={term}&limit={limit?} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/auto-suggest-search.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/auto-suggest-search.get.json.ftl new file mode 100644 index 0000000000..b1d813cec8 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/auto-suggest-search.get.json.ftl @@ -0,0 +1,13 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "suggestions" : + [ + <#list suggestions as suggestion> + { + "weight" : ${suggestion.weight?c}, + "term" : "${suggestion.term}" + }<#if suggestion_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-docs.get.config.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-docs.get.config.xml new file mode 100644 index 0000000000..0dce806754 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-docs.get.config.xml @@ -0,0 +1,4 @@ + + AND + %(cm:name cm:title cm:description TEXT TAG) + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-docs.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-docs.get.desc.xml new file mode 100644 index 0000000000..1738f43e80 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-docs.get.desc.xml @@ -0,0 +1,9 @@ + + Share Live Search - Documents + Share Live Search Component Data Webscript + /slingshot/live-search-docs?t={term} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-docs.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-docs.get.js new file mode 100644 index 0000000000..a6e9c14a41 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-docs.get.js @@ -0,0 +1,24 @@ + + +function main() +{ + if (args.t === null || args.t.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "Query terms must be provided"); + return; + } + + var params = + { + type: "documents", + term: args.t, + siteId: args.s, + rootNode: (args.rootNode !== null) ? args.rootNode : null, + maxResults: (args.maxResults !== null) ? parseInt(args.maxResults, 10) : DEFAULT_MAX_RESULTS, + startIndex: (args.startIndex !== null) ? parseInt(args.startIndex, 10) : 0 + }; + + model.data = liveSearch(params); +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-docs.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-docs.get.json.ftl new file mode 100644 index 0000000000..63775207a9 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-docs.get.json.ftl @@ -0,0 +1,33 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalRecords": ${data.totalRecords?c}, + "startIndex": ${data.startIndex?c}, + "hasMoreRecords": ${data.hasMoreRecords?string}, + "items": + [ + <#list data.items as item> + { + "nodeRef": "${item.nodeRef}", + "name": "${item.name!''}", + "title": "${item.title!''}", + "description": "${item.description!''}", + "modifiedOn": "${xmldate(item.modifiedOn)}", + "modifiedBy": "${item.modifiedBy}", + <#if item.site??> + "site": + { + "shortName": "${item.site.shortName}", + "title": "${item.site.title}" + }, + "container": "${item.container}", + + <#if item.lastThumbnailModification??> + "lastThumbnailModification": "${item.lastThumbnailModification}", + + "size": ${item.size?c}, + "mimetype": "${item.mimetype!''}" + }<#if item_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-people.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-people.get.desc.xml new file mode 100644 index 0000000000..f722a3b508 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-people.get.desc.xml @@ -0,0 +1,9 @@ + + Share Live Search - People + Share Live Search Component Data Webscript + /slingshot/live-search-people?t={term} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-people.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-people.get.js new file mode 100644 index 0000000000..2762cfc6f1 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-people.get.js @@ -0,0 +1,22 @@ + + +function main() +{ + if (args.t === null || args.t.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "Query terms must be provided"); + return; + } + + var params = + { + type: "people", + term: args.t, + maxResults: (args.maxResults !== null) ? parseInt(args.maxResults, 10) : DEFAULT_MAX_RESULTS, + startIndex: (args.startIndex !== null) ? parseInt(args.startIndex, 10) : 0 + }; + + model.data = liveSearch(params); +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-people.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-people.get.json.ftl new file mode 100644 index 0000000000..8c8a564e13 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-people.get.json.ftl @@ -0,0 +1,26 @@ +<#macro personSummaryJSON person> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "url": "${url.serviceContext + "/api/people/" + person.properties.userName}", + "userName": "${person.properties.userName}", + "firstName": "${person.properties.firstName!""}", + "lastName": "${person.properties.lastName!""}", + "jobtitle": "${person.properties.jobtitle!""}", + "location": "${person.properties.location!""}", + "email": "${person.properties.email!""}", + "organization": "${person.properties.organization!""}" +} + + +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalRecords": ${data.totalRecords?c}, + "startIndex": ${data.startIndex?c}, + "items": + [ + <#list data.items as person> + <@personSummaryJSON person=person/><#if person_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-sites.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-sites.get.desc.xml new file mode 100644 index 0000000000..b29563dccb --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-sites.get.desc.xml @@ -0,0 +1,9 @@ + + Share Live Search - Sites + Share Live Search Component Data Webscript + /slingshot/live-search-sites?t={term} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-sites.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-sites.get.js new file mode 100644 index 0000000000..2ddc859df0 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-sites.get.js @@ -0,0 +1,22 @@ + + +function main() +{ + if (args.t === null || args.t.length === 0) + { + status.setCode(status.STATUS_BAD_REQUEST, "Query terms must be provided"); + return; + } + + var params = + { + type: "sites", + term: args.t, + maxResults: (args.maxResults !== null) ? parseInt(args.maxResults, 10) : DEFAULT_MAX_RESULTS, + startIndex: (args.startIndex !== null) ? parseInt(args.startIndex, 10) : 0 + }; + + model.data = liveSearch(params); +} + +main(); \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-sites.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-sites.get.json.ftl new file mode 100644 index 0000000000..986081f134 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search-sites.get.json.ftl @@ -0,0 +1,13 @@ +<#import "../../repository/site/site.lib.ftl" as siteLib/> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalRecords": ${data.totalRecords?c}, + "startIndex": ${data.startIndex?c}, + "items": + [ + <#list data.items as site> + <@siteLib.siteJSONManagers site=site roles="none"/><#if site_has_next>, + + ] +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search.lib.js new file mode 100644 index 0000000000..49ee71c51f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/live-search.lib.js @@ -0,0 +1,291 @@ +/** + * Live Search Component + * + * Takes the following object as Input: + * params + * { + * type: search mode type - one of "documents|sites|people" + * term: search terms + * maxResults: maximum results to return + * }; + * + * Outputs: + * items - Array of objects containing the search results + */ + +const DEFAULT_MAX_RESULTS = 5; +const SITES_SPACE_QNAME_PATH = "/app:company_home/st:sites/"; +const SURF_CONFIG_QNAMEPATH = "/cm:surf-config/"; + +/** + * Returns site information data structure. + * { shortName: siteId, title: title } + * + * Caches the data to avoid repeatedly querying the repository. + */ +var siteDataCache = {}; +function getSiteData(siteId) +{ + if (typeof siteDataCache[siteId] === "object") + { + return siteDataCache[siteId]; + } + var site = siteService.getSite(siteId); + var data = + { + shortName : siteId, + title : (site !== null ? site.title : "unknown") + }; + siteDataCache[siteId] = data; + return data; +} + +/** + * Return the fts-alfresco query template to use. + * The default searches name, title, descripton, calendar, link, full text and tag fields. + * It is configurable via the .config.xml attached to this webscript. + */ +function getQueryTemplate() +{ + var t = + [{ + field: "keywords", + template: "%(cm:name cm:title cm:description TEXT TAG)" + }], + qt = new XML(config.script)["default-query-template"]; + if (qt != null && qt.length() != 0) + { + t[0].template = qt.toString(); + } + return t; +} + +/** + * Process and return a document item node + */ +function getDocumentItem(container, node) +{ + // check whether this is a valid folder or a file + var item = null; + if (node.qnamePath.indexOf(SURF_CONFIG_QNAMEPATH) === -1) + { + if (node.isDocument) + { + item = + { + nodeRef: node.nodeRef.toString(), + name: node.name, + title: node.properties["cm:title"], + description: node.properties["cm:description"], + modifiedOn: node.properties["cm:modified"], + modifiedBy: node.properties["cm:modifier"], + createdOn: node.properties["cm:created"], + createdBy: node.properties["cm:creator"], + mimetype: node.mimetype, + size: node.size + }; + if (container.siteId !== null) + { + item.site = getSiteData(container.siteId); + item.container = container.containerId; + } + if (node.hasAspect("{http://www.alfresco.org/model/content/1.0}thumbnailModification")) + { + var dates = node.properties["lastThumbnailModification"]; + for (var i=0; i= 1) + { + var siteQName = Packages.org.alfresco.util.ISO9075.decode(tmp.split("/")[0]); + siteId = siteQName.substring(siteQName.indexOf(":") + 1); + tmp = tmp.substring(pos + 1); + pos = tmp.indexOf('/'); + if (pos >= 1) + { + // strip container id from the path + var containerId = tmp.substring(0, pos); + containerId = containerId.substring(containerId.indexOf(":") + 1); + + container.siteId = siteId; + container.containerId = containerId; + } + } + } + + return container; +} + +/** + * Dispatch a live search to the appropriate search method for the requested result type. + */ +function liveSearch(params) +{ + switch (params.type) + { + case "documents": + return getDocResults(params); + break; + case "sites": + return getSiteResults(params); + break; + case "people": + return getPeopleResults(params); + break; + } +} + +/** + * Return Document Search results with the given search terms. + * + * "AND" is the default operator unless configured otherwise, OR, AND and NOT are also supported - + * as is any other valid fts-alfresco elements such as "quoted terms" and (bracket terms) and also + * propname:propvalue syntax. + * + * @param params Object containing search parameters - see API description above + */ +function getDocResults(params) +{ + // ensure a TYPE is specified + var ftsQuery = params.term + ' AND +TYPE:"cm:content"'; + + // site constraint + if (params.siteId !== null) + { + // use SITE syntax to restrict to specific site + ftsQuery += ' AND SITE:"' + params.siteId + '"'; + } + + // root node - generally used for overridden Repository root in Share + if (params.rootNode !== null) + { + ftsQuery = 'PATH:"' + rootNode.qnamePath + '//*" AND (' + ftsQuery + ')'; + } + + // main query construction + ftsQuery = '(' + ftsQuery + ') AND -TYPE:"cm:thumbnail" AND -TYPE:"cm:failedThumbnail" AND -TYPE:"cm:rating" AND -TYPE:"fm:post" AND -ASPECT:"sys:hidden" AND -cm:creator:System'; + + if (logger.isLoggingEnabled()) + logger.log("LiveQuery:\r\n" + ftsQuery); + + // get default fts operator from the config + // + // TODO: common search lib - for both live and standard e.g. to get values like this... + // + var operator = "AND"; + var cf = new XML(config.script)["default-operator"]; + if (cf != null && cf.length != 0) + { + operator = cf.toString(); + } + + // perform fts-alfresco language query + var queryDef = { + query: ftsQuery, + language: "fts-alfresco", + templates: getQueryTemplate(), + defaultField: "keywords", + defaultOperator: operator, + onerror: "no-results", + page: { + maxItems: params.maxResults, + skipCount: params.startIndex + } + }; + var rs = search.queryResultSet(queryDef); + nodes = rs.nodes, + results = []; + + if (logger.isLoggingEnabled()) + logger.log("Processing resultset of length: " + nodes.length); + + for (var i=0, item; i + AND + %(cm:name cm:title cm:description ia:whatEvent ia:descriptionEvent lnk:title lnk:description TEXT TAG) + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.get.desc.xml new file mode 100644 index 0000000000..dbd125e39c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.get.desc.xml @@ -0,0 +1,9 @@ + + Share Search + Share Search Component Data Webscript + /slingshot/search?term={term?}&tag={tag?}&site={site?}&container={container?}&sort={sort?}&query={query?}&repo={repo?} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.get.html.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.get.html.ftl new file mode 100644 index 0000000000..db07bf09a0 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.get.html.ftl @@ -0,0 +1 @@ +<#include "search.get.json.ftl"> \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.get.js new file mode 100644 index 0000000000..aebd5ac93b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.get.js @@ -0,0 +1,43 @@ + +function main() +{ + var params = + { + siteId: args.site, + containerId: args.container, + repo: (args.repo !== null) ? (args.repo == "true") : false, + term: args.term, + tag: args.tag, + query: args.query, + rootNode: args.rootNode, + sort: args.sort, + maxResults: (args.maxResults !== null) ? parseInt(args.maxResults, 10) : DEFAULT_MAX_RESULTS, + pageSize: (args.pageSize !== null) ? parseInt(args.pageSize, 10) : DEFAULT_PAGE_SIZE, + startIndex: (args.startIndex !== null) ? parseInt(args.startIndex, 10) : 0, + facetFields: args.facetFields, + filters: args.filters, + encodedFilters: args.encodedFilters, + spell: (args.spellcheck !== null) ? (args.spellcheck == "true") : false + }; + + if (args.highlightFields) + { + // Data for search term highlighting... + params.highlightFields = args.highlightFields; + params.highlightPrefix = (args.highlightPrefix !== null) ? args.highlightPrefix : DEFAULT_HIGHLIGHT_POSTFIX; + params.highlightPostfix = (args.highlightPostfix !== null) ? args.highlightPostfix : DEFAULT_HIGHLIGHT_POSTFIX; + params.highlightSnippetCount = (args.highlightSnippetCount !== null) ? parseInt(args.highlightSnippetCount, 10) : DEFAULT_HIGHLIGHT_SNIPPET_COUNT; + params.highlightFragmentSize = (args.highlightFragmentSize !== null) ? parseInt(args.highlightFragmentSize, 10) : DEFAULT_HIGHLIGHT_FRAGMENT_SIZE; + params.highlightUsePhraseHighlighter = (args.highlightUsePhraseHighlighter !== null) ? args.highlightUsePhraseHighlighter.toUpperCase() === "TRUE" : DEFAULT_HIGHLIGHT_USE_PHRASE_HIGHLIGHTER; + params.highlightMergeContiguous = (args.highlightMergeContiguous !== null) ? args.highlightMergeContiguous.toUpperCase() === "TRUE" : DEFAULT_HIGHLIGHT_MERGE_CONTIGUOUS; + + if (args.highlightMaxAnalyzedChars !== null) + { + params.highlightMaxAnalyzedChars = parseInt(args.highlightMaxAnalyzedChars, 10) + } + } + + model.data = getSearchResults(params); +} + +main(); diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.get.json.ftl new file mode 100644 index 0000000000..598c244790 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.get.json.ftl @@ -0,0 +1,104 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalRecords": ${data.paging.totalRecords?c}, + "totalRecordsUpper": ${data.paging.totalRecordsUpper?c}, + "startIndex": ${data.paging.startIndex?c}, + "numberFound": ${(data.paging.numberFound!-1)?c}, + "facets": + { + <#if data.facets??><#list data.facets?keys as field> + "${field}": + [ + <#assign facets=data.facets[field]><#list facets as f> + { + "label": "${f.facetLabel}", + "value": "${f.facetValue}", + "hits": ${f.hits?c}, + "index": ${f.facetLabelIndex?c} + }<#if f_has_next>, + + ]<#if field_has_next>, + + }, + "highlighting": + { + <#if data.highlighting??><#list data.highlighting?keys as nodeRef> + "${nodeRef}": + { + <#assign fields=data.highlighting[nodeRef]><#list fields?keys as property> + "${property}": "${fields[property][0]}" + <#if property_has_next>, + + }<#if nodeRef_has_next>, + + }, + "items": + [ + <#list data.items as item> + { + "node": <#noescape>${item.nodeJSON}, + "nodeRef": "${item.nodeRef}", + "type": "${item.type}", + "name": "${item.name!''}", + "displayName": "${item.displayName!''}", + <#if item.title??> + "title": "${item.title}", + + "description": "${item.description!''}", + "modifiedOn": "${xmldate(item.modifiedOn)}", + "modifiedByUser": "${item.modifiedByUser}", + "modifiedBy": "${item.modifiedBy}", + "fromDate": "${xmldate(item.fromDate)}", + "size": ${item.size?c}, + "mimetype": "${item.mimetype!''}", + <#if item.site??> + "site": + { + "shortName": "${item.site.shortName}", + "title": "${item.site.title}" + }, + "container": "${item.container}", + + <#if item.path??> + "path": "${item.path}", + + "lastThumbnailModification": + [ + <#if item.lastThumbnailModification??> + <#list item.lastThumbnailModification as lastThumbnailMod> + "${lastThumbnailMod}" + <#if lastThumbnailMod_has_next>, + + + ], + "tags": [<#list item.tags as tag>"${tag}"<#if tag_has_next>,], + "highlighting": + { + <#list item.highlighting?keys as property> + "${property}": "${item.highlighting[property][0]}" + <#if property_has_next>, + + } + }<#if item_has_next>, + + ], + "spellcheck": + { + <#if data.spellcheck?? && data.spellcheck.spellCheckExist> + "searchRequest": "${data.spellcheck.originalSearchTerm}", + <#if data.spellcheck.searchedFor> + <#list data.spellcheck.results as collationQueryStr> + "searchedFor": "${collationQueryStr?string}" + <#break> + + <#else> + "searchSuggestions": [ + <#list data.spellcheck.results as suggestion> + "${suggestion?string}"<#if suggestion_has_next>, + + ] + + + } +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.lib.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.lib.js new file mode 100644 index 0000000000..14328b4c0f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/search/search.lib.js @@ -0,0 +1,1438 @@ +/** + * Search Component + * + * Takes the following object as Input: + * params + * { + * siteId: the site identifier to search into, null for all sites + * containerId: the component the search in, null for all components in the site + * term: search terms + * tag: search tag + * query: advanced search query json + * sort: sort parameter + * maxResults: maximum results to return + * }; + * + * Outputs: + * items - Array of objects containing the search results + */ + +const DEFAULT_MAX_RESULTS = 250; +const DEFAULT_PAGE_SIZE = 50; +const SITES_SPACE_QNAME_PATH = "/app:company_home/st:sites/"; + +const DEFAULT_HIGHLIGHT_FIELDS = "cm:name,cm:description,cm:title,content,ia:descriptionEvent,ia:whatEvent,lnk:title"; +const DEFAULT_HIGHLIGHT_PREFIX = "\u0000"; +const DEFAULT_HIGHLIGHT_POSTFIX = "\u0003"; +const DEFAULT_HIGHLIGHT_SNIPPET_COUNT = 255; +const DEFAULT_HIGHLIGHT_FRAGMENT_SIZE = 100; +const DEFAULT_HIGHLIGHT_USE_PHRASE_HIGHLIGHTER = true; +const DEFAULT_HIGHLIGHT_MERGE_CONTIGUOUS = true; + +/** + * Returns site information data structure. + * { shortName: siteId, title: title } + * + * Caches the data to avoid repeatedly querying the repository. + */ +var siteDataCache = {}; +function getSiteData(siteId) +{ + if (typeof siteDataCache[siteId] === "object") + { + return siteDataCache[siteId]; + } + var site = siteService.getSite(siteId); + var data = + { + shortName : siteId, + title : (site !== null ? site.title : "unknown") + }; + siteDataCache[siteId] = data; + return data; +} + +/** + * Returns person display name string as returned to the user. + * + * Caches the person full name to avoid repeatedly querying the repository. + */ +var personDataCache = {}; +function getPersonDisplayName(userId) +{ + if (typeof personDataCache[userId] === "object") + { + return personDataCache[userId]; + } + + var displayName = people.getPersonFullName(userId); + if (displayName == null) + { + displayName = ""; + } + personDataCache[userId] = displayName; + return displayName; +} + +/** + * Cache to not display twice the same element (e.g. if two comments of the + * same blog post match the search criteria + */ +var processedCache; +function checkProcessedCache(key) +{ + var found = processedCache.hasOwnProperty(key); + if (!found) + { + processedCache[key] = true; + } + else if (found && logger.isLoggingEnabled()) + logger.log("...already processed item with key: " + key); + return found; +} + +/** + * Returns an item outside of a site in the main repository. + */ +function getRepositoryItem(folderPath, node, populate, highlighting) +{ + // check whether we already processed this document + if (checkProcessedCache("" + node.nodeRef.toString())) + { + return null; + } + + // check whether this is a valid folder or a file + var item = t = null; + if (node.isContainer || node.isDocument) + { + if (!populate) return {}; + item = + { + nodeRef: node.nodeRef.toString(), + tags: ((t = node.tags) !== null) ? t : [], + name: node.name, + displayName: highlighting["cm:name"] ? highlighting["cm:name"].get(0) : node.name, + title: highlighting["cm:title"] ? highlighting["cm:title"].get(0) : node.properties["cm:title"], + description:highlighting["cm:description"] ? highlighting["cm:description"].get(0) : node.properties["cm:description"], + modifiedOn: node.properties["cm:modified"], + modifiedByUser: node.properties["cm:modifier"], + createdOn: node.properties["cm:created"], + createdByUser: node.properties["cm:creator"], + lastThumbnailModification: node.properties["cm:lastThumbnailModification"], + mimetype: node.mimetype, + path: folderPath.join("/"), + nodeJSON: appUtils.toJSON(node, true) + }; + item.modifiedBy = getPersonDisplayName(item.modifiedByUser); + item.createdBy = getPersonDisplayName(item.createdByUser); + } + if (node.isContainer) + { + item.type = "folder"; + item.size = -1; + } + else if (node.isDocument) + { + item.type = "document"; + item.size = node.size; + } + + return item; +} + +/** + * Returns an item of the document library component. + */ +function getDocumentItem(siteId, containerId, pathParts, node, populate, highlighting) +{ + // PENDING: how to handle comments? the document should + // be returned instead + + // check whether we already processed this document + if (checkProcessedCache("" + node.nodeRef.toString())) + { + return null; + } + + // check whether this is a valid folder or a file + var item = t = null; + if (node.isContainer || node.isDocument) + { + if (!populate) return {}; + item = + { + site: getSiteData(siteId), + container: containerId, + nodeRef: node.nodeRef.toString(), + tags: ((t = node.tags) !== null) ? t : [], + name: node.name, + displayName: highlighting["cm:name"] ? highlighting["cm:name"].get(0) : node.name, + title: highlighting["cm:title"] ? highlighting["cm:title"].get(0) : node.properties["cm:title"], + description: highlighting["cm:description"] ? highlighting["cm:description"].get(0) : node.properties["cm:description"], + modifiedOn: node.properties["cm:modified"], + modifiedByUser: node.properties["cm:modifier"], + createdOn: node.properties["cm:created"], + createdByUser: node.properties["cm:creator"], + lastThumbnailModification: node.properties["cm:lastThumbnailModification"], + mimetype: node.mimetype, + path: pathParts.join("/"), + nodeJSON: appUtils.toJSON(node, true) + }; + item.modifiedBy = getPersonDisplayName(item.modifiedByUser); + item.createdBy = getPersonDisplayName(item.createdByUser); + } + if (node.isContainer) + { + item.type = "folder"; + item.size = -1; + } + else if (node.isDocument) + { + item.type = "document"; + item.size = node.size; + } + + return item; +} + +function getBlogPostItem(siteId, containerId, pathParts, node, populate, highlighting) +{ + /** + * Investigate the rest of the path. the first item is the blog post, ignore everything that follows + * are replies or folders + */ + var site = siteService.getSite(siteId); + if (site === null) + { + return null; + } + var container = site.getContainer(containerId); + + /** + * Find the direct child of the container + * Note: this only works for post which are direct children of the blog container + */ + var child = node; + var parent = child.parent; + while ((parent !== null) && (!parent.nodeRef.equals(container.nodeRef))) + { + child = parent; + parent = parent.parent; + } + + // check whether we found the container + if (parent === null) + { + return null; + } + + // check whether we already added this blog post + if (checkProcessedCache("" + child.nodeRef.toString())) + { + return null; + } + + // child is our blog post + if (!populate) return {}; + var item, t = null; + item = + { + site: getSiteData(siteId), + container: containerId, + nodeRef: child.nodeRef.toString(), + type: "blogpost", + tags: ((t = child.tags) !== null) ? t : [], + name: child.name, + modifiedOn: child.properties["cm:modified"], + modifiedByUser: child.properties["cm:modifier"], + createdOn: node.properties["cm:created"], + createdByUser: node.properties["cm:creator"], + size: child.size, + displayName: highlighting["cm:title"] ? highlighting["cm:title"].get(0) : child.properties["cm:title"], + nodeJSON: appUtils.toJSON(node, true) + }; + item.modifiedBy = getPersonDisplayName(item.modifiedByUser); + item.createdBy = getPersonDisplayName(item.createdByUser); + + return item; +} + +function getForumPostItem(siteId, containerId, pathParts, node, populate, highlighting) +{ + // try to find the first fm:topic node, that's what we return as search result + var topicNode = node; + while ((topicNode !== null) && (topicNode.type != "{http://www.alfresco.org/model/forum/1.0}topic")) + { + topicNode = topicNode.parent; + } + if (topicNode === null) + { + return null; + } + + // make sure we haven't already added the post + if (checkProcessedCache("" + topicNode.nodeRef.toString())) + { + return null; + } + + // find the first post, which contains the post title + // PENDING: error prone + var title =""; + var postNode; + try + { + var postNode = topicNode.childAssocs["cm:contains"].get(0); + title = postNode.properties["cm:title"]; + } + catch(e1) + { + try + { + postNode = topicNode.childAssocs["cm:contains"][0]; + title = postNode.properties["cm:title"]; + } + catch(e2) {} + } + + // child is our forum post + if (!populate) return {}; + var item = t = null; + item = + { + site: getSiteData(siteId), + container: containerId, + nodeRef: topicNode.nodeRef.toString(), + type: "forumpost", + tags: ((t = topicNode.tags) !== null) ? t : [], + name: topicNode.name, + description: highlighting["cm:description"] ? highlighting["cm:description"].get(0) : topicNode.properties["cm:description"], + modifiedOn: topicNode.properties["cm:modified"], + modifiedByUser: topicNode.properties["cm:modifier"], + createdOn: node.properties["cm:created"], + createdByUser: node.properties["cm:creator"], + size: topicNode.size, + displayName: highlighting["cm:title"] ? highlighting["cm:title"].get(0) : title, + nodeJSON: appUtils.toJSON(node, true) + }; + item.modifiedBy = getPersonDisplayName(item.modifiedByUser); + item.createdBy = getPersonDisplayName(item.createdByUser); + + return item; +} + +function getCalendarItem(siteId, containerId, pathParts, node, populate, highlighting) +{ + // only process nodes of the correct type + if (node.type != "{http://www.alfresco.org/model/calendar}calendarEvent") + { + return null; + } + + // make sure we haven't already added the event + if (checkProcessedCache("" + node.nodeRef.toString())) + { + return null; + } + + if (!populate) return {}; + var item, t = null; + item = + { + site: getSiteData(siteId), + container: containerId, + nodeRef: node.nodeRef.toString(), + type: "calendarevent", + tags: ((t = node.tags) !== null) ? t : [], + name: node.name, + description: highlighting["ia:descriptionEvent"] ? highlighting["ia:descriptionEvent"].get(0) : node.properties["ia:descriptionEvent"], + modifiedOn: node.properties["cm:modified"], + modifiedByUser: node.properties["cm:modifier"], + createdOn: node.properties["cm:created"], + createdByUser: node.properties["cm:creator"], + fromDate: node.properties["ia:fromDate"], + size: -1, + displayName: highlighting["ia:whatEvent"] ? highlighting["ia:whatEvent"].get(0) : node.properties["ia:whatEvent"], + nodeJSON: appUtils.toJSON(node, true) + }; + item.modifiedBy = getPersonDisplayName(item.modifiedByUser); + item.createdBy = getPersonDisplayName(item.createdByUser); + + return item; +} + +function getWikiItem(siteId, containerId, pathParts, node, populate, highlighting) +{ + // only process documents + if (!node.isDocument) + { + return null; + } + + // make sure we haven't already added the page + if (checkProcessedCache("" + node.nodeRef.toString())) + { + return null; + } + + if (!populate) return {}; + var item, t = null; + item = + { + site: getSiteData(siteId), + container: containerId, + nodeRef: node.nodeRef.toString(), + type: "wikipage", + tags: ((t = node.tags) !== null) ? t : [], + name: node.name, + description: highlighting["cm:description"] ? highlighting["cm:description"].get(0) : node.properties["cm:description"], + modifiedOn: node.properties["cm:modified"], + modifiedByUser: node.properties["cm:modifier"], + createdOn: node.properties["cm:created"], + createdByUser: node.properties["cm:creator"], + size: node.size, + displayName: highlighting["cm:name"] ? highlighting["cm:name"].get(0) : ("" + node.name).replace(/_/g, " "), + nodeJSON: appUtils.toJSON(node, true) + }; + item.modifiedBy = getPersonDisplayName(item.modifiedByUser); + item.createdBy = getPersonDisplayName(item.createdByUser); + + return item; +} + +function getLinkItem(siteId, containerId, pathParts, node, populate, highlighting) +{ + // only process documents + if (!node.isDocument) + { + return null; + } + + // make sure we haven't already added this link + if (checkProcessedCache("" + node.nodeRef.toString())) + { + return null; + } + + var item = t = null; + if (!populate) return {}; + item = + { + site: getSiteData(siteId), + container: containerId, + nodeRef: node.nodeRef.toString(), + type: "link", + tags: ((t = node.tags) !== null) ? t : [], + name: node.name, + description: highlighting["cm:description"] ? highlighting["cm:description"].get(0) : node.properties["cm:description"], + modifiedOn: node.properties["cm:modified"], + modifiedByUser: node.properties["cm:modifier"], + createdOn: node.properties["cm:created"], + createdByUser: node.properties["cm:creator"], + size: -1, + displayName: highlighting["lnk:title"] ? highlighting["lnk:title"].get(0) : node.properties["lnk:title"], + nodeJSON: appUtils.toJSON(node, true) + }; + item.modifiedBy = getPersonDisplayName(item.modifiedByUser); + item.createdBy = getPersonDisplayName(item.createdByUser); + + return item; +} + +function getDataItem(siteId, containerId, pathParts, node, populate, highlighting) +{ + // make sure we haven't already added this item + if (checkProcessedCache("" + node.nodeRef.toString())) + { + return null; + } + + var item = null; + + // data item can be either ba containing dl:dataList or any dl:dataListItem subtype + if (node.type == "{http://www.alfresco.org/model/datalist/1.0}dataList") + { + if (!populate) return {}; + // found a data list + item = + { + site: getSiteData(siteId), + container: containerId, + nodeRef: node.nodeRef.toString(), + type: "datalist", + tags: [], + name: node.name, + description: highlighting["cm:description"] ? highlighting["cm:description"].get(0) : node.properties["cm:description"], + modifiedOn: node.properties["cm:modified"], + modifiedByUser: node.properties["cm:modifier"], + createdOn: node.properties["cm:created"], + createdByUser: node.properties["cm:creator"], + size: -1, + displayName: highlighting["cm:title"] ? highlighting["cm:title"].get(0) : node.properties["cm:title"], + nodeJSON: appUtils.toJSON(node, true) + }; + item.modifiedBy = getPersonDisplayName(item.modifiedByUser); + item.createdBy = getPersonDisplayName(item.createdByUser); + } + else if (node.isSubType("{http://www.alfresco.org/model/datalist/1.0}dataListItem")) + { + if (!populate) return {}; + // found a data list item + item = + { + site: getSiteData(siteId), + container: containerId, + nodeRef: node.nodeRef.toString(), + type: "datalistitem", + tags: [], + name: node.parent.name, // used to generate link to parent datalist - not ideal + modifiedOn: node.properties["cm:modified"], + modifiedByUser: node.properties["cm:modifier"], + createdOn: node.properties["cm:created"], + createdByUser: node.properties["cm:creator"], + size: -1, + nodeJSON: appUtils.toJSON(node, true), + displayName: highlighting["cm:name"] ? highlighting["cm:name"].get(0) : node.name // unfortunately does not have a common display name property + }; + item.modifiedBy = getPersonDisplayName(item.modifiedByUser); + item.createdBy = getPersonDisplayName(item.createdByUser); + } + + return item; +} + +/** + * Delegates the extraction to the correct extraction function + * depending on containerId. + */ +function getItem(siteId, containerId, pathParts, node, populate, meta) +{ + var highlighting = {}; + if (meta && meta.highlighting && node && node.nodeRef) + { + highlighting = meta.highlighting[node.nodeRef.toString()] || {}; + } + + var item = null; + if (siteId == null) + { + item = getRepositoryItem(pathParts, node, populate, highlighting); + } + else + { + switch ("" + containerId.toLowerCase()) + { + case "documentlibrary": + item = getDocumentItem(siteId, containerId, pathParts, node, populate, highlighting); + break; + case "blog": + item = getBlogPostItem(siteId, containerId, pathParts, node, populate, highlighting); + break; + case "discussions": + item = getForumPostItem(siteId, containerId, pathParts, node, populate, highlighting); + break; + case "calendar": + item = getCalendarItem(siteId, containerId, pathParts, node, populate, highlighting); + break; + case "wiki": + item = getWikiItem(siteId, containerId, pathParts, node, populate, highlighting); + break; + case "links": + item = getLinkItem(siteId, containerId, pathParts, node, populate, highlighting); + break; + case "datalists": + item = getDataItem(siteId, containerId, pathParts, node, populate, highlighting); + break; + } + } + + if (meta && meta.highlighting) + { + item.highlighting = highlighting; + } + + return item; +} + +/** + * Splits the qname path to a node to extract Site information and display path parts. + * The display path will be truncated to match the overridden root node if present. + * + * Returns an array with: + * [0] = site or null + * [1] = container or null + * [2] = remaining part of the cm:name based path to the object - as an array + */ +function splitQNamePath(node, rootNodeDisplayPath, rootNodeQNamePath, qnameOnly) +{ + var path = node.qnamePath, + displayPath = qnameOnly ? null : utils.displayPath(node).split("/"), + siteId = null, + containerId = null; + + if (path.match("^"+SITES_SPACE_QNAME_PATH) == SITES_SPACE_QNAME_PATH) + { + // this item is contained within a Site + + // ensure we have not matched a Site folder directly or some node created under the st:sites folder that is not a Site + var qpathUnderSitesFolder = path.substring(SITES_SPACE_QNAME_PATH.length), + positionContainer = qpathUnderSitesFolder.indexOf("/"); + if (positionContainer !== -1) + { + // Decode the Site ID from the qname path using the util method - as we may not have displayPath + // the displayPath will look something like this: ["", "Company Home", "Sites", "MySite", "documentLibrary", "MyFolder"] + var siteQName = Packages.org.alfresco.util.ISO9075.decode(qpathUnderSitesFolder.substring(0, positionContainer)); + siteId = siteQName.substring(siteQName.indexOf(":") + 1); + var qpathContainer = qpathUnderSitesFolder.substring(positionContainer + 1); + var positionUnderContainer = qpathContainer.indexOf("/"); + if (positionUnderContainer !== -1) + { + // extract container id from the qname path - strip off namespace + containerId = qpathContainer.substring(0, positionUnderContainer); + containerId = containerId.substring(containerId.indexOf(":") + 1); + + // construct remaining part of the cm:name based display path to the object + // by removing everything up to the path of the item under the container folder + if (!qnameOnly) displayPath = displayPath.slice(5, displayPath.length); + } + } + } + else + { + // check if we have an overridden root node and the node is under that path + if (!qnameOnly && rootNodeDisplayPath !== null && path.indexOf(rootNodeQNamePath) === 0) + { + // restructure the display path of the node + displayPath = displayPath.splice(rootNodeDisplayPath.length); + // empty element is required at the start of the repository paths - we want to show it as the repo root later + displayPath.unshift(""); + } + } + + return [ siteId, containerId, displayPath ]; +} + +/** + * Processes the search results, extracting the given page of results from the startIndex up to the maxPageResults + * from the total list of nodes passed in. Filters out unnecessary nodes + * + * @return the final search results object + */ +function processResults(nodes, maxPageResults, startIndex, rootNode, meta) +{ + // empty cache state + processedCache = {}; + var results = [], + added = processed = failed = 0, + parts, + item, + rootNodeDisplayPath = rootNode ? utils.displayPath(rootNode).split("/") : null, + rootNodeQNamePath = rootNode ? rootNode.qnamePath : null; + + if (logger.isLoggingEnabled()) + logger.log("Processing resultset of length: " + nodes.length); + + startIndex = startIndex ? startIndex : 0; + for (var i = 0, j = nodes.length; i < j; i++) + { + // For each node we extract the site/container qname path and then + // let the per-container helper function decide what to do. + try + { + // We only want to populate node return structures if we are going to add the items to the results, + // so we skip (process, but don't populate or add to results) until we have reached the startIndex + // then we populate and add items up to the maxPageResults - after that we still need to process + // (but don't populate or add to results) each item to correctly calculate the totalRecordsUpper. + var populate = (processed >= startIndex && added < maxPageResults); + parts = splitQNamePath(nodes[i], rootNodeDisplayPath, rootNodeQNamePath, !populate); + item = getItem(parts[0], parts[1], parts[2], nodes[i], populate, meta); + if (item !== null) + { + processed++; + if (populate) + { + results.push(item); + added++; + } + } + else + { + failed++; + } + } + catch (e) + { + // THOR-833 + if (logger.isWarnLoggingEnabled() == true) + { + logger.warn("search.lib.js: Skipping node due to exception when processing query result: " + e); + logger.warn("..." + nodes[i].nodeRef); + } + + failed++; + } + } + + if (logger.isLoggingEnabled()) + logger.log("Filtered resultset to length: " + results.length + ". Discarded item count: " + failed); + + return ( + { + paging: + { + totalRecords: results.length, + totalRecordsUpper: nodes.length - failed, + startIndex: startIndex, + numberFound: meta ? meta.numberFound : -1 + }, + facets: meta ? meta.facets : null, + highlighting: meta ? meta.highlighting : null, + items: results, + spellcheck: meta ? meta.spellcheck : null + }); +} + +/** + * Processes the search results for a single page. Filters out unnecessary nodes + * + * @return the final search results object + */ +function processResultsSinglePage(nodes, startIndex, rootNode, meta) +{ + // empty cache state + processedCache = {}; + var results = [], + failed = 0, + parts, + item, + rootNodeDisplayPath = rootNode ? utils.displayPath(rootNode).split("/") : null, + rootNodeQNamePath = rootNode ? rootNode.qnamePath : null; + + if (logger.isLoggingEnabled()) + logger.log("Processing resultset of length: " + nodes.length); + + for (var i = 0, j = nodes.length; i < j; i++) + { + // For each node we extract the site/container qname path and then + // let the per-container helper function decide what to do. + try + { + parts = splitQNamePath(nodes[i], rootNodeDisplayPath, rootNodeQNamePath, false); + item = getItem(parts[0], parts[1], parts[2], nodes[i], true, meta); + if (item !== null) + { + results.push(item); + } + else + { + failed++; + } + } + catch (e) + { + // THOR-833 + if (logger.isWarnLoggingEnabled() == true) + { + logger.warn("search.lib.js: Skipping node due to exception when processing query result: " + e); + logger.warn("..." + nodes[i].nodeRef); + } + failed++; + } + } + + if (logger.isLoggingEnabled()) + logger.log("Filtered resultset to length: " + results.length + ". Discarded item count: " + failed); + + return ( + { + paging: + { + totalRecords: results.length, + totalRecordsUpper: -1, + startIndex: startIndex, + numberFound: meta ? meta.numberFound : -1 + }, + facets: meta ? meta.facets : null, + highlighting: meta ? meta.highlighting : null, + items: results, + spellcheck: meta ? meta.spellcheck : null + }); +} + +/** + * Helper to escape the QName string so it is valid inside an fts-alfresco query. + * The language supports the SQL92 identifier standard. + * + * @param qname The QName string to escape + * @return escaped string + */ +function escapeQName(qname) +{ + var separator = qname.indexOf(':'), + namespace = qname.substring(0, separator), + localname = qname.substring(separator + 1); + + return escapeString(namespace) + ':' + escapeString(localname); +} + +function escapeString(value) +{ + var result = ""; + + for (var i=0,c; i= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) + { + result += '\\'; + } + } + else + { + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '#')) + { + result += '\\'; + } + } + result += c; + } + return result; +} + +/** + * Helper method used to determine whether the property value is multi-valued. + * + * @param propValue the property value to test + * @param modePropValue the logical operand that should be used for multi-value property + * @return true if it is multi-valued, false otherwise + */ +function isMultiValueProperty(propValue, modePropValue) +{ + return modePropValue != null && propValue.indexOf(",") !== -1; +} + +/** + * Helper method used to construct lucene query fragment for a multi-valued property. + * + * @param propName property name + * @param propValue property value (comma separated) + * @param operand logical operand that should be used + * @param pseudo is it a pseudo property + * @return lucene query with multi-valued property + */ +function processMultiValue(propName, propValue, operand, pseudo) +{ + var multiValue = propValue.split(","), + formQuery = ""; + for (var i = 0; i < multiValue.length; i++) + { + if (i > 0) + { + formQuery += ' ' + operand + ' '; + } + + if (pseudo) + { + formQuery += '(cm:content.' + propName + ':"' + multiValue[i] + '")'; + } + else + { + formQuery += '(' + escapeQName(propName) + ':"' + multiValue[i] + '")'; + } + } + + return formQuery; +} + +/** + * Resolve a root node reference to use as the Repository root for a search. + * + * NOTE: see ParseArgs.resolveNode() + * + * @method resolveRootNode + * @param reference {string} "virtual" nodeRef, nodeRef or xpath expressions + * @return {ScriptNode|null} Node corresponding to supplied expression. Returns null if node cannot be resolved. + */ +function resolveRootNode(reference) +{ + try + { + var node = utils.resolveNodeReference(reference); + if (node === null) + { + logger.warn("Unable to resolve specified root node reference: " + reference); + } + else if (companyhome.nodeRef === node.nodeRef) + { + // default root node - no special handling required + return null; + } + return node; + } + catch (e) + { + logger.warn("Unable to resolve specified root node reference: " + reference); + return null; + } +} + +/** + * Return Search results with the given search terms. + * + * "or" is the default operator, AND and NOT are also supported - as is any other valid fts-alfresco + * elements such as "quoted terms" and (bracket terms) and also propname:propvalue syntax. + * + * @param params Object containing search parameters - see API description above + */ +function getSearchResults(params) +{ + var nodes, + ftsQuery = "", + term = params.term, + tag = params.tag, + formData = params.query, + rootNode = params.rootNode ? resolveRootNode(params.rootNode) : null; + + // Simple keyword search and tag specific search + if (term !== null && term.length !== 0) + { + // TAG is now part of the default search macro + ftsQuery = term + " "; + } + else if (tag !== null && tag.length !== 0) + { + // Just look for tag + ftsQuery = "TAG:" + tag +" "; + } + + // Advanced search form data search. + // Supplied as json in the standard Alfresco Forms data structure: + // prop_:value|assoc_:value + // name = namespace_propertyname|pseudopropertyname + // value = string value - comma separated for multi-value, no escaping yet! + // - underscore represents colon character in name + // - pseudo property is one of any cm:content url property: mimetype|encoding|size + // - always string values - interogate DD for type data + // - an additional "-mode" suffixed parameter for a value is allowed to specify + // either an AND or OR join condition for multi-value property searches + if (formData !== null && formData.length !== 0) + { + var formQuery = "", + formJson = jsonUtils.toObject(formData); + + // extract form data and generate search query + var first = true; + var useSubCats = false; + for (var p in formJson) + { + // retrieve value and check there is someting to search for + // currently all values are returned as strings + var propValue = formJson[p], modePropValue = formJson[p + "-mode"]; + if (propValue.length !== 0) + { + if (p.indexOf("prop_") === 0 && p.match("-mode$") != "-mode") + { + // found a property - is it namespace_propertyname or pseudo property format? + var propName = p.substr(5); + if (propName.indexOf("_") !== -1) + { + // property name - convert to DD property name format + propName = propName.replace("_", ":"); + + // special case for range packed properties + if (propName.match("-range$") == "-range") + { + // currently support text based ranges (usually numbers) or date ranges + // range value is packed with a | character separator + + // if neither value is specified then there is no need to add the term + if (propValue.length > 1) + { + var from, to, sepindex = propValue.indexOf("|"); + if (propName.match("-date-range$") == "-date-range") + { + // date range found + propName = propName.substr(0, propName.length - "-date-range".length) + + // work out if "from" and/or "to" are specified - use MIN and MAX otherwise; + // we only want the "YYYY-MM-DD" part of the ISO date value - so crop the strings + from = (sepindex === 0 ? "MIN" : propValue.substr(0, 10)); + to = (sepindex === propValue.length - 1 ? "MAX" : propValue.substr(sepindex + 1, 10)); + } + else + { + // simple range found + propName = propName.substr(0, propName.length - "-range".length); + + // work out if "min" and/or "max" are specified - use MIN and MAX otherwise + from = (sepindex === 0 ? "MIN" : propValue.substr(0, sepindex)); + to = (sepindex === propValue.length - 1 ? "MAX" : propValue.substr(sepindex + 1)); + } + formQuery += (first ? '' : ' AND ') + escapeQName(propName) + ':"' + from + '".."' + to + '"'; + first = false; + } + } + else if (isCategoryProperty(formJson, p)) + { + // If there's no suffix it means this property holds the value for categories + if (propName.indexOf("usesubcats") == -1 && propName.indexOf("isCategory") == -1) + { + // Determines if the checkbox use sub categories was clicked + if (formJson[p + "_usesubcats"] == "true") + { + useSubCats = true; + } + + // Build list of category terms to search for + var catQuery = ""; + var cats = propValue.split(','); + if (propName.indexOf("cm:categories") != -1) + { + catQuery = processDefaultCategoryProperty(cats, useSubCats); + } + else + { + catQuery = processCustomCategoryProperty(propName, cats, useSubCats); + } + + if (catQuery.length !== 0) + { + // surround category terms with brackets if appropriate + formQuery += (first ? '' : ' AND ') + "(" + catQuery + ")"; + first = false; + } + } + } + else if (isMultiValueProperty(propValue, modePropValue) || isListProperty(formJson, p)) + { + if(propName.indexOf('isListProperty') === -1) + { + formQuery += (first ? '(' : ' AND ('); + formQuery += processMultiValue(propName, propValue, modePropValue, false); + formQuery += ')'; + first = false; + } + } + else + { + if (propValue.charAt(0) === '"' && propValue.charAt(propValue.length-1) === '"') + { + formQuery += (first ? '' : ' AND ') + escapeQName(propName) + ':' + propValue; + } + else + { + var index = propValue.lastIndexOf(" "); + formQuery += (first ? '' : ' AND ') + escapeQName(propName); + if (index > 0 && index < propValue.length - 1) + { + formQuery += ':(' + propValue + ')'; + } + else + { + formQuery += ':"' + propValue + '"'; + } + } + first = false; + } + } + else + { + if (isMultiValueProperty(propValue, modePropValue)) + { + // multi-valued pseudo cm:content property - e.g. mimetype, size or encoding + formQuery += (first ? '(' : ' AND ('); + formQuery += processMultiValue(propName, propValue, modePropValue, true); + formQuery += ')'; + } + else + { + // single pseudo cm:content property - e.g. mimetype, size or encoding + formQuery += (first ? '' : ' AND ') + 'cm:content.' + propName + ':"' + propValue + '"'; + } + first = false; + } + } + } + } + + if (formQuery.length !== 0 || ftsQuery.length !== 0) + { + // extract data type for this search - advanced search query is type specific + ftsQuery = 'TYPE:"' + formJson.datatype + '"' + + (formQuery.length !== 0 ? ' AND (' + formQuery + ')' : '') + + (ftsQuery.length !== 0 ? ' AND (' + ftsQuery + ')' : ''); + } + } + + if (ftsQuery.length !== 0) + { + // Filter queries + var fqs = []; + if (params.filters) + { + var filters = []; + if (params.encodedFilters) + { + var encodedFilters = params.encodedFilters.split(","); + for(var i=0; i 1) + { + // special case for some filters e.g. TYPE content or folder + switch (filterParts[0]) + { + case "TYPE": + { + ftsQuery += ' AND +TYPE:"' + filterParts[1] + '"'; + break; + } + default: + { + // facet filtering selection - reduce query results + // bracket each filter part within the attribute statement + ftsQuery += ' AND (' + filterParts[0] + ':('; + for (var p=1; p 0 ) + { + if (params.containerId !== null && params.containerId.length > 0) + { + // using PATH to restrict to container and site + path = SITES_SPACE_QNAME_PATH; + path += "cm:" + search.ISO9075Encode(params.siteId) + "/"; + path += "cm:" + search.ISO9075Encode(params.containerId) + "/"; + } + else + { + // use SITE syntax to restrict to specific site + site = "SITE:\"" + params.siteId + "\"" ; + } + } + else + { + if (params.containerId !== null && params.containerId.length > 0) + { + // using PATH to restrict to container and site + path = SITES_SPACE_QNAME_PATH; + path += "*/"; + path += "cm:" + search.ISO9075Encode(params.containerId) + "/"; + } + else + { + // use SITE syntax to restrict to specific site + site = "SITE:\"_ALL_SITES_\"" ; + } + } + } + + // root node - generally used for overridden Repository root in Share + if (params.repo && rootNode !== null) + { + ftsQuery = 'PATH:"' + rootNode.qnamePath + '//*" AND (' + ftsQuery + ')'; + } + else if (path !== null) + { + fqs.push('PATH:"' + path + '/*"'); + } + else if (site !== null) + { + fqs.push(site); + } + + fqs.push('-TYPE:"cm:thumbnail" AND -TYPE:"cm:failedThumbnail" AND -TYPE:"cm:rating" AND -TYPE:"st:site"' + + ' AND -ASPECT:"st:siteContainer" AND -ASPECT:"sys:hidden" AND -cm:creator:System AND -QNAME:comment\\-*'); + + // sort field - expecting field to in one of the following formats: + // - short QName form such as: cm:name + // - pseudo cm:content field starting with "." such as: .size + // - any other directly supported search field such as: TYPE + var sortColumns = []; + var sort = params.sort; + if (sort != null && sort.length != 0) + { + var asc = true; + var separator = sort.indexOf("|"); + if (separator != -1) + { + asc = (sort.substring(separator + 1) == "true"); + sort = sort.substring(0, separator); + } + var column; + if (sort.charAt(0) == '.') + { + // handle pseudo cm:content fields + column = "@{http://www.alfresco.org/model/content/1.0}content" + sort; + } + else if (sort.indexOf(":") != -1) + { + // handle attribute field sort + column = "@" + utils.longQName(sort); + } + else + { + // other sort types e.g. TYPE + column = sort; + } + sortColumns.push( + { + column: column, + ascending: asc + }); + } + + if (logger.isLoggingEnabled()) + logger.log("Query:\r\n" + ftsQuery + "\r\nSortby: " + (sort != null ? sort : "")); + + // perform fts-alfresco language query + var qt = getQueryTemplate(); + var queryDef = { + query: ftsQuery, + language: "fts-alfresco", + page: { + maxItems: params.maxResults > 0 ? params.maxResults + 1 : params.pageSize, + skipCount: params.maxResults > 0 ? 0 : params.startIndex + }, + templates: qt.template, + defaultField: "keywords", + defaultOperator: qt.operator, + onerror: "no-results", + sort: sortColumns, + fieldFacets: params.facetFields != null ? params.facetFields.split(",") : null, + filterQueries: fqs, + searchTerm: params.term, + spellCheck: params.spell + }; + + // Configure search term highlighting... + if (params.highlightFields) + { + var fields = []; + var highlightFields = params.highlightFields.split(","); + for (var i = 0; i < highlightFields.length; i++) + { + fields.push({ + field: highlightFields[i] + }); + } + queryDef.highlight = { + prefix: params.highlightPrefix, + postfix: params.highlightPostfix, + snippetCount: params.highlightSnippetCount, + fragmentSize: params.highlightFragmentSize, + usePhraseHighlighter: params.highlightUsePhraseHighlighter, + mergeContiguous: params.highlightMergeContiguous, + fields: fields + }; + + if (params.highlightMaxAnalyzedChars) + { + queryDef.highlight.maxAnalyzedChars = params.highlightMaxAnalyzedChars; + } + } + + var rs = search.queryResultSet(queryDef); + nodes = rs.nodes; + } + else + { + // failed to process the search string - empty list returned + var rs = {}; + nodes = []; + } + + if (params.maxResults > 0) + { + return processResults( + nodes, + params.maxResults, + params.startIndex, + rootNode, + rs.meta); + } + else + { + return processResultsSinglePage( + nodes, + params.startIndex, + rootNode, + rs.meta); + } +} + +/** + * Return the fts-alfresco query template to use. + * The default searches name, title, descripton, calendar, link, full text and tag fields. + * It is configurable via the .config.xml attached to this webscript. + */ +function getQueryTemplate() +{ + var t = + [{ + field: "keywords", + template: "%(cm:name cm:title cm:description ia:whatEvent ia:descriptionEvent lnk:title lnk:description TEXT TAG)" + }], + xml = new XML(config.script), + qt = xml["default-query-template"]; + if (qt != null && qt.length() != 0) + { + t[0].template = qt.toString(); + } + + // get default fts operator from the config + // + // TODO: common search lib - for both live and standard e.g. to get values like this... + // + var operator = "AND", + cf = xml["default-operator"]; + if (cf != null && cf.length != 0) + { + operator = cf.toString(); + } + + return { + template: t, + operator: operator + }; +} + +/* +* Helper method used to determine whether the property is tied to a list of properties. +* +* @param formJSON the list of the properties provided to the form +* @param prop propertyname +* @return true if it is tied to a list of properties, false otherwise +*/ +function isListProperty(formJson, prop) +{ + return prop.indexOf('isListProperty') != -1 || prop+'_isListProperty' in formJson; +} + +/** + * Helper method used to determine whether the property is tied to categories. + * + * @param formJSON the list of the properties provided to the form + * @param prop propertyname + * @return true if it is tied to categories, false otherwise + */ +function isCategoryProperty(formJson, prop) +{ + return prop.indexOf('usesubcats') != -1 || prop.indexOf('isCategory') != -1 || + prop+'_usesubcats' in formJson || prop+'_isCategory' in formJson; +} + +/** + * Helper method used to construct lucene query fragment for a default category property. + * + * @param cats the selected categories (array of string noderef) + * @param useSubCats boolean that indicates if should search also in subcategories + * @return lucene query with default category property + */ +function processDefaultCategoryProperty(cats, useSubCats) +{ + var firstCat = true; + var catQuery = ""; + for (var i = 0; i < cats.length; i++) + { + var cat = cats[i]; + var catNode = search.findNode(cat); + if (catNode) + { + catQuery += (firstCat ? '' : ' OR ') + "PATH:\"" + catNode.qnamePath + (useSubCats ? "//*\"" : "/member\"" ); + + firstCat = false; + } + } + return catQuery; +} + +/** + * Helper method used to construct lucene query fragment for a custom category property. + * + * @param propName property name + * @param cats the selected categories (array of string noderef) + * @param useSubCats boolean that indicates if should search also in subcategories + * @return lucene query with custom category property + */ +function processCustomCategoryProperty(propName, cats, useSubCats) +{ + var catQuery = ""; + + // Prepare the query that will be used to load all the noderefs for the selected categories values + // (and their subcategories if selected) + var selectedCatsQuery = ""; + for (var i = 0; i < cats.length; i++) + { + var cat = cats[i]; + var catNode = search.findNode(cat); + if (catNode) + { + selectedCatsQuery += "+PATH:\"" + catNode.qnamePath + (useSubCats ? "//." : '') + "\" "; + } + } + + if (selectedCatsQuery.length !== 0) + { + // Load all the noderefs for the selected categories values + var queryDefCat = { + query: selectedCatsQuery, + language: "fts-alfresco", + onerror: "no-results" + }; + var rs = search.queryResultSet(queryDefCat); + var selectedCatNodes = rs.nodes; + + // Create lucene query with custom category property + if (selectedCatNodes && selectedCatNodes.length !== 0) + { + for (var j = 0; j < selectedCatNodes.length; j++) + { + var selectedCatNode = selectedCatNodes[j]; + catQuery += (j == 0 ? '':' OR ') + escapeQName(propName) + ':' + selectedCatNode.id; + } + } + } + + return catQuery; +} diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/sites/site-identifier-used.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/sites/site-identifier-used.get.desc.xml new file mode 100644 index 0000000000..05bbe8c03b --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/sites/site-identifier-used.get.desc.xml @@ -0,0 +1,10 @@ + + Site Identifiers In Use + Determines whether or not the supplied site identifiers (the title and shortName attributes) are already being used or note + /slingshot/site-identifier-used?shortName={shortName?} + /slingshot/site-identifier-used?title={title?} + argument + user + required + limited_support + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/sites/site-identifier-used.get.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/sites/site-identifier-used.get.js new file mode 100644 index 0000000000..1e19c112c8 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/sites/site-identifier-used.get.js @@ -0,0 +1,33 @@ +// See SHA-1612 +// This WebScript has been added to support the ability to determine whether or not a site title or shortName +// has already been used. It runs with admin privileges but does not return anything other than a true/false +// response to prevent exploratory disclosure of private data. +function main() +{ + var foundMatch = false; + + var shortName = args["shortName"]; + var title = args["title"]; + + var useShortName = !!shortName; + + // Get the list of sites + var sites = siteService.findSites(shortName || title, -1); + if (sites && sites.length) + { + for (var i=0; i + movewikipage + Wiki - Page details + /slingshot/wiki/page/{siteId}/{pageTitle} + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post.json.ftl new file mode 100644 index 0000000000..ff80f35c67 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post.json.ftl @@ -0,0 +1,9 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ +<#if result.error?exists> + "error" : "${result.error}" +<#else> + "title" : "${result.title}" + +} + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post.properties new file mode 100644 index 0000000000..cea23d209e --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post.properties @@ -0,0 +1,3 @@ +page-moved=This page has been moved +page-moved-here=here +page-not-found=The Wiki page could not be found diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_de.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_de.properties new file mode 100755 index 0000000000..971a261909 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_de.properties @@ -0,0 +1,3 @@ +page-moved=Diese Seite wurde verschoben nach +page-moved-here=hier +page-not-found=Die Wiki-Seite wurde nicht gefunden diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_es.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_es.properties new file mode 100755 index 0000000000..48be2932ca --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_es.properties @@ -0,0 +1,3 @@ +page-moved=La p\u00e1gina se ha movido a +page-moved-here=aqu\u00ed +page-not-found=No se ha podido encontrar la p\u00e1gina Wiki diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_fr.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_fr.properties new file mode 100755 index 0000000000..d32d9d3ced --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_fr.properties @@ -0,0 +1,3 @@ +page-moved=Cette page a \u00e9t\u00e9 d\u00e9plac\u00e9e +page-moved-here=ici +page-not-found=La page du wiki est introuvable diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_it.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_it.properties new file mode 100755 index 0000000000..34851435f1 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_it.properties @@ -0,0 +1,3 @@ +page-moved=La pagina \u00e8 stata spostata +page-moved-here=qui +page-not-found=Impossibile trovare la pagina Wiki diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_ja.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_ja.properties new file mode 100755 index 0000000000..7f13a64179 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_ja.properties @@ -0,0 +1,3 @@ +page-moved=\u3053\u306e\u30da\u30fc\u30b8\u306f\u79fb\u52d5\u3055\u308c\u307e\u3057\u305f +page-moved-here=\u3053\u3053 +page-not-found=Wiki \u30da\u30fc\u30b8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_nb.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_nb.properties new file mode 100755 index 0000000000..20991f2f05 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_nb.properties @@ -0,0 +1,3 @@ +page-moved=Denne siden er flyttet +page-moved-here=hit +page-not-found=Kan ikke finne Wiki-siden diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_nl.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_nl.properties new file mode 100755 index 0000000000..101c317e12 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_nl.properties @@ -0,0 +1,3 @@ +page-moved=Deze pagina is +page-moved-here=hierheen verplaatst +page-not-found=Kan de wiki-pagina niet vinden diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_pt_BR.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_pt_BR.properties new file mode 100644 index 0000000000..b639d0d633 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_pt_BR.properties @@ -0,0 +1,3 @@ +page-moved=Essa p\u00e1gina foi movida +page-moved-here=aqui +page-not-found=N\u00e3o foi poss\u00edvel encontrar a p\u00e1gina de Wiki diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_ru.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_ru.properties new file mode 100755 index 0000000000..cdddba2be6 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_ru.properties @@ -0,0 +1,3 @@ +page-moved=\u042d\u0442\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0430 +page-moved-here=\u0441\u044e\u0434\u0430 +page-not-found=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0430 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_zh_CN.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_zh_CN.properties new file mode 100755 index 0000000000..e0afdc9d78 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/move.post_zh_CN.properties @@ -0,0 +1,3 @@ +page-moved=\u6b64\u9875\u9762\u5df2\u79fb\u5230 +page-moved-here=\u6b64\u5904 +page-not-found=\u65e0\u6cd5\u627e\u5230\u6b64\u7ef4\u57fa\u9875\u9762 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.delete.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.delete.desc.xml new file mode 100644 index 0000000000..b2040f4e9f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.delete.desc.xml @@ -0,0 +1,9 @@ + + wikidelete + Wiki - Deletes page + /slingshot/wiki/page/{siteId}/{pageTitle} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.delete.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.delete.json.ftl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.desc.xml new file mode 100644 index 0000000000..e76c291155 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.desc.xml @@ -0,0 +1,9 @@ + + wikipage + Wiki - Page details + /slingshot/wiki/page/{siteId}/{pageTitle} + argument + user + required + internal + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.html.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.html.ftl new file mode 100644 index 0000000000..aea7be95a8 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.html.ftl @@ -0,0 +1 @@ +${result.page.contents} diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.json.404.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.json.404.ftl new file mode 100644 index 0000000000..2ca3373415 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.json.404.ftl @@ -0,0 +1,9 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "permissions": + { + "create": ${container.hasPermission("CreateChildren")?string}, + "edit": ${container.hasPermission("Write")?string} + } +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.json.ftl new file mode 100644 index 0000000000..99d861850d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.json.ftl @@ -0,0 +1,51 @@ +<#macro dateFormat date>${xmldate(date)} +<#escape x as jsonUtils.encodeJSONString(x)> +{ +<#if result.page??> + <#assign page = result.page> + <#assign node = result.node> + "name": "${page.systemName}", + "title": "<#if page.title?has_content>${page.title}<#else>${page.systemName?replace("_", " ")}", + "pagetext": "${page.contents}", + "pageList": [ + <#list result.pageList as p> + "${p}"<#if p_has_next>, + + ]<#if !result.minWikiData>, + <#if !result.minWikiData> + "tags": [ + <#list result.tags as tag> + "${tag}"<#if tag_has_next>, + + ], + "links": [ + <#list result.links as link> + "${link}"<#if link_has_next>, + + ], + <#if node.hasAspect("cm:versionable")> + "versionhistory": [ + <#list node.versionHistory as record> + { + "name": "${record.name}", + "title": "${record.title!""}", + "version": "${record.versionLabel}", + "versionId": "${record.id}", + "date": "<@dateFormat record.createdDate />", + "author": "${record.creator}" + }<#if record_has_next>, + + ], + + "permissions": + { + "create": ${result.container.hasPermission("CreateChildren")?string}, + "edit": ${node.hasPermission("Write")?string}, + "delete": ${node.hasPermission("Delete")?string} + } + +<#else> + "error" : "${result.error!""}" + +} + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.mediawiki.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.mediawiki.ftl new file mode 100644 index 0000000000..dd63b57841 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.mediawiki.ftl @@ -0,0 +1,6 @@ +<#if result.page??> +${result.page.contents} +<#else> +<#-- An error occured --> +${result.error!""} + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.properties new file mode 100644 index 0000000000..10f930f611 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.properties @@ -0,0 +1 @@ +page-not-found=The Wiki Page could not be found diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_de.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_de.properties new file mode 100755 index 0000000000..4c58483a7e --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_de.properties @@ -0,0 +1 @@ +page-not-found=Die Wiki-Seite wurde nicht gefunden diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_es.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_es.properties new file mode 100755 index 0000000000..bf63bcc354 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_es.properties @@ -0,0 +1 @@ +page-not-found=No se ha podido encontrar la p\u00e1gina Wiki diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_fr.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_fr.properties new file mode 100755 index 0000000000..7fb0341755 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_fr.properties @@ -0,0 +1 @@ +page-not-found=La page du wiki est introuvable diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_it.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_it.properties new file mode 100755 index 0000000000..19e07713cf --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_it.properties @@ -0,0 +1 @@ +page-not-found=Impossibile trovare la pagina Wiki diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_ja.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_ja.properties new file mode 100755 index 0000000000..82b202d8ef --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_ja.properties @@ -0,0 +1 @@ +page-not-found=Wiki \u30da\u30fc\u30b8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_nb.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_nb.properties new file mode 100755 index 0000000000..382f8fa033 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_nb.properties @@ -0,0 +1 @@ +page-not-found=Kan ikke finne Wiki-siden diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_nl.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_nl.properties new file mode 100755 index 0000000000..0e554f3606 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_nl.properties @@ -0,0 +1 @@ +page-not-found=Kan de wiki-pagina niet vinden diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_pt_BR.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_pt_BR.properties new file mode 100644 index 0000000000..a3dff3cbac --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_pt_BR.properties @@ -0,0 +1 @@ +page-not-found=N\u00e3o foi poss\u00edvel encontrar a p\u00e1gina de Wiki diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_ru.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_ru.properties new file mode 100755 index 0000000000..89d866f9f4 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_ru.properties @@ -0,0 +1 @@ +page-not-found=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0430 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_zh_CN.properties b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_zh_CN.properties new file mode 100755 index 0000000000..c1a07df32f --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get_zh_CN.properties @@ -0,0 +1 @@ +page-not-found=\u65e0\u6cd5\u627e\u5230\u6b64\u7ef4\u57fa\u9875\u9762 diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.put.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.put.desc.xml new file mode 100644 index 0000000000..c166c0fb36 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.put.desc.xml @@ -0,0 +1,9 @@ + + wikipage + Wiki - Page details + /slingshot/wiki/page/{siteId}/{pageTitle} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.put.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.put.json.ftl new file mode 100644 index 0000000000..57c6a00648 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.put.json.ftl @@ -0,0 +1,10 @@ +<#assign page = result.page> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "name": "${page.systemName}", + "title": "${page.title}", + "pagetext": "${page.contents}", + <#assign tags><#list page.tags as tag>"${tag}"<#if tag_has_next>, + "tags": <#noescape>[${tags}] +} + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.desc.xml new file mode 100644 index 0000000000..61b5f6e48d --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.desc.xml @@ -0,0 +1,9 @@ + + wikipage + Wiki - Page details + /slingshot/wiki/pages/{siteId} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.json.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.json.ftl new file mode 100644 index 0000000000..7daa61877c --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.json.ftl @@ -0,0 +1,77 @@ +<#macro dateFormat date>${xmldate(date)} +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "totalPages": ${wiki.pages?size?c}, + "permissions": + { + "create": ${wiki.container.hasPermission("CreateChildren")?string} + }, + "pages": + [ + <#if pageMetaOnly> + <#list wiki.pages as p> + <#assign page = p.page> + { + "name": "${p.name}", + "title": "<#if p.title?has_content>${p.title}<#else>${p.name?replace("_", " ")}", + }<#if p_has_next>, + + <#else> + <#list wiki.pages?sort_by(['modified'])?reverse as p> + <#assign node = p.node> + <#assign page = p.page> + { + "name": "${p.name}", + "title": "<#if p.title?has_content>${p.title}<#else>${p.name?replace("_", " ")}", + "text": "${page.contents}", + "tags": [ + <#list p.tags as tag> + "${tag}"<#if tag_has_next>, + + ], + "createdOn": "${formatDateISO8601(p.created)}", + <#if p.createdBy??> + <#assign createdBy = (p.createdBy.properties.firstName!"" + " " + p.createdBy.properties.lastName!"")?trim> + <#assign createdByUser = p.createdBy.properties.userName> + <#else> + <#assign createdBy=""> + <#assign createdByUser=""> + + "createdBy": "${createdBy}", + "createdByUser": "${createdByUser}", + "modifiedOn": "${formatDateISO8601(p.modified)}", + <#if p.modifiedBy??> + <#assign modifiedBy = (p.modifiedBy.properties.firstName!"" + " " + p.modifiedBy.properties.lastName!"")?trim> + <#assign modifiedByUser = p.modifiedBy.properties.userName> + <#else> + <#assign modifiedBy=""> + <#assign modifiedByUser=""> + + "modifiedBy": "${modifiedBy}", + "modifiedByUser": "${modifiedByUser}", + "permissions": + { + "edit": ${node.hasPermission("Write")?string}, + "delete": ${node.hasPermission("Delete")?string} + } + }<#if p_has_next>, + + + ], + "pageTitles": + [ + <#if wiki.pageTitles??> + <#list wiki.pageTitles as title> + "${title}"<#if title_has_next>, + + + ] +} + + +<#function formatDateISO8601 dateItem> + <# local temp=${.locale} --> + <#setting locale="en_US"> + <#return dateItem?datetime?string("yyyy-MM-dd'T'HH:mm:ss.SSSZ")> + <# setting locale=temp --> + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.rss.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.rss.ftl new file mode 100644 index 0000000000..df17aac703 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.rss.ftl @@ -0,0 +1,26 @@ + + + + Alfresco - Wiki Page + ${shareUrl}/proxy/alfresco-feed/slingshot/wiki/pages/${siteId}?format=rss + Alfresco Wiki Page - Recent Changes + <#assign locale_original=.locale><#setting locale="en_US">${date?string("EEE, dd MMM yyyy HH:mm:ss Z")}<#setting locale=locale_original> + <#assign locale_original=.locale><#setting locale="en_US">${date?string("EEE, dd MMM yyyy HH:mm:ss Z")}<#setting locale=locale_original> + Alfresco ${server.edition} v${server.version} + + Alfresco - Wiki Page Recent Changes + ${shareUrl}/proxy/alfresco-feed/slingshot/wiki/pages/${siteId}?format=rss + ${shareUrl}/proxy/alfresco/images/logo/AlfrescoLogo200.png + + <#list wiki.pages?sort_by(['modified'])?reverse as p> + <#assign node = p.node> + <#assign page = p.page> + + ${(page.title!"")?html} + ${shareUrl}/page/site/${siteId}/wiki-page?title=${page.systemName?url('UTF-8')} + <#assign locale_original=.locale><#setting locale="en_US">${page.modifiedAt?string("EEE, dd MMM yyyy HH:mm:ss Z")}<#setting locale=locale_original> + ${node.id} + + + + diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.desc.xml b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.desc.xml new file mode 100644 index 0000000000..5ffbf6f12a --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.desc.xml @@ -0,0 +1,9 @@ + + wikipage + Wiki - Page details + /slingshot/wiki/version/{siteId}/{pageTitle}/{versionId} + argument + user + required + internal + \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.html.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.html.ftl new file mode 100644 index 0000000000..51a16e9af1 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.html.ftl @@ -0,0 +1 @@ +${content} \ No newline at end of file diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.mediawiki.ftl b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.mediawiki.ftl new file mode 100644 index 0000000000..51a16e9af1 --- /dev/null +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.mediawiki.ftl @@ -0,0 +1 @@ +${content} \ No newline at end of file diff --git a/amps/share-services/src/test/java/org/alfresco/repo/web/scripts/ReadOnlyTransactionInGetSlingshotApiTest.java b/amps/share-services/src/test/java/org/alfresco/repo/web/scripts/ReadOnlyTransactionInGetSlingshotApiTest.java new file mode 100644 index 0000000000..6803e04584 --- /dev/null +++ b/amps/share-services/src/test/java/org/alfresco/repo/web/scripts/ReadOnlyTransactionInGetSlingshotApiTest.java @@ -0,0 +1,252 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts; + +import java.text.MessageFormat; +import java.util.List; + +import org.alfresco.repo.node.archive.NodeArchiveService; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.transaction.TransactionService; +import org.springframework.context.ApplicationContext; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * Set of tests that ensure Slingshot GET APIs are run successfully in a read-only + * transaction (ALF-10179). + * + * Some webscripts have a side effect of creating a "container" these tests + * are to ensure this is handled gracefully in a way that allows the main + * transaction to be declared as readonly for performance reasons. + * + * @see ReadOnlyTransactionInGetRestApiTest + * @author Gavin Cornwell + * @author Matt Ward + * @since 5.1 + */ +public class ReadOnlyTransactionInGetSlingshotApiTest extends BaseWebScriptTest +{ + private static final String TEST_SITE_NAME = "readOnlyTestSite"; + + private static final String URL_GET_SITE_DATALISTS = "/slingshot/datalists/lists/site/" + TEST_SITE_NAME + "/dataLists"; + private static final String URL_GET_SITE_WIKI = "/slingshot/wiki/pages/" + TEST_SITE_NAME; + private static final String URL_GET_SITE_WIKI_PAGE = "/slingshot/wiki/page/" + TEST_SITE_NAME + "/AWikiPage"; + private static final String URL_GET_SITE_WIKI_PAGE_VERSION = "/slingshot/wiki/version/" + TEST_SITE_NAME + "/AWikiPage/123456789"; + private static final String URL_GET_DOCLIB_CATEGORYNODE = "/slingshot/doclib/categorynode/node/{0}"; + private static final String URL_GET_DOCLIB_TREENODE = "/slingshot/doclib/treenode/site/" + TEST_SITE_NAME + "/documentLibrary"; + private static final String URL_GET_DOCLIB_NODE = "/slingshot/doclib/node/{0}"; + private static final String URL_GET_DOCLIB2_NODE = "/slingshot/doclib2/node/{0}"; + private static final String URL_GET_DOCLIB_LOCATION = "/slingshot/doclib/node/{0}/location"; + private static final String URL_GET_DOCLIB_DOCLIST = "/slingshot/doclib/doclist/documents/site/" + TEST_SITE_NAME + "/documentLibrary"; + private static final String URL_GET_DOCLIB2_DOCLIST = "/slingshot/doclib2/doclist/documents/site/" + TEST_SITE_NAME + "/documentLibrary"; + private static final String URL_GET_DOCLIB_IMAGES = "/slingshot/doclib/images/site/" + TEST_SITE_NAME + "/documentLibrary"; + + private SiteService siteService; + private NodeService nodeService; + private TransactionService transactionService; + private NodeArchiveService nodeArchiveService; + + private NodeRef testSiteNodeRef; + private String testSiteNodeRefString; + + private boolean logEnabled = false; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + ApplicationContext appContext = getServer().getApplicationContext(); + + this.siteService = (SiteService)appContext.getBean("SiteService"); + this.nodeService = (NodeService)appContext.getBean("NodeService"); + this.transactionService = (TransactionService)appContext.getBean("TransactionService"); + this.nodeArchiveService = (NodeArchiveService)getServer().getApplicationContext().getBean("nodeArchiveService"); + + // set admin as current user + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // delete the test site if it's still hanging around from previous runs + SiteInfo site = siteService.getSite(TEST_SITE_NAME); + if (site != null) + { + siteService.deleteSite(TEST_SITE_NAME); + nodeArchiveService.purgeArchivedNode(nodeArchiveService.getArchivedNode(site.getNodeRef())); + } + + // create the test site, this should create a site but it won't have any containers created + SiteInfo siteInfo = this.siteService.createSite("collaboration", TEST_SITE_NAME, "Read Only Test Site", + "Test site for ReadOnlyTransactionRestApiTest", SiteVisibility.PUBLIC); + this.testSiteNodeRef = siteInfo.getNodeRef(); + this.testSiteNodeRefString = this.testSiteNodeRef.toString().replace("://", "/"); + + // ensure there are no containers present at the start of the test + List children = nodeService.getChildAssocs(this.testSiteNodeRef); + assertTrue("The test site should not have any containers", children.isEmpty()); + } + + /* (non-Javadoc) + * @see junit.framework.TestCase#tearDown() + */ + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + + SiteInfo site = siteService.getSite(TEST_SITE_NAME); + // use retrying transaction to delete the site + this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + // delete the test site + siteService.deleteSite(TEST_SITE_NAME); + + return null; + } + }); + nodeArchiveService.purgeArchivedNode(nodeArchiveService.getArchivedNode(site.getNodeRef())); + + AuthenticationUtil.clearCurrentSecurityContext(); + } + + public void testGetSiteDataLists() throws Exception + { + // TODO: Fixme - This REST API still requires a readwrite transaction to be successful + + Response response = sendRequest(new GetRequest(URL_GET_SITE_DATALISTS), 200); + logResponse(response); + assertEquals(Status.STATUS_OK, response.getStatus()); + } + + public void testGetSiteWiki() throws Exception + { + Response response = sendRequest(new GetRequest(URL_GET_SITE_WIKI), 200); + logResponse(response); + assertEquals(Status.STATUS_OK, response.getStatus()); + } + + public void testGetSiteWikiPage() throws Exception + { + Response response = sendRequest(new GetRequest(URL_GET_SITE_WIKI_PAGE), 404); + logResponse(response); + assertEquals(Status.STATUS_NOT_FOUND, response.getStatus()); + } + + public void testGetSiteWikiPageVersion() throws Exception + { + Response response = sendRequest(new GetRequest(URL_GET_SITE_WIKI_PAGE_VERSION), 404); + logResponse(response); + assertEquals(Status.STATUS_NOT_FOUND, response.getStatus()); + } + + public void testGetDoclibTreeNode() throws Exception + { + // TODO: Fixme - This REST API still requires a readwrite transaction to be successful + + Response response = sendRequest(new GetRequest(URL_GET_DOCLIB_TREENODE), 200); + logResponse(response); + assertEquals(Status.STATUS_OK, response.getStatus()); + } + + public void testGetDoclibDoclist() throws Exception + { + // TODO: Fixme - This REST API still requires a readwrite transaction to be successful + + Response response = sendRequest(new GetRequest(URL_GET_DOCLIB_DOCLIST), 200); + logResponse(response); + assertEquals(Status.STATUS_OK, response.getStatus()); + } + + public void testGetDoclib2Doclist() throws Exception + { + // TODO: Fixme - This REST API still requires a readwrite transaction to be successful + + Response response = sendRequest(new GetRequest(URL_GET_DOCLIB2_DOCLIST), 200); + logResponse(response); + assertEquals(Status.STATUS_OK, response.getStatus()); + } + + public void testGetDoclibImages() throws Exception + { + // TODO: Fixme - This REST API still requires a readwrite transaction to be successful + + Response response = sendRequest(new GetRequest(URL_GET_DOCLIB_IMAGES), 200); + logResponse(response); + assertEquals(Status.STATUS_OK, response.getStatus()); + } + + public void testGetDoclibCategoryNode() throws Exception + { + Response response = sendRequest(new GetRequest(MessageFormat.format(URL_GET_DOCLIB_CATEGORYNODE, + testSiteNodeRefString)), 200); + logResponse(response); + assertEquals(Status.STATUS_OK, response.getStatus()); + } + + public void testGetDoclibNode() throws Exception + { + Response response = sendRequest(new GetRequest(MessageFormat.format(URL_GET_DOCLIB_NODE, + testSiteNodeRefString)), 200); + logResponse(response); + assertEquals(Status.STATUS_OK, response.getStatus()); + } + + public void testGetDoclib2Node() throws Exception + { + Response response = sendRequest(new GetRequest(MessageFormat.format(URL_GET_DOCLIB2_NODE, + testSiteNodeRefString)), 200); + logResponse(response); + assertEquals(Status.STATUS_OK, response.getStatus()); + } + + public void testGetDoclibNodeLocation() throws Exception + { + Response response = sendRequest(new GetRequest(MessageFormat.format(URL_GET_DOCLIB_LOCATION, + testSiteNodeRefString)), 200); + logResponse(response); + assertEquals(Status.STATUS_OK, response.getStatus()); + } + + private void logResponse(Response response) + { + if (this.logEnabled) + { + try + { + System.out.println(response.getContentAsString()); + } + catch (Exception e) + { + System.err.println("Unable to log response: " + e.toString()); + } + } + } +} diff --git a/amps/share-services/src/test/java/org/alfresco/repo/web/scripts/permission/PermissionServiceTest.java b/amps/share-services/src/test/java/org/alfresco/repo/web/scripts/permission/PermissionServiceTest.java new file mode 100644 index 0000000000..b9a53f5ffa --- /dev/null +++ b/amps/share-services/src/test/java/org/alfresco/repo/web/scripts/permission/PermissionServiceTest.java @@ -0,0 +1,362 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.permission; + +import java.util.HashSet; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyMap; +import org.json.JSONArray; +import org.json.JSONObject; +import org.json.JSONException; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * Test for RestAPI permission services + * + * @author alex.mukha + * @since 4.2.3 + */ +public class PermissionServiceTest extends BaseWebScriptTest +{ + private MutableAuthenticationService authenticationService; + private AuthenticationComponent authenticationComponent; + private PersonService personService; + private NodeService nodeService; + private PermissionService permissionService; + private FileFolderService fileFolderService; + private SiteService siteService; + + private static final String USER_ONE = "USER_ONE_" + GUID.generate(); + private static final String USER_TWO = "USER_TWO_" + GUID.generate(); + private static final String USER_THREE = "USER_THREE_" + GUID.generate(); + private static final String URL_DOCLIB_PERMISSIONS = "/slingshot/doclib/permissions"; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + this.authenticationService = (MutableAuthenticationService)getServer().getApplicationContext().getBean("AuthenticationService"); + this.authenticationComponent = (AuthenticationComponent)getServer().getApplicationContext().getBean("authenticationComponent"); + this.personService = (PersonService)getServer().getApplicationContext().getBean("PersonService"); + this.nodeService = (NodeService)getServer().getApplicationContext().getBean("NodeService"); + this.permissionService = (PermissionService)getServer().getApplicationContext().getBean("PermissionService"); + this.fileFolderService = (FileFolderService)getServer().getApplicationContext().getBean("FileFolderService"); + this.siteService = (SiteService)getServer().getApplicationContext().getBean("SiteService"); + + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + // Create users + createUser(USER_ONE); + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + + // Clear the users + deleteUser(USER_ONE); + } + + /** + * Test for MNT-11725 + */ + public void testDowngradePermissions() throws Exception + { + NodeRef rootNodeRef = this.nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + NodeRef folderRef = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName(NamespaceService.ALFRESCO_URI, "testFolder"), + ContentModel.TYPE_FOLDER).getChildRef(); + permissionService.setPermission(folderRef, USER_ONE, PermissionService.COORDINATOR, true); + permissionService.setInheritParentPermissions(folderRef, false); + + authenticationComponent.setCurrentUser(USER_ONE); + + // JSON fromat + // {"permissions": + // [{"authority":"userA", + // "role":"Consumer"}, + // {"authority":"userA", + // "role":"Coordinator", + // "remove":true}], + // "isInherited":true} + + /* negative test, we are first deleting the coordinator role and then try to add consumer */ + JSONObject changePermission = new JSONObject(); + JSONArray permissions = new JSONArray(); + // First delete permission, then add + JSONObject addPermission = new JSONObject(); + addPermission.put("authority", USER_ONE); + addPermission.put("role", PermissionService.CONSUMER); + JSONObject removePermission = new JSONObject(); + removePermission.put("authority", USER_ONE); + removePermission.put("role", PermissionService.COORDINATOR); + removePermission.put("remove","true"); + permissions.put(removePermission); + permissions.put(addPermission); + changePermission.put("permissions", permissions); + changePermission.put("isInherited", "true"); + + sendRequest(new PostRequest(URL_DOCLIB_PERMISSIONS + + "/" + StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getProtocol() + + "/" + StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getIdentifier() + + "/" + folderRef.getId(), changePermission.toString(), "application/json"), Status.STATUS_INTERNAL_SERVER_ERROR); + + /* positive test */ + changePermission = new JSONObject(); + permissions = new JSONArray(); + // First add permission, then delete + addPermission = new JSONObject(); + addPermission.put("authority", USER_ONE); + addPermission.put("role", PermissionService.CONSUMER); + removePermission = new JSONObject(); + removePermission.put("authority", USER_ONE); + removePermission.put("role", PermissionService.COORDINATOR); + removePermission.put("remove","true"); + permissions.put(addPermission); + permissions.put(removePermission); + changePermission.put("permissions", permissions); + changePermission.put("isInherited", "true"); + + sendRequest(new PostRequest(URL_DOCLIB_PERMISSIONS + + "/" + StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getProtocol() + + "/" + StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getIdentifier() + + "/" + folderRef.getId(), changePermission.toString(), "application/json"), Status.STATUS_OK); + + AccessStatus accessStatus = permissionService.hasPermission(folderRef, PermissionService.CONSUMER); + assertTrue("The permission was not set correctly", accessStatus == AccessStatus.ALLOWED); + + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + nodeService.deleteNode(folderRef); + } + + private void createUser(String userName) + { + if (this.authenticationService.authenticationExists(userName) == false) + { + this.authenticationService.createAuthentication(userName, "PWD".toCharArray()); + + PropertyMap properties = new PropertyMap(4); + properties.put(ContentModel.PROP_USERNAME, userName); + properties.put(ContentModel.PROP_FIRSTNAME, "firstName"); + properties.put(ContentModel.PROP_LASTNAME, "lastName"); + properties.put(ContentModel.PROP_EMAIL, "email@email.com"); + properties.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + this.personService.createPerson(properties); + } + } + + private void deleteUser(String username) + { + this.personService.deletePerson(username); + if(this.authenticationService.authenticationExists(username)) + { + this.authenticationService.deleteAuthentication(username); + } + } + + /** + * Test for MNT-15509 Grant or/and Deny the same permission on multiple ancestors of a + * file and check if the file has duplicate inherited permissions + */ + public void testMultipleInheritedPermissions() throws Exception + { + // Create the site + String siteName = GUID.generate(); + siteService.createSite("Testing", siteName, siteName, null, SiteVisibility.PUBLIC); + + // Ensure we have a doclib + NodeRef siteContainer = siteService.createContainer(siteName, SiteService.DOCUMENT_LIBRARY, ContentModel.TYPE_FOLDER, null); + + // Create Folder1 + // Give USER_ONE COORDINATOR role to Folder1 + // Give USER_TWO CONTRIBUTOR role to Folder1 + // Deny USER_THREE CONSUMER role to Folder1 + NodeRef folder1 = fileFolderService.create(siteContainer, "Folder1", ContentModel.TYPE_FOLDER).getNodeRef(); + permissionService.setPermission(folder1, USER_ONE, PermissionService.COORDINATOR, true); + permissionService.setPermission(folder1, USER_TWO, PermissionService.CONTRIBUTOR, true); + permissionService.setPermission(folder1, USER_THREE, PermissionService.CONSUMER, false); + permissionService.setInheritParentPermissions(folder1, true); + + // Create Folder2 in Folder1 + // Give USER_ONE COORDINATOR role to Folder2 + // Deny USER_TWO CONTRIBUTOR role to Folder2 + // Give USER_THREE CONSUMER role to Folder2 + NodeRef folder2 = fileFolderService.create(folder1, "Folder2", ContentModel.TYPE_FOLDER).getNodeRef(); + permissionService.setPermission(folder2, USER_ONE, PermissionService.COORDINATOR, true); + permissionService.setPermission(folder2, USER_TWO, PermissionService.CONTRIBUTOR, false); + permissionService.setPermission(folder2, USER_THREE, PermissionService.CONSUMER, true); + permissionService.setInheritParentPermissions(folder2, true); + + // Create Folder3 in Folder2 + // Give USER_ONE CONSUMER role to Folder3 twice + NodeRef folder3 = fileFolderService.create(folder2, "Folder3", ContentModel.TYPE_FOLDER).getNodeRef(); + permissionService.setPermission(folder3, USER_ONE, PermissionService.CONSUMER, true); + permissionService.setPermission(folder3, USER_ONE, PermissionService.CONSUMER, true); + permissionService.setPermission(folder3, USER_ONE, PermissionService.COORDINATOR, true); + permissionService.setInheritParentPermissions(folder3, true); + + // Get Folder3's inherited permissions + Response response = sendRequest( + new GetRequest(URL_DOCLIB_PERMISSIONS + "/" + StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getProtocol() + "/" + + StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getIdentifier() + "/" + folder3.getId()), Status.STATUS_OK); + + JSONObject jsonResponse = new JSONObject(response.getContentAsString()); + + //Check if the request returns duplicate direct permissions + HashSet directPermissions = new HashSet<>(); + JSONArray directPermissionsArray = jsonResponse.getJSONArray("direct"); + for (int i = 0; i < directPermissionsArray.length(); i++) + { + AccessPermission permission = new AccessPermission(directPermissionsArray.getJSONObject(i)); + + assertTrue(directPermissions.add(permission)); + } + + //used to check allow/deny permission inheritance + AccessPermission denyPermissionInheritedTest = null; + AccessPermission allowPermissionInheritedTest = null; + + // Check if the request returns duplicate inherited permissions + HashSet inheritedPermissions = new HashSet<>(); + JSONArray inheritedPermissionsArray = jsonResponse.getJSONArray("inherited"); + for (int i = 0; i < inheritedPermissionsArray.length(); i++) + { + AccessPermission permission = new AccessPermission(inheritedPermissionsArray.getJSONObject(i)); + if (USER_TWO.equals(permission.getAuthority().getName()) && PermissionService.CONTRIBUTOR.equals(permission.getRole())) + { + denyPermissionInheritedTest = permission; + } + if (USER_THREE.equals(permission.getAuthority().getName()) && PermissionService.CONSUMER.equals(permission.getRole())) + { + allowPermissionInheritedTest = permission; + } + assertTrue(inheritedPermissions.add(permission)); + } + + // Check if on folder3 USER_TWO inherits DENY for CONTRIBUTOR role from + // folder 2 although on folder 1 was ALLOW + assertNull(denyPermissionInheritedTest); + + // Check if on folder3 USER_THREE inherits ALLOW for CONSUMER role from + // folder 2 although on folder 1 was DENY + assertNotNull(allowPermissionInheritedTest); + } +} + +class AccessPermission +{ + private PermissionAuthority authority; + private String role; + + public AccessPermission(JSONObject jsonPermission) throws JSONException + { + authority = new PermissionAuthority(jsonPermission.getJSONObject("authority")); + role = jsonPermission.getString("role"); + } + + public PermissionAuthority getAuthority() + { + return this.authority; + } + + public String getRole() + { + return this.role; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null || !(obj instanceof AccessPermission)) + { + return false; + } + + return (role.equals(((AccessPermission) obj).getRole()) && authority.equals(((AccessPermission) obj).getAuthority())); + } + + @Override + public int hashCode() + { + return authority.hashCode() + role.hashCode(); + } +} + +class PermissionAuthority +{ + private String name; + private String displayName; + + public PermissionAuthority(JSONObject jsonAuth) throws JSONException + { + name = jsonAuth.getString("name"); + displayName = jsonAuth.getString("displayName"); + } + + public String getName() + { + return this.name; + } + + public String getDisplayName() + { + return this.displayName; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null || !(obj instanceof PermissionAuthority)) + { + return false; + } + + return (name.equals(((PermissionAuthority) obj).getName()) && displayName.equals(((PermissionAuthority) obj).getDisplayName())); + } + + @Override + public int hashCode() + { + return name.hashCode() + displayName.hashCode(); + } +} diff --git a/amps/share-services/src/test/java/org/alfresco/repo/web/scripts/search/AdvancedSearchTest.java b/amps/share-services/src/test/java/org/alfresco/repo/web/scripts/search/AdvancedSearchTest.java new file mode 100644 index 0000000000..728e10b59a --- /dev/null +++ b/amps/share-services/src/test/java/org/alfresco/repo/web/scripts/search/AdvancedSearchTest.java @@ -0,0 +1,167 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.search; + +import java.io.IOException; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.transaction.TransactionService; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Ignore; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + + +@Ignore("This test doesn't pass on HEAD-BUG-FIX and isn't included in a test suite.") +public class AdvancedSearchTest extends BaseWebScriptTest +{ + private static final String TEST_FILE_NAME1 = "qwe iop.txt"; + private static final String TEST_FILE_NAME2 = "qwe yu iop.txt"; + private static final String TEST_IN_SITES_FILE_NAME = "qwesiteiop"; + private static final String SEARCH_NAME = "qwe iop"; + private static final String SEARCH_IN_SITES = "qwesiteiop"; + private static final String TEST_FOLDER = "test_folder-" + System.currentTimeMillis(); + + private TransactionService transactionService; + private NodeService nodeService; + private FileFolderService fileFolderService; + private SearchService searchService; + private NamespaceService namespaceService; + private AuthenticationComponent authenticationComponent; + private NodeRef testNodeRef1; + private NodeRef testNodeRef2; + private NodeRef folderNodeRef; + private NodeRef rootNodeRef; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + this.transactionService = (TransactionService) getServer().getApplicationContext().getBean("TransactionService"); + this.nodeService = (NodeService) getServer().getApplicationContext().getBean("NodeService"); + this.fileFolderService = (FileFolderService)getServer().getApplicationContext().getBean("FileFolderService"); + this.authenticationComponent = (AuthenticationComponent) getServer().getApplicationContext().getBean("authenticationComponent"); + this.searchService = (SearchService) getServer().getApplicationContext().getBean("SearchService"); + this.namespaceService = (NamespaceService) getServer().getApplicationContext().getBean("namespaceService"); + + this.authenticationComponent.setSystemUserAsCurrentUser(); + + this.rootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + + List results = searchService.selectNodes(this.rootNodeRef, "/app:company_home", null, namespaceService, false); + if (results.size() == 0) + { + throw new AlfrescoRuntimeException("Can't find /app:company_home"); + } + + NodeRef companyHomeNodeRef = results.get(0); + + folderNodeRef = fileFolderService.create(companyHomeNodeRef, TEST_FOLDER, ContentModel.TYPE_FOLDER).getNodeRef(); + + testNodeRef1 = fileFolderService.create(folderNodeRef, TEST_FILE_NAME1, ContentModel.TYPE_CONTENT).getNodeRef(); + testNodeRef2 = fileFolderService.create(folderNodeRef, TEST_FILE_NAME2, ContentModel.TYPE_CONTENT).getNodeRef(); + + + assertNotNull(testNodeRef1); + assertNotNull(testNodeRef2); + } + + private void deleteNodeIfExists(NodeRef nodeRef) + { + if (nodeService.exists(nodeRef)) + { + nodeService.deleteNode(nodeRef); + } + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + RetryingTransactionCallback deleteCallback = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + deleteNodeIfExists(testNodeRef1); + deleteNodeIfExists(testNodeRef2); + deleteNodeIfExists(folderNodeRef); + return null; + } + + }; + this.transactionService.getRetryingTransactionHelper().doInTransaction(deleteCallback); + this.authenticationComponent.clearCurrentSecurityContext(); + } + + public void testSearchFile() throws IOException, JSONException + { + //Name search without quotes + String url = "/slingshot/search?site=&term=&tag=&maxResults=251&sort=&query={\"prop_cm_name\":\"" + SEARCH_NAME + "\",\"datatype\":\"cm:content\"}&repo=true&rootNode=alfresco://company/home"; + Response res = sendRequest(new GetRequest(url), Status.STATUS_OK); + JSONObject result = new JSONObject(res.getContentAsString()); + assertEquals(2, result.getInt("totalRecords")); + + url = "/slingshot/search?site=&term=&tag=&maxResults=251&sort=&query={\"prop_cm_name\":\"\\\"" + SEARCH_NAME + "\\\"\",\"datatype\":\"cm:content\"}&repo=true&rootNode=alfresco://company/home"; + res = sendRequest(new GetRequest(url), Status.STATUS_OK); + result = new JSONObject(res.getContentAsString()); + assertEquals(1, result.getInt("totalRecords")); + } + + public void testSearchInSites() throws IOException, JSONException + { + List results = searchService.selectNodes(this.rootNodeRef, "/app:company_home/st:sites", null, namespaceService, false); + if (results.size() == 0) + { + throw new AlfrescoRuntimeException("Can't find /app:company_home/st:sites"); + } + + NodeRef sitesNodeRef = results.get(0); + + long time = System.currentTimeMillis(); + + NodeRef sitefolderNodeRef = fileFolderService.create(sitesNodeRef, TEST_FOLDER, ContentModel.TYPE_FOLDER).getNodeRef(); + NodeRef nodeRef = fileFolderService.create(sitefolderNodeRef, TEST_IN_SITES_FILE_NAME + time, ContentModel.TYPE_CONTENT).getNodeRef(); + // String url = "/slingshot/search?site=&term=" + SEARCH_NAME + "&tag=&maxResults=251&sort=&query=&repo=false&rootNode=alfresco%3A%2F%2Fcompany%2Fhome&pageSize=50&startIndex=0"; + String url = "/slingshot/search?site=&term=&tag=&maxResults=251&sort=&query={\"prop_cm_name\":\"" + SEARCH_IN_SITES + time + "\",\"datatype\":\"cm:content\"}&repo=true&rootNode=alfresco://company/home"; + Response res = sendRequest(new GetRequest(url), Status.STATUS_OK); + JSONObject result = new JSONObject(res.getContentAsString()); + assertEquals(1, result.getInt("totalRecords")); + + deleteNodeIfExists(nodeRef); + deleteNodeIfExists(sitefolderNodeRef); + + } + +} diff --git a/amps/share-services/src/test/java/org/alfresco/repo/web/scripts/wiki/WikiRestApiTest.java b/amps/share-services/src/test/java/org/alfresco/repo/web/scripts/wiki/WikiRestApiTest.java new file mode 100644 index 0000000000..abf39ca381 --- /dev/null +++ b/amps/share-services/src/test/java/org/alfresco/repo/web/scripts/wiki/WikiRestApiTest.java @@ -0,0 +1,1009 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.wiki; + +import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.archive.NodeArchiveService; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.site.SiteModel; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteRole; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.cmr.wiki.WikiPageInfo; +import org.alfresco.service.cmr.wiki.WikiService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.PropertyMap; +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONObject; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer.DeleteRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * Unit Test to test the Wiki Web Script API + * + * @author Nick Burch + * @since 4.0 + */ +public class WikiRestApiTest extends BaseWebScriptTest +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(WikiRestApiTest.class); + + private MutableAuthenticationService authenticationService; + private AuthenticationComponent authenticationComponent; + private TransactionService transactionService; + private BehaviourFilter policyBehaviourFilter; + private PersonService personService; + private NodeService nodeService; + private NodeService internalNodeService; + private SiteService siteService; + private WikiService wikiService; + private NodeArchiveService nodeArchiveService; + + private static final String USER_ONE = "UserOneSecondToo"; + private static final String USER_TWO = "UserTwoSecondToo"; + private static final String USERDETAILS_FIRSTNAME = "FirstName123"; + private static final String USERDETAILS_LASTNAME = "LastName123"; + private static final String SITE_SHORT_NAME_WIKI = "WikiSiteShortNameTest"; + + private static final String PAGE_TITLE_ONE = "TestPageOne"; + private static final String PAGE_TITLE_TWO = "Test_Page_Two"; + private static final String PAGE_TITLE_THREE = "Still_Test_Page_Three"; + private static final String PAGE_CONTENTS_ONE = "http://google.com/"; + private static final String PAGE_CONTENTS_TWO = "http://alfresco.com/"; + private static final String PAGE_CONTENTS_THREE = "http://share.alfresco.com/"; + private static final String PAGE_CONTENTS_LINK = "Text text [[TestPageOne|P1]] [[Test_Page_Two|P2]] [[Invalid|Invalid]] text"; + + private static final String URL_WIKI_BASE = "/slingshot/wiki/page"; + private static final String URL_WIKI_LIST = URL_WIKI_BASE + "s/" + SITE_SHORT_NAME_WIKI; + private static final String URL_WIKI_FETCH = URL_WIKI_BASE + "/" + SITE_SHORT_NAME_WIKI + "/"; // plus title + private static final String URL_WIKI_UPDATE = URL_WIKI_BASE + "/" + SITE_SHORT_NAME_WIKI + "/"; // plus title + private static final String URL_WIKI_DELETE = URL_WIKI_BASE + "/" + SITE_SHORT_NAME_WIKI + "/"; // plus title + private static final String URL_WIKI_RENAME = URL_WIKI_BASE + "/" + SITE_SHORT_NAME_WIKI + "/"; // plus title + private static final String URL_WIKI_VERSION = "/slingshot/wiki/version/" + SITE_SHORT_NAME_WIKI + "/"; + + + // General methods + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + this.authenticationService = (MutableAuthenticationService)getServer().getApplicationContext().getBean("AuthenticationService"); + this.authenticationComponent = (AuthenticationComponent)getServer().getApplicationContext().getBean("authenticationComponent"); + this.policyBehaviourFilter = (BehaviourFilter)getServer().getApplicationContext().getBean("policyBehaviourFilter"); + this.transactionService = (TransactionService)getServer().getApplicationContext().getBean("transactionService"); + this.personService = (PersonService)getServer().getApplicationContext().getBean("PersonService"); + this.nodeService = (NodeService)getServer().getApplicationContext().getBean("NodeService"); + this.siteService = (SiteService)getServer().getApplicationContext().getBean("SiteService"); + this.wikiService = (WikiService)getServer().getApplicationContext().getBean("WikiService"); + this.internalNodeService = (NodeService)getServer().getApplicationContext().getBean("nodeService"); + this.nodeArchiveService = (NodeArchiveService)getServer().getApplicationContext().getBean("nodeArchiveService"); + + // Authenticate as user + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + + // Create test site + // - only create the site if it doesn't already exist + SiteInfo siteInfo = this.siteService.getSite(SITE_SHORT_NAME_WIKI); + if (siteInfo == null) + { + this.siteService.createSite("WikiSitePreset", SITE_SHORT_NAME_WIKI, "WikiSiteTitle", "TestDescription", SiteVisibility.PUBLIC); + } + + // Ensure the links container is there + if(!siteService.hasContainer(SITE_SHORT_NAME_WIKI, "wiki")) + { + siteService.createContainer(SITE_SHORT_NAME_WIKI, "wiki", null, null); + } + + // Create users + createUser(USER_ONE, SiteModel.SITE_COLLABORATOR); + createUser(USER_TWO, SiteModel.SITE_COLLABORATOR); + + // Do tests as inviter user + this.authenticationComponent.setCurrentUser(USER_ONE); + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + + // admin user required to delete user + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + + SiteInfo siteInfo = this.siteService.getSite(SITE_SHORT_NAME_WIKI); + if (siteInfo != null) + { + // delete the site + siteService.deleteSite(SITE_SHORT_NAME_WIKI); + nodeArchiveService.purgeArchivedNode(nodeArchiveService.getArchivedNode(siteInfo.getNodeRef())); + } + + // delete the users + if(personService.personExists(USER_ONE)) + { + personService.deletePerson(USER_ONE); + } + if(this.authenticationService.authenticationExists(USER_ONE)) + { + this.authenticationService.deleteAuthentication(USER_ONE); + } + + if(personService.personExists(USER_TWO)) + { + personService.deletePerson(USER_TWO); + } + if(this.authenticationService.authenticationExists(USER_TWO)) + { + this.authenticationService.deleteAuthentication(USER_TWO); + } + } + + private void createUser(String userName, String role) + { + // if user with given user name doesn't already exist then create user + if (this.authenticationService.authenticationExists(userName) == false) + { + // create user + this.authenticationService.createAuthentication(userName, "password".toCharArray()); + + // create person properties + PropertyMap personProps = new PropertyMap(); + personProps.put(ContentModel.PROP_USERNAME, userName); + personProps.put(ContentModel.PROP_FIRSTNAME, USERDETAILS_FIRSTNAME); + personProps.put(ContentModel.PROP_LASTNAME, USERDETAILS_LASTNAME); + personProps.put(ContentModel.PROP_EMAIL, "FirstName123.LastName123@email.com"); + personProps.put(ContentModel.PROP_JOBTITLE, "JobTitle123"); + personProps.put(ContentModel.PROP_JOBTITLE, "Organisation123"); + + // create person node for user + this.personService.createPerson(personProps); + } + + // add the user as a member with the given role + this.siteService.setMembership(SITE_SHORT_NAME_WIKI, userName, role); + } + + + // Test helper methods + + private JSONObject getPages(String filter, String username) throws Exception + { + String origUser = this.authenticationComponent.getCurrentUserName(); + if (username != null) + { + this.authenticationComponent.setCurrentUser(username); + filter = "myPages"; + } + + String url = URL_WIKI_LIST; + if (filter == null) + { + filter = "all"; + } + url += "?filter=" + filter; + url += "&startIndex=0&page=1&pageSize=4"; + + Response response = sendRequest(new GetRequest(url), 200); + JSONObject result = new JSONObject(response.getContentAsString()); + + if (username != null) + { + this.authenticationComponent.setCurrentUser(origUser); + } + + return result; + } + + private JSONObject getPage(String name, int expectedStatus) throws Exception + { + Response response = sendRequest(new GetRequest(URL_WIKI_FETCH + name), expectedStatus); + if (expectedStatus == Status.STATUS_OK) + { + JSONObject result = new JSONObject(response.getContentAsString()); + if (result.has("page")) + { + return result.getJSONObject("page"); + } + return result; + } + else if (expectedStatus == Status.STATUS_NOT_FOUND) + { + JSONObject result = new JSONObject(response.getContentAsString()); + return result; + } + else + { + return null; + } + } + + /** + * Fetches the content of a page at a given version + * Note - not JSON based. + */ + private String getPageAtVersion(String name, String version, int expectedStatus) throws Exception + { + Response response = sendRequest(new GetRequest(URL_WIKI_VERSION + name + "/" + version), expectedStatus); + if (expectedStatus == Status.STATUS_OK) + { + return response.getContentAsString(); + } + else if (expectedStatus == Status.STATUS_NOT_FOUND) + { + return response.getContentAsString(); + } + else + { + return null; + } + } + + /** + * Creates a single wiki page based on the supplied details + */ + private JSONObject createOrUpdatePage(String title, String contents, String version, int expectedStatus) + throws Exception + { + return createOrUpdatePage(null, title, contents, version, expectedStatus); + } + + /** + * Creates a single wiki page based on the supplied details + */ + private JSONObject createOrUpdatePage(String pageName, String title, String contents, String version, int expectedStatus) throws Exception + { + String name = null; + if (pageName == null) + { + name = title.replace(' ', '_'); + } + else + { + name = pageName; + } + + JSONObject json = new JSONObject(); + json.put("site", SITE_SHORT_NAME_WIKI); + json.put("title", title); + json.put("pagecontent", contents); + json.put("tags", ""); + json.put("page", "wiki-page"); // TODO Is this really needed? + + if (version == null || "force".equals(version)) + { + // Allow the save as-is, no versioning check + json.put("forceSave", "true"); // Allow the save as-is + } + else + { + if ("none".equals(version)) + { + // No versioning + } + else + { + json.put("currentVersion", version); + } + } + + Response response = sendRequest(new PutRequest(URL_WIKI_UPDATE + name, json.toString(), "application/json"), expectedStatus); + if (expectedStatus == Status.STATUS_OK) + { + JSONObject result = new JSONObject(response.getContentAsString()); + if (result.has("page")) + { + return result.getJSONObject("page"); + } + return result; + } + else + { + return null; + } + } + + /** + * Renames the page + */ + private JSONObject renamePage(String oldTitle, String newTitle, int expectedStatus) throws Exception + { + String name = oldTitle.replace(' ', '_'); + + JSONObject json = new JSONObject(); + json.put("site", SITE_SHORT_NAME_WIKI); + json.put("name", newTitle); + json.put("page", "wiki-page"); // TODO Is this really needed? + + Response response = sendRequest(new PostRequest(URL_WIKI_RENAME + name, json.toString(), "application/json"), expectedStatus); + if (expectedStatus == Status.STATUS_OK) + { + JSONObject result = new JSONObject(response.getContentAsString()); + return result; + } + else + { + return null; + } + } + + /** + * Deletes the page + */ + private JSONObject deletePage(String title, int expectedStatus) throws Exception + { + String name = title.replace(' ', '_'); + + Response response = sendRequest(new DeleteRequest(URL_WIKI_DELETE + name), expectedStatus); + if (expectedStatus == Status.STATUS_OK) + { + JSONObject result = new JSONObject(response.getContentAsString()); + return result; + } + else + { + return null; + } + } + + /** + * Monkeys with the created date on a wiki page + */ + private void pushPageCreatedDateBack(String name, int daysAgo) throws Exception + { + NodeRef container = siteService.getContainer(SITE_SHORT_NAME_WIKI, "wiki"); + NodeRef node = nodeService.getChildByName(container, ContentModel.ASSOC_CONTAINS, name); + + Date created = (Date)nodeService.getProperty(node, ContentModel.PROP_CREATED); + Date newCreated = new Date(created.getTime() - daysAgo*24*60*60*1000); + + UserTransaction txn = transactionService.getUserTransaction(); + txn.begin(); + + this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE); + internalNodeService.setProperty(node, ContentModel.PROP_CREATED, newCreated); + this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE); + + txn.commit(); + + // Now chance something else on the node to have it re-indexed + nodeService.setProperty(node, ContentModel.PROP_CREATED, newCreated); + nodeService.setProperty(node, ContentModel.PROP_DESCRIPTION, "Forced change"); + } + + + // Tests + + /** + * Creating, editing, fetching and deleting a link + */ + public void testCreateEditDeleteEntry() throws Exception + { + JSONObject page; + JSONObject permissions; + String name; + + + // None to start with + page = getPages(null, null); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("totalPages")); + assertEquals(0, page.getInt("totalPages")); + + + // Won't be there to start with + page = getPage(PAGE_TITLE_ONE, Status.STATUS_NOT_FOUND); + + // Create + page = createOrUpdatePage(PAGE_TITLE_ONE, PAGE_CONTENTS_ONE, null, Status.STATUS_OK); + name = PAGE_TITLE_ONE.replace(' ', '_'); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("title")); + + assertEquals(name, page.getString("name")); + assertEquals(PAGE_TITLE_ONE, page.getString("title")); + assertEquals(PAGE_CONTENTS_ONE, page.getString("pagetext")); + assertEquals(0, page.getJSONArray("tags").length()); + + + // Fetch + page = getPage(name, Status.STATUS_OK); + + assertEquals(name, page.getString("name")); + assertEquals(PAGE_TITLE_ONE, page.getString("title")); + assertEquals(PAGE_CONTENTS_ONE, page.getString("pagetext")); + assertEquals(0, page.getJSONArray("tags").length()); + assertEquals(0, page.getJSONArray("links").length()); + + // Check the permissions + assertEquals(true, page.has("permissions")); + permissions = page.getJSONObject("permissions"); + assertEquals(true, permissions.getBoolean("create")); + assertEquals(true, permissions.getBoolean("edit")); + assertEquals(true, permissions.getBoolean("delete")); + + + // Edit + // We should get a simple message + page = createOrUpdatePage(PAGE_TITLE_ONE, "M"+PAGE_CONTENTS_ONE, null, Status.STATUS_OK); + assertEquals(name, page.getString("name")); + assertEquals(PAGE_TITLE_ONE, page.getString("title")); + assertEquals("M"+PAGE_CONTENTS_ONE, page.getString("pagetext")); + assertEquals(0, page.getJSONArray("tags").length()); + + + + // Fetch + page = getPage(name, Status.STATUS_OK); + + assertEquals(name, page.getString("name")); + assertEquals(PAGE_TITLE_ONE, page.getString("title")); + assertEquals("M"+PAGE_CONTENTS_ONE, page.getString("pagetext")); + assertEquals(0, page.getJSONArray("tags").length()); + assertEquals(0, page.getJSONArray("links").length()); + + + // Fetch as a different user, permissions different + this.authenticationComponent.setCurrentUser(USER_TWO); + page = getPage(name, Status.STATUS_OK); + + // Check the basics + assertEquals(name, page.getString("name")); + assertEquals(PAGE_TITLE_ONE, page.getString("title")); + assertEquals("M"+PAGE_CONTENTS_ONE, page.getString("pagetext")); + assertEquals(0, page.getJSONArray("tags").length()); + assertEquals(0, page.getJSONArray("links").length()); + + // Different user in the site, can edit but not delete + assertEquals(true, page.has("permissions")); + permissions = page.getJSONObject("permissions"); + assertEquals(true, permissions.getBoolean("create")); + assertEquals(true, permissions.getBoolean("edit")); + assertEquals(false, permissions.getBoolean("delete")); + + this.authenticationComponent.setCurrentUser(USER_ONE); + + + // Delete + page = deletePage(name, Status.STATUS_NO_CONTENT); + assertEquals(null, page); // No response + + + // Fetch, will have gone + page = getPage(name, Status.STATUS_NOT_FOUND); + + // On a page that isn't there, you do get permissions + assertEquals(true, page.has("permissions")); + permissions = page.getJSONObject("permissions"); + assertEquals(true, permissions.getBoolean("create")); + assertEquals(true, permissions.getBoolean("edit")); + assertEquals(false, permissions.has("delete")); // No delete for non existing page + + + // Can't delete again + deletePage(name, Status.STATUS_NOT_FOUND); + } + + public void testRenaming() throws Exception + { + JSONObject page; + String name; + String name2; + + + // Create a page + page = createOrUpdatePage(PAGE_TITLE_TWO, PAGE_CONTENTS_ONE, null, Status.STATUS_OK); + name = PAGE_TITLE_TWO.replace(' ', '_'); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("title")); + + + // Fetch it and check + page = getPage(name, Status.STATUS_OK); + assertEquals(name, page.getString("name")); + assertEquals(PAGE_TITLE_TWO, page.getString("title")); + + + // Have it renamed + page = renamePage(PAGE_TITLE_TWO, PAGE_TITLE_THREE, Status.STATUS_OK); + name2 = PAGE_TITLE_THREE.replace(' ', '_'); + assertEquals(PAGE_TITLE_THREE, page.getString("title")); + + + // Fetch it at the new address + page = getPage(name2, Status.STATUS_OK); + assertEquals(name2, page.getString("name")); + assertEquals(PAGE_TITLE_THREE, page.getString("title")); + + + // Get the old one, and ensure we see the "has been moved" + page = getPage(name, Status.STATUS_OK); + assertEquals(name, page.getString("name")); + assertEquals(PAGE_TITLE_TWO, page.getString("title")); + assertEquals("This page has been moved [["+PAGE_TITLE_THREE+"|here]].", page.getString("pagetext")); + + + // Ensure you can't rename onto an existing page + page = createOrUpdatePage(PAGE_TITLE_ONE, PAGE_CONTENTS_THREE, null, Status.STATUS_OK); + String name1 = PAGE_TITLE_ONE.replace(' ', '_'); + + // Check the rename won't work + renamePage(PAGE_TITLE_THREE, PAGE_TITLE_ONE, Status.STATUS_CONFLICT); + + // Check that the pages weren't changed + page = getPage(name2, Status.STATUS_OK); + assertEquals(name2, page.getString("name")); + assertEquals(PAGE_TITLE_THREE, page.getString("title")); + + page = getPage(name1, Status.STATUS_OK); + assertEquals(name1, page.getString("name")); + assertEquals(PAGE_TITLE_ONE, page.getString("title")); + } + + public void testRenamePageWithEmptyTitle() throws Exception + { + JSONObject page; + String name = System.currentTimeMillis()+""; + String name2 = System.currentTimeMillis()+1+""; + + // Create a page + page = createOrUpdatePage(name, name, null, Status.STATUS_OK); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("title")); + + // Fetch it and check + page = getPage(name, Status.STATUS_OK); + assertEquals(name, page.getString("name")); + assertEquals(name, page.getString("title")); + + //update title + SiteInfo site = siteService.getSite(SITE_SHORT_NAME_WIKI); + WikiPageInfo pageInfo = wikiService.getWikiPage(site.getShortName(), name); + NodeRef pageRef = pageInfo.getNodeRef(); + nodeService.setProperty(pageRef, ContentModel.PROP_TITLE, ""); + + // Fetch it and check + page = getPage(name, Status.STATUS_OK); + JSONArray versions = page.getJSONArray("versionhistory"); + + int maxVersionIndex = versions.length()-1; + double vNum = Double.parseDouble(versions.getJSONObject(maxVersionIndex).getString("version")); + String newTitle =versions.getJSONObject(maxVersionIndex).getString("title"); + for (int i = versions.length()-2; i>=0; i--) + { + JSONObject version = versions.getJSONObject(i); + String ver = version.getString("version"); + if (Double.parseDouble(ver)>vNum) + { + maxVersionIndex = i; + vNum = Double.parseDouble(ver); + newTitle =versions.getJSONObject(maxVersionIndex).getString("title"); + } + } + assertEquals(name, page.getString("name")); + assertEquals("", newTitle); + + renamePage(name, name2, Status.STATUS_OK); + + // Fetch it at the new address + page = getPage(name2, Status.STATUS_OK); + assertEquals(name2, page.getString("name")); + assertEquals(name2, page.getString("title")); + } + + public void testVersioning() throws Exception + { + WikiPageInfo wikiInfo; + JSONObject page; + JSONArray versions; + String name; + + // Create a page + page = createOrUpdatePage(PAGE_TITLE_TWO, PAGE_CONTENTS_ONE, null, Status.STATUS_OK); + name = PAGE_TITLE_TWO.replace(' ', '_'); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("title")); + + + // Check it was versioned by default + wikiInfo = wikiService.getWikiPage(SITE_SHORT_NAME_WIKI, name); + assertNotNull(wikiInfo); + assertEquals(true, nodeService.hasAspect(wikiInfo.getNodeRef(), ContentModel.ASPECT_VERSIONABLE)); + + // Check the JSON for versioning + page = getPage(name, Status.STATUS_OK); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("versionhistory")); + + versions = page.getJSONArray("versionhistory"); + assertEquals(1, versions.length()); + assertEquals("1.0", versions.getJSONObject(0).get("version")); + assertEquals(USER_ONE, versions.getJSONObject(0).get("author")); + + + // Fetch it at this version + String content = getPageAtVersion(name, "1.0", Status.STATUS_OK); + assertEquals(PAGE_CONTENTS_ONE, content); + + + // Upload a new copy without a version flag, denied + createOrUpdatePage(PAGE_TITLE_TWO, "Changed Contents", "none", Status.STATUS_CONFLICT); + + + // Upload a new copy with the appropriate version, allowed + String PAGE_CONTENTS_CHANGED = "Changed Contents 2"; + page = createOrUpdatePage(PAGE_TITLE_TWO, PAGE_CONTENTS_CHANGED, "1.0", Status.STATUS_OK); + + page = getPage(name, Status.STATUS_OK); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("versionhistory")); + + versions = page.getJSONArray("versionhistory"); + assertEquals(2, versions.length()); + assertEquals("1.1", versions.getJSONObject(0).get("version")); + assertEquals(USER_ONE, versions.getJSONObject(0).get("author")); + assertEquals("1.0", versions.getJSONObject(1).get("version")); + assertEquals(USER_ONE, versions.getJSONObject(1).get("author")); + + + // Check the version contents + content = getPageAtVersion(name, "1.1", Status.STATUS_OK); + assertEquals(PAGE_CONTENTS_CHANGED, content); + content = getPageAtVersion(name, "1.0", Status.STATUS_OK); + assertEquals(PAGE_CONTENTS_ONE, content); + + + // Upload a new copy with the force flag, allowed + String PAGE_CONTENTS_CHANGED3 = "Changed Contents 3"; + page = createOrUpdatePage(PAGE_TITLE_TWO, PAGE_CONTENTS_CHANGED3, "force", Status.STATUS_OK); + + page = getPage(name, Status.STATUS_OK); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("versionhistory")); + + versions = page.getJSONArray("versionhistory"); + assertEquals(3, versions.length()); + assertEquals("1.2", versions.getJSONObject(0).get("version")); + assertEquals(USER_ONE, versions.getJSONObject(0).get("author")); + assertEquals("1.1", versions.getJSONObject(1).get("version")); + assertEquals(USER_ONE, versions.getJSONObject(1).get("author")); + assertEquals("1.0", versions.getJSONObject(2).get("version")); + assertEquals(USER_ONE, versions.getJSONObject(2).get("author")); + + // Check the version contents + content = getPageAtVersion(name, "1.2", Status.STATUS_OK); + assertEquals(PAGE_CONTENTS_CHANGED3, content); + content = getPageAtVersion(name, "1.1", Status.STATUS_OK); + assertEquals(PAGE_CONTENTS_CHANGED, content); + content = getPageAtVersion(name, "1.0", Status.STATUS_OK); + assertEquals(PAGE_CONTENTS_ONE, content); + + // You get an empty string back for invalid versions + content = getPageAtVersion(name, "1.4", Status.STATUS_OK); + assertEquals("", content); + } + + public void testLinks() throws Exception + { + JSONObject page; + JSONArray links; + String name; + String name2; + + // Create a page with no links + page = createOrUpdatePage(PAGE_TITLE_TWO, PAGE_CONTENTS_TWO, null, Status.STATUS_OK); + name = PAGE_TITLE_TWO.replace(' ', '_'); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("title")); + + + // Check, won't have any links shown + page = getPage(name, Status.STATUS_OK); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("links")); + links = page.getJSONArray("links"); + assertEquals(0, links.length()); + + + // Create a page with links + // Should have links to pages 1 and 2 + page = createOrUpdatePage(PAGE_TITLE_THREE, PAGE_CONTENTS_LINK, null, Status.STATUS_OK); + name2 = PAGE_TITLE_THREE.replace(' ', '_'); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("title")); + + // Check + page = getPage(name2, Status.STATUS_OK); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("links")); + + links = page.getJSONArray("links"); + assertEquals(3, links.length()); + assertEquals(PAGE_TITLE_ONE, links.getString(0)); + assertEquals(name, links.getString(1)); + assertEquals("Invalid", links.getString(2)); + + + // Create the 1st page, now change + page = createOrUpdatePage(PAGE_TITLE_ONE, PAGE_CONTENTS_ONE, null, Status.STATUS_OK); + + page = getPage(name2, Status.STATUS_OK); + assertEquals("Incorrect JSON: " + page.toString(), true, page.has("links")); + + links = page.getJSONArray("links"); + assertEquals(3, links.length()); + assertEquals(PAGE_TITLE_ONE, links.getString(0)); + assertEquals(name, links.getString(1)); + assertEquals("Invalid", links.getString(2)); + } + + /** + * Listing + */ + public void testOverallListing() throws Exception + { + JSONObject pages; + JSONArray entries; + + // Initially, there are no events + pages = getPages(null, null); + assertEquals("Incorrect JSON: " + pages.toString(), true, pages.has("totalPages")); + assertEquals(0, pages.getInt("totalPages")); + + + // Add two links to get started with + createOrUpdatePage(PAGE_TITLE_ONE, PAGE_CONTENTS_ONE, null, Status.STATUS_OK); + createOrUpdatePage(PAGE_TITLE_TWO, PAGE_CONTENTS_TWO, null, Status.STATUS_OK); + + // Check again + pages = getPages(null, null); + + // Should have two links + assertEquals("Incorrect JSON: " + pages.toString(), true, pages.has("totalPages")); + assertEquals(2, pages.getInt("totalPages")); + + entries = pages.getJSONArray("pages"); + assertEquals(2, entries.length()); + // Sorted by newest created first + assertEquals(PAGE_TITLE_TWO, entries.getJSONObject(0).getString("title")); + assertEquals(PAGE_TITLE_ONE, entries.getJSONObject(1).getString("title")); + + + // Add a third, which is internal, and created by the other user + this.authenticationComponent.setCurrentUser(USER_TWO); + JSONObject page3 = createOrUpdatePage(PAGE_TITLE_THREE, PAGE_CONTENTS_THREE, null, Status.STATUS_OK); + String name3 = PAGE_TITLE_THREE.replace(' ', '_'); + createOrUpdatePage(PAGE_TITLE_THREE, "UD"+PAGE_CONTENTS_THREE, null, Status.STATUS_OK); + this.authenticationComponent.setCurrentUser(USER_ONE); + + + // Check now, should have three links + pages = getPages(null, null); + assertEquals(3, pages.getInt("totalPages")); + + entries = pages.getJSONArray("pages"); + assertEquals(3, entries.length()); + assertEquals(PAGE_TITLE_THREE, entries.getJSONObject(0).getString("title")); + assertEquals(PAGE_TITLE_TWO, entries.getJSONObject(1).getString("title")); + assertEquals(PAGE_TITLE_ONE, entries.getJSONObject(2).getString("title")); + + + // Ask for filtering by user + pages = getPages(null, USER_ONE); + assertEquals(2, pages.getInt("totalPages")); + + entries = pages.getJSONArray("pages"); + assertEquals(2, entries.length()); + assertEquals(PAGE_TITLE_TWO, entries.getJSONObject(0).getString("title")); + assertEquals(PAGE_TITLE_ONE, entries.getJSONObject(1).getString("title")); + + pages = getPages(null, USER_TWO); + assertEquals(1, pages.getInt("totalPages")); + + entries = pages.getJSONArray("pages"); + assertEquals(1, entries.length()); + assertEquals(PAGE_TITLE_THREE, entries.getJSONObject(0).getString("title")); + + + + // Ask for filtering by recently added docs + pages = getPages("recentlyAdded", null); + assertEquals(3, pages.getInt("totalPages")); + + entries = pages.getJSONArray("pages"); + assertEquals(3, entries.length()); + assertEquals(PAGE_TITLE_THREE, entries.getJSONObject(0).getString("title")); + assertEquals(PAGE_TITLE_TWO, entries.getJSONObject(1).getString("title")); + assertEquals(PAGE_TITLE_ONE, entries.getJSONObject(2).getString("title")); + + // Push one back into the past + pushPageCreatedDateBack(name3, 10); + + pages = getPages("recentlyAdded", null); + assertEquals(2, pages.getInt("totalPages")); + + entries = pages.getJSONArray("pages"); + assertEquals(2, entries.length()); + assertEquals(PAGE_TITLE_TWO, entries.getJSONObject(0).getString("title")); + assertEquals(PAGE_TITLE_ONE, entries.getJSONObject(1).getString("title")); + + + // Now for recently modified ones + pages = getPages("recentlyModified", null); + assertEquals(3, pages.getInt("totalPages")); + + entries = pages.getJSONArray("pages"); + assertEquals(3, entries.length()); + assertEquals(PAGE_TITLE_THREE, entries.getJSONObject(0).getString("title")); + assertEquals(PAGE_TITLE_TWO, entries.getJSONObject(1).getString("title")); + assertEquals(PAGE_TITLE_ONE, entries.getJSONObject(2).getString("title")); +// assertEquals(PAGE_TITLE_THREE, entries.getJSONObject(2).getString("title")); + + + // Change the owner+creator of one of the pages to System, ensure that + // this doesn't break anything in the process + String pageTwoName = entries.getJSONObject(1).getString("name"); + WikiPageInfo pageTwo = wikiService.getWikiPage(SITE_SHORT_NAME_WIKI, pageTwoName); + nodeService.setProperty(pageTwo.getNodeRef(), ContentModel.PROP_OWNER, AuthenticationUtil.SYSTEM_USER_NAME); + nodeService.setProperty(pageTwo.getNodeRef(), ContentModel.PROP_CREATOR, AuthenticationUtil.SYSTEM_USER_NAME); + nodeService.setProperty(pageTwo.getNodeRef(), ContentModel.PROP_MODIFIER, AuthenticationUtil.SYSTEM_USER_NAME); + + // Check the listing still works (note - order will have changed) + pages = getPages("recentlyModified", null); + assertEquals(3, pages.getInt("totalPages")); + + entries = pages.getJSONArray("pages"); + assertEquals(3, entries.length()); + assertEquals(PAGE_TITLE_TWO, entries.getJSONObject(0).getString("title")); + assertEquals(PAGE_TITLE_THREE, entries.getJSONObject(1).getString("title")); + assertEquals(PAGE_TITLE_ONE, entries.getJSONObject(2).getString("title")); + + + // Delete User Two, who owns the 3rd page, and ensure that this + // doesn't break anything + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + personService.deletePerson(USER_TWO); + this.authenticationComponent.setCurrentUser(USER_ONE); + + // Check the listing still works + pages = getPages("recentlyModified", null); + assertEquals(3, pages.getInt("totalPages")); + + entries = pages.getJSONArray("pages"); + assertEquals(3, entries.length()); + assertEquals(PAGE_TITLE_TWO, entries.getJSONObject(0).getString("title")); + assertEquals(PAGE_TITLE_THREE, entries.getJSONObject(1).getString("title")); + assertEquals(PAGE_TITLE_ONE, entries.getJSONObject(2).getString("title")); + + + // Now hide the site, and remove the user from it, won't be allowed to see it + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + SiteInfo site = siteService.getSite(SITE_SHORT_NAME_WIKI); + site.setVisibility(SiteVisibility.PRIVATE); + siteService.updateSite(site); + siteService.removeMembership(SITE_SHORT_NAME_WIKI, USER_ONE); + this.authenticationComponent.setCurrentUser(USER_ONE); + + sendRequest(new GetRequest(URL_WIKI_LIST), Status.STATUS_NOT_FOUND); + } + + public void test_MNT11595() throws Exception + { + final String user = "wikiUser"; + + try + { + // admin authentication + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + + MutableAuthenticationService mas = (MutableAuthenticationService) getServer().getApplicationContext().getBean("authenticationService"); + + // create user + createUser(user, SiteModel.SITE_MANAGER); + + assertTrue(personService.personExists(user)); + + // invite user to a site with 'Manager' role + siteService.setMembership(SITE_SHORT_NAME_WIKI, user, SiteRole.SiteManager.toString()); + + // user authentication + this.authenticationComponent.setCurrentUser(user); + + // create wiki page by user ('Manager' role) + WikiPageInfo wikiPage = this.wikiService.createWikiPage(SITE_SHORT_NAME_WIKI, "test wiki page", + "I like pigs. Dogs look up to us. Cats look down on us. Pigs treat us as equals. Sir Winston Churchill"); + + String uri = "/slingshot/wiki/page/" + SITE_SHORT_NAME_WIKI + "/Main_Page?alf_ticket=" + mas.getCurrentTicket() + "application/json"; + + Response responseManagerRole = sendRequest(new GetRequest(uri), 404); + JSONObject resultManagerRole = new JSONObject(responseManagerRole.getContentAsString()); + JSONObject permissionsManagerRole = resultManagerRole.getJSONObject("permissions"); + assertTrue(permissionsManagerRole.getBoolean("create")); + assertTrue(permissionsManagerRole.getBoolean("edit")); + + // admin authentication + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + + // change user role - 'Consumer' role + siteService.setMembership(SITE_SHORT_NAME_WIKI, user, SiteRole.SiteConsumer.toString()); + + // user authentication + this.authenticationComponent.setCurrentUser(user); + + Response responseConsumerRole = sendRequest(new GetRequest(uri), 404); + JSONObject resultConsumerRole = new JSONObject(responseConsumerRole.getContentAsString()); + JSONObject permissionsConsumerRole = resultConsumerRole.getJSONObject("permissions"); + assertFalse(permissionsConsumerRole.getBoolean("create")); + assertFalse(permissionsConsumerRole.getBoolean("edit")); + } + finally + { + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + + if (personService.personExists(user)) + { + personService.deletePerson(user); + } + + if (this.authenticationService.authenticationExists(user)) + { + this.authenticationService.deleteAuthentication(user); + } + } + } + + public void testXSSInjection() throws Exception + { + WikiPageInfo wikiPage = null; + WikiPageInfo wikiPageNew = null; + try{ + wikiPage = this.wikiService.createWikiPage(SITE_SHORT_NAME_WIKI, "test_wiki", "[[Test]]"); + wikiPageNew = this.wikiService.createWikiPage(SITE_SHORT_NAME_WIKI, "Test", "test_content"); + Pattern LINK_PATTERN_MATCH = Pattern.compile("\\[\\[([^\\|\\]]+)"); + Matcher m = LINK_PATTERN_MATCH.matcher(wikiPage.getContents()); + while (m.find()) + { + String link = m.group(1); + link += "?title="; + WikiPageInfo wikiPage2 = this.wikiService.getWikiPage(SITE_SHORT_NAME_WIKI, link); + WikiPageInfo wikiPage1 = this.wikiService.getWikiPage(SITE_SHORT_NAME_WIKI, StringEscapeUtils.unescapeHtml(link)); + assertEquals(wikiPage2, wikiPage1); + } + + } + finally{ + this.wikiService.deleteWikiPage(wikiPage); + this.wikiService.deleteWikiPage(wikiPageNew); + } + } +} \ No newline at end of file diff --git a/amps/share-services/src/test/java/org/alfresco/repo/wiki/WikiServiceImplTest.java b/amps/share-services/src/test/java/org/alfresco/repo/wiki/WikiServiceImplTest.java new file mode 100644 index 0000000000..3252bd4d49 --- /dev/null +++ b/amps/share-services/src/test/java/org/alfresco/repo/wiki/WikiServiceImplTest.java @@ -0,0 +1,879 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.wiki; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.site.SiteModel; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.cmr.tagging.TaggingService; +import org.alfresco.service.cmr.wiki.WikiPageInfo; +import org.alfresco.service.cmr.wiki.WikiService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.PropertyMap; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.context.ApplicationContext; + +/** + * Test cases for {@link WikiServiceImpl}. + * + * @author Nick Burch + * @since 4.0 + */ +public class WikiServiceImplTest +{ + private static final String TEST_SITE_PREFIX = "WikiSiteTest"; + private static final long ONE_DAY_MS = 24*60*60*1000; + + private static final ApplicationContext testContext = ApplicationContextHelper.getApplicationContext( + new String[] { "classpath:alfresco/application-context.xml", + "classpath:alfresco/web-scripts-application-context.xml" }); + + // injected services + private static MutableAuthenticationService AUTHENTICATION_SERVICE; + private static BehaviourFilter BEHAVIOUR_FILTER; + private static WikiService WIKI_SERVICE; + private static ContentService CONTENT_SERVICE; + @SuppressWarnings("unused") + private static DictionaryService DICTIONARY_SERVICE; + private static NodeService NODE_SERVICE; + private static NodeService PUBLIC_NODE_SERVICE; + private static PersonService PERSON_SERVICE; + private static RetryingTransactionHelper TRANSACTION_HELPER; + @SuppressWarnings("unused") + private static TransactionService TRANSACTION_SERVICE; + private static PermissionService PERMISSION_SERVICE; + private static SiteService SITE_SERVICE; + private static TaggingService TAGGING_SERVICE; + + private static final String TEST_USER = WikiServiceImplTest.class.getSimpleName() + "_testuser"; + private static final String ADMIN_USER = AuthenticationUtil.getAdminUserName(); + + private static SiteInfo WIKI_SITE; + private static SiteInfo ALTERNATE_WIKI_SITE; + + /** + * Temporary test nodes (created during a test method) that need deletion after the test method. + */ + private List testNodesToTidy = new ArrayList(); + /** + * Temporary test nodes (created BeforeClass) that need deletion after this test class. + */ + private static List CLASS_TEST_NODES_TO_TIDY = new ArrayList(); + + @BeforeClass public static void initTestsContext() throws Exception + { + AUTHENTICATION_SERVICE = (MutableAuthenticationService)testContext.getBean("authenticationService"); + BEHAVIOUR_FILTER = (BehaviourFilter)testContext.getBean("policyBehaviourFilter"); + WIKI_SERVICE = (WikiService)testContext.getBean("WikiService"); + CONTENT_SERVICE = (ContentService)testContext.getBean("ContentService"); + DICTIONARY_SERVICE = (DictionaryService)testContext.getBean("dictionaryService"); + NODE_SERVICE = (NodeService)testContext.getBean("nodeService"); + PUBLIC_NODE_SERVICE = (NodeService)testContext.getBean("NodeService"); + PERSON_SERVICE = (PersonService)testContext.getBean("personService"); + TRANSACTION_HELPER = (RetryingTransactionHelper)testContext.getBean("retryingTransactionHelper"); + TRANSACTION_SERVICE = (TransactionService)testContext.getBean("TransactionService"); + PERMISSION_SERVICE = (PermissionService)testContext.getBean("permissionService"); + SITE_SERVICE = (SiteService)testContext.getBean("siteService"); + TAGGING_SERVICE = (TaggingService)testContext.getBean("TaggingService"); + + // Do the setup as admin + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + createUser(TEST_USER); + + // We need to create the test site as the test user so that they can contribute content to it in tests below. + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + createTestSites(); + } + + @Test public void createNewEntry() throws Exception + { + WikiPageInfo page; + + // Nothing to start with + PagingResults results = + WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), new PagingRequest(10)); + assertEquals(0, results.getPage().size()); + + + // Get with an arbitrary name gives nothing + page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), "madeUp"); + assertEquals(null, page); + + page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), "madeUp2"); + assertEquals(null, page); + + // Create one + page = WIKI_SERVICE.createWikiPage( + WIKI_SITE.getShortName(), "Title", "This Is Some Content"); + + + // Ensure it got a noderef, and the correct site + assertNotNull(page.getNodeRef()); + assertNotNull(page.getSystemName()); + + NodeRef container = PUBLIC_NODE_SERVICE.getPrimaryParent(page.getNodeRef()).getParentRef(); + NodeRef site = PUBLIC_NODE_SERVICE.getPrimaryParent(container).getParentRef(); + assertEquals(WIKI_SITE.getNodeRef(), site); + + // Ensure the content was correctly set up + ContentReader reader = CONTENT_SERVICE.getReader(page.getNodeRef(), ContentModel.PROP_CONTENT); + assertEquals("This Is Some Content", reader.getContentString()); + assertEquals(MimetypeMap.MIMETYPE_HTML, reader.getMimetype()); + assertEquals("UTF-8", reader.getEncoding()); + + + + // Check the details on the object + assertEquals("Title", page.getTitle()); + assertEquals("This Is Some Content", page.getContents()); + assertEquals(ADMIN_USER, page.getCreator()); + assertEquals(0, page.getTags().size()); + + + // Fetch it, and check the details + page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName()); + assertEquals("Title", page.getTitle()); + assertEquals("This Is Some Content", page.getContents()); + assertEquals(ADMIN_USER, page.getCreator()); + assertEquals(0, page.getTags().size()); + + + // Mark it as done with + testNodesToTidy.add(page.getNodeRef()); + } + + @Test public void createUpdateDeleteEntry() throws Exception + { + WikiPageInfo page; + + // Run as the test user instead + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + + + // Create a page + page = WIKI_SERVICE.createWikiPage( + WIKI_SITE.getShortName(), "Title", "This Is Some Content"); + testNodesToTidy.add(page.getNodeRef()); + + + // Check it + assertEquals("Title", page.getSystemName()); + assertEquals("Title", page.getTitle()); + assertEquals("This Is Some Content", page.getContents()); + assertEquals(TEST_USER, page.getCreator()); + assertEquals(0, page.getTags().size()); + + + // Change it + page.setTitle("New Title"); + page.setContents("This is new content"); + + page = WIKI_SERVICE.updateWikiPage(page); + assertEquals("New_Title", page.getSystemName()); // Name has underscores + assertEquals("New_Title", page.getTitle()); + + + // Fetch, and check + page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName()); + assertEquals("New_Title", page.getSystemName()); // Name has underscores + assertEquals("New_Title", page.getTitle()); + assertEquals("This is new content", page.getContents()); + assertEquals(TEST_USER, page.getCreator()); + assertEquals(0, page.getTags().size()); + + + // Delete it + WIKI_SERVICE.deleteWikiPage(page); + + // Check it went + assertEquals(null, WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName())); + + + // Create a new node with spaces in title + page = WIKI_SERVICE.createWikiPage( + WIKI_SITE.getShortName(), "Title Space", "This Is Some Content"); + testNodesToTidy.add(page.getNodeRef()); + + // Check it + assertEquals("Title_Space", page.getSystemName()); + assertEquals("Title_Space", page.getTitle()); + assertEquals("This Is Some Content", page.getContents()); + assertEquals(TEST_USER, page.getCreator()); + assertEquals(0, page.getTags().size()); + + + // Edit it without renaming + page.setContents("Changed contents"); + page = WIKI_SERVICE.updateWikiPage(page); + + // Check + page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName()); + assertEquals("Title_Space", page.getSystemName()); + assertEquals("Title_Space", page.getTitle()); + assertEquals("Changed contents", page.getContents()); + assertEquals(TEST_USER, page.getCreator()); + assertEquals(0, page.getTags().size()); + + + // Now edit with renaming + page.setTitle("Alternate Title"); + page = WIKI_SERVICE.updateWikiPage(page); + + // Check + page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName()); + assertEquals("Alternate_Title", page.getSystemName()); + assertEquals("Alternate_Title", page.getTitle()); + assertEquals("Changed contents", page.getContents()); + assertEquals(TEST_USER, page.getCreator()); + assertEquals(0, page.getTags().size()); + } + + /** + * Ensures that when we try to write an entry to the + * container of a new site, it is correctly setup for us. + * This test does it's own transactions + */ + @Test public void newContainerSetup() throws Exception + { + final String TEST_SITE_NAME = "WikiTestNewTestSite"; + + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + if(SITE_SERVICE.getSite(TEST_SITE_NAME) != null) + { + SITE_SERVICE.deleteSite(TEST_SITE_NAME); + } + SITE_SERVICE.createSite( + TEST_SITE_PREFIX, TEST_SITE_NAME, "Test", "Test", SiteVisibility.PUBLIC); + + // Won't have the container to start with + assertFalse(SITE_SERVICE.hasContainer(TEST_SITE_NAME, WikiServiceImpl.WIKI_COMPONENT)); + + // Create a link + WIKI_SERVICE.createWikiPage( + TEST_SITE_NAME, "Title", "TextTextText"); + + // It will now exist + assertTrue(SITE_SERVICE.hasContainer(TEST_SITE_NAME, WikiServiceImpl.WIKI_COMPONENT)); + + // It'll be a tag scope too + NodeRef container = SITE_SERVICE.getContainer(TEST_SITE_NAME, WikiServiceImpl.WIKI_COMPONENT); + assertTrue(TAGGING_SERVICE.isTagScope(container)); + + // Tidy up + SITE_SERVICE.deleteSite(TEST_SITE_NAME); + return null; + } + }); + } + + @Test public void tagging() throws Exception + { + WikiPageInfo page; + final String TAG_1 = "link_tag_1"; + final String TAG_2 = "link_tag_2"; + final String TAG_3 = "link_tag_3"; + + // Create one without tagging + page = WIKI_SERVICE.createWikiPage( + WIKI_SITE.getShortName(), "Title", "This Is Some Content"); + testNodesToTidy.add(page.getNodeRef()); + + // Check + assertEquals(0, page.getTags().size()); + + page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName()); + assertEquals(0, page.getTags().size()); + + + // Update it to have tags + page.getTags().add(TAG_1); + page.getTags().add(TAG_2); + page.getTags().add(TAG_1); + assertEquals(3, page.getTags().size()); + WIKI_SERVICE.updateWikiPage(page); + + // Check + page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName()); + assertEquals(2, page.getTags().size()); + assertEquals(true, page.getTags().contains(TAG_1)); + assertEquals(true, page.getTags().contains(TAG_2)); + assertEquals(false, page.getTags().contains(TAG_3)); + + + // Update it to have different tags + page.getTags().remove(TAG_2); + page.getTags().add(TAG_3); + page.getTags().add(TAG_1); + WIKI_SERVICE.updateWikiPage(page); + + // Check it as-is + assertEquals(3, page.getTags().size()); // Includes duplicate tag until re-loaded + assertEquals(true, page.getTags().contains(TAG_1)); + assertEquals(false, page.getTags().contains(TAG_2)); + assertEquals(true, page.getTags().contains(TAG_3)); + + // Now load and re-check + page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName()); + assertEquals(2, page.getTags().size()); // Duplicate now gone + assertEquals(true, page.getTags().contains(TAG_1)); + assertEquals(false, page.getTags().contains(TAG_2)); + assertEquals(true, page.getTags().contains(TAG_3)); + + + // Update it to have no tags + page.getTags().clear(); + WIKI_SERVICE.updateWikiPage(page); + + // Check + page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName()); + assertEquals(0, page.getTags().size()); + + + // Update it to have tags again + page.getTags().add(TAG_1); + page.getTags().add(TAG_2); + page.getTags().add(TAG_3); + WIKI_SERVICE.updateWikiPage(page); + + // Check + page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName()); + assertEquals(3, page.getTags().size()); + assertEquals(true, page.getTags().contains(TAG_1)); + assertEquals(true, page.getTags().contains(TAG_2)); + assertEquals(true, page.getTags().contains(TAG_3)); + + // Tidy + WIKI_SERVICE.deleteWikiPage(page); + } + + /** + * Tests for listing the wiki pages of a site, possibly by user or date range + */ + @Test public void pagesListing() throws Exception + { + PagingRequest paging = new PagingRequest(10); + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + + // Nothing to start with + PagingResults results = + WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), paging); + assertEquals(0, results.getPage().size()); + + // Add a few + WikiPageInfo pageA = WIKI_SERVICE.createWikiPage( + WIKI_SITE.getShortName(), "TitleA", "ContentA"); + WikiPageInfo pageB = WIKI_SERVICE.createWikiPage( + WIKI_SITE.getShortName(), "TitleB", "ContentB"); + WikiPageInfo pageC = WIKI_SERVICE.createWikiPage( + WIKI_SITE.getShortName(), "TitleC", "ContentC"); + testNodesToTidy.add(pageA.getNodeRef()); + testNodesToTidy.add(pageB.getNodeRef()); + testNodesToTidy.add(pageC.getNodeRef()); + + // Check now, should be newest first + results = WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), paging); + assertEquals(3, results.getPage().size()); + assertEquals("TitleC", results.getPage().get(0).getTitle()); + assertEquals("TitleB", results.getPage().get(1).getTitle()); + assertEquals("TitleA", results.getPage().get(2).getTitle()); + + // Add one more, as a different user, and drop the page size + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + WikiPageInfo pageD = WIKI_SERVICE.createWikiPage( + WIKI_SITE.getShortName(), "TitleD", "ContentD"); + testNodesToTidy.add(pageD.getNodeRef()); + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + + paging = new PagingRequest(3); + results = WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), paging); + assertEquals(3, results.getPage().size()); + assertEquals("TitleD", results.getPage().get(0).getTitle()); + assertEquals("TitleC", results.getPage().get(1).getTitle()); + assertEquals("TitleB", results.getPage().get(2).getTitle()); + + paging = new PagingRequest(3, 3); + results = WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), paging); + assertEquals(1, results.getPage().size()); + assertEquals("TitleA", results.getPage().get(0).getTitle()); + + + // Now check filtering by user + paging = new PagingRequest(10); + + results = WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), TEST_USER, paging); + assertEquals(3, results.getPage().size()); + assertEquals("TitleC", results.getPage().get(0).getTitle()); + assertEquals("TitleB", results.getPage().get(1).getTitle()); + assertEquals("TitleA", results.getPage().get(2).getTitle()); + + results = WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), ADMIN_USER, paging); + assertEquals(1, results.getPage().size()); + assertEquals("TitleD", results.getPage().get(0).getTitle()); + + + // Now check filtering by date range + // Arrange it so that the orders are: + // Created -> C B A D + // Modified -> D C B A + pushAuditableDatesBack(pageB, 10, 0); + pushAuditableDatesBack(pageC, 100, 10); + pushAuditableDatesBack(pageD, 0, 100); + pageA.setContents("UpdatedContentsA"); + pageA = WIKI_SERVICE.updateWikiPage(pageA); + + + Date today = new Date(); + Date tomorrow = new Date(today.getTime()+ONE_DAY_MS); + Date yesterday = new Date(today.getTime()-ONE_DAY_MS); + Date twoWeeksAgo = new Date(today.getTime()-14*ONE_DAY_MS); + + + // Check by created date + + // Very recent ones + results = WIKI_SERVICE.listWikiPagesByCreated(WIKI_SITE.getShortName(), yesterday, tomorrow, paging); + assertEquals(2, results.getPage().size()); + assertEquals("TitleD", results.getPage().get(0).getTitle()); + assertEquals("TitleA", results.getPage().get(1).getTitle()); + + // Fairly old ones + results = WIKI_SERVICE.listWikiPagesByCreated(WIKI_SITE.getShortName(), twoWeeksAgo, yesterday, paging); + assertEquals(1, results.getPage().size()); + assertEquals("TitleB", results.getPage().get(0).getTitle()); + + // Fairly old to current + results = WIKI_SERVICE.listWikiPagesByCreated(WIKI_SITE.getShortName(), twoWeeksAgo, tomorrow, paging); + assertEquals(3, results.getPage().size()); + assertEquals("TitleD", results.getPage().get(0).getTitle()); + assertEquals("TitleA", results.getPage().get(1).getTitle()); + assertEquals("TitleB", results.getPage().get(2).getTitle()); + + + // Check by modified date + + // Very recent ones + results = WIKI_SERVICE.listWikiPagesByModified(WIKI_SITE.getShortName(), yesterday, tomorrow, paging); + assertEquals(2, results.getPage().size()); + assertEquals("TitleA", results.getPage().get(0).getTitle()); + assertEquals("TitleB", results.getPage().get(1).getTitle()); + + // Fairly old ones + results = WIKI_SERVICE.listWikiPagesByModified(WIKI_SITE.getShortName(), twoWeeksAgo, yesterday, paging); + assertEquals(1, results.getPage().size()); + assertEquals("TitleC", results.getPage().get(0).getTitle()); + + // Fairly old to current + results = WIKI_SERVICE.listWikiPagesByModified(WIKI_SITE.getShortName(), twoWeeksAgo, tomorrow, paging); + assertEquals(3, results.getPage().size()); + assertEquals("TitleA", results.getPage().get(0).getTitle()); + assertEquals("TitleB", results.getPage().get(1).getTitle()); + assertEquals("TitleC", results.getPage().get(2).getTitle()); + + + // Bring C back to current and re-check + pageC.setContents("Changed C"); + pageC = WIKI_SERVICE.updateWikiPage(pageC); + + // Order doesn't change, sorting is by created date not modified date + results = WIKI_SERVICE.listWikiPagesByModified(WIKI_SITE.getShortName(), twoWeeksAgo, tomorrow, paging); + assertEquals(3, results.getPage().size()); + assertEquals("TitleA", results.getPage().get(0).getTitle()); + assertEquals("TitleB", results.getPage().get(1).getTitle()); + assertEquals("TitleC", results.getPage().get(2).getTitle()); + + + // Tidy + paging = new PagingRequest(10); + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + results = WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), paging); + for (WikiPageInfo link : results.getPage()) + { + PUBLIC_NODE_SERVICE.deleteNode(link.getNodeRef()); + } + results = WIKI_SERVICE.listWikiPages(ALTERNATE_WIKI_SITE.getShortName(), paging); + for (WikiPageInfo link : results.getPage()) + { + PUBLIC_NODE_SERVICE.deleteNode(link.getNodeRef()); + } + } + + /** + * Checks that the correct permission checking occurs on fetching + * links listings (which go through canned queries) + */ + @Test public void pagesListingPermissionsChecking() throws Exception + { + PagingRequest paging = new PagingRequest(10); + PagingResults results; + + // Nothing to start with in either site + results = WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), paging); + assertEquals(0, results.getPage().size()); + results = WIKI_SERVICE.listWikiPages(ALTERNATE_WIKI_SITE.getShortName(), paging); + assertEquals(0, results.getPage().size()); + + // Double check that we're only allowed to see the 1st site + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + assertEquals(true, SITE_SERVICE.isMember(WIKI_SITE.getShortName(), TEST_USER)); + assertEquals(false, SITE_SERVICE.isMember(ALTERNATE_WIKI_SITE.getShortName(), TEST_USER)); + return null; + } + }); + + + // Now become the test user + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + + + // Add two events to one site and three to the other + // Note - add the events as a different user for the site that the + // test user isn't a member of! + WikiPageInfo pageA = WIKI_SERVICE.createWikiPage( + WIKI_SITE.getShortName(), "TitleA", "ContentA"); + WikiPageInfo pageB = WIKI_SERVICE.createWikiPage( + WIKI_SITE.getShortName(), "TitleB", "ContentB"); + testNodesToTidy.add(pageA.getNodeRef()); + testNodesToTidy.add(pageB.getNodeRef()); + + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + WikiPageInfo pagePrivA = WIKI_SERVICE.createWikiPage( + ALTERNATE_WIKI_SITE.getShortName(), "PrivTitleA", "Contents A"); + WikiPageInfo pagePrivB = WIKI_SERVICE.createWikiPage( + ALTERNATE_WIKI_SITE.getShortName(), "PrivTitleB", "Contents B"); + WikiPageInfo pagePrivC = WIKI_SERVICE.createWikiPage( + ALTERNATE_WIKI_SITE.getShortName(), "PrivTitleC", "Contents C"); + testNodesToTidy.add(pagePrivA.getNodeRef()); + testNodesToTidy.add(pagePrivB.getNodeRef()); + testNodesToTidy.add(pagePrivC.getNodeRef()); + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + + + // Check again, as we're not in the 2nd site won't see any there + results = WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), paging); + assertEquals(2, results.getPage().size()); + results = WIKI_SERVICE.listWikiPages(ALTERNATE_WIKI_SITE.getShortName(), paging); + assertEquals(0, results.getPage().size()); + + + // Join the site, now we can see both + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + SITE_SERVICE.setMembership(ALTERNATE_WIKI_SITE.getShortName(), TEST_USER, SiteModel.SITE_COLLABORATOR); + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + return null; + } + }); + + results = WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), paging); + assertEquals(2, results.getPage().size()); + results = WIKI_SERVICE.listWikiPages(ALTERNATE_WIKI_SITE.getShortName(), paging); + assertEquals(3, results.getPage().size()); + + + // Explicitly remove their permissions from one node, check it vanishes from the list + PERMISSION_SERVICE.setInheritParentPermissions(pagePrivC.getNodeRef(), false); + PERMISSION_SERVICE.clearPermission(pagePrivC.getNodeRef(), TEST_USER); + + results = WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), paging); + assertEquals(2, results.getPage().size()); + results = WIKI_SERVICE.listWikiPages(ALTERNATE_WIKI_SITE.getShortName(), paging); + assertEquals(2, results.getPage().size()); + + + // Leave, they go away again + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + SITE_SERVICE.removeMembership(ALTERNATE_WIKI_SITE.getShortName(), TEST_USER); + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + return null; + } + }); + + results = WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), paging); + assertEquals(2, results.getPage().size()); + results = WIKI_SERVICE.listWikiPages(ALTERNATE_WIKI_SITE.getShortName(), paging); + assertEquals(0, results.getPage().size()); + + + // Tidy + paging = new PagingRequest(10); + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + results = WIKI_SERVICE.listWikiPages(WIKI_SITE.getShortName(), paging); + for (WikiPageInfo link : results.getPage()) + { + PUBLIC_NODE_SERVICE.deleteNode(link.getNodeRef()); + } + results = WIKI_SERVICE.listWikiPages(ALTERNATE_WIKI_SITE.getShortName(), paging); + for (WikiPageInfo link : results.getPage()) + { + PUBLIC_NODE_SERVICE.deleteNode(link.getNodeRef()); + } + } + + + // -------------------------------------------------------------------------------- + + + /** + * Alters the created date on a wiki page for testing + */ + private void pushAuditableDatesBack(WikiPageInfo page, int createdDaysAgo, int modifiedDaysAgo) throws Exception + { + final NodeRef node = page.getNodeRef(); + + final Date created = page.getCreatedAt(); + final Date newCreated = new Date(created.getTime() - createdDaysAgo*ONE_DAY_MS); + final Date modified = page.getModifiedAt(); + final Date newModified = new Date(modified.getTime() - modifiedDaysAgo*ONE_DAY_MS); + + // Update the created date + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + BEHAVIOUR_FILTER.disableBehaviour(ContentModel.ASPECT_AUDITABLE); + NODE_SERVICE.setProperty(node, ContentModel.PROP_CREATED, newCreated); + NODE_SERVICE.setProperty(node, ContentModel.PROP_MODIFIED, newModified); + return null; + } + }, false, true); + + // Change something else too in the public nodeservice, to force a re-index + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + BEHAVIOUR_FILTER.disableBehaviour(ContentModel.ASPECT_AUDITABLE); + PUBLIC_NODE_SERVICE.setProperty(node, ContentModel.PROP_CREATED, newCreated); + PUBLIC_NODE_SERVICE.setProperty(node, ContentModel.PROP_MODIFIED, newModified); + PUBLIC_NODE_SERVICE.setProperty(node, ContentModel.PROP_DESCRIPTION, "Forced Change"); + return null; + } + }, false, true); + + // Check it was taken + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + assertEquals(newCreated, NODE_SERVICE.getProperty(node, ContentModel.PROP_CREATED)); + assertEquals(newCreated, PUBLIC_NODE_SERVICE.getProperty(node, ContentModel.PROP_CREATED)); + assertEquals(newModified, NODE_SERVICE.getProperty(node, ContentModel.PROP_MODIFIED)); + assertEquals(newModified, PUBLIC_NODE_SERVICE.getProperty(node, ContentModel.PROP_MODIFIED)); + return null; + } + }, false, true); + + // Update the object itself + ((WikiPageInfoImpl)page).setCreatedAt(newCreated); + ((WikiPageInfoImpl)page).setModifiedAt(newModified); + } + + private static void createTestSites() throws Exception + { + final WikiServiceImpl privateWikiPageService = (WikiServiceImpl)testContext.getBean("wikiService"); + + WIKI_SITE = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public SiteInfo execute() throws Throwable + { + SiteInfo site = SITE_SERVICE.createSite( + TEST_SITE_PREFIX, + WikiServiceImplTest.class.getSimpleName() + "_testSite" + System.currentTimeMillis(), + "test site title", "test site description", + SiteVisibility.PUBLIC); + privateWikiPageService.getSiteWikiContainer(site.getShortName(), true); + CLASS_TEST_NODES_TO_TIDY.add(site.getNodeRef()); + return site; + } + }); + + // Create the alternate site as admin + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + ALTERNATE_WIKI_SITE = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public SiteInfo execute() throws Throwable + { + SiteInfo site = SITE_SERVICE.createSite( + TEST_SITE_PREFIX, + WikiServiceImplTest.class.getSimpleName() + "_testAltSite" + System.currentTimeMillis(), + "alternate site title", "alternate site description", + SiteVisibility.PRIVATE); + privateWikiPageService.getSiteWikiContainer(site.getShortName(), true); + CLASS_TEST_NODES_TO_TIDY.add(site.getNodeRef()); + return site; + } + }); + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + } + + /** + * By default, all tests are run as the admin user. + */ + @Before public void setAdminUser() + { + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + } + + @After public void deleteTestNodes() throws Exception + { + performDeletionOfNodes(testNodesToTidy); + } + + @AfterClass public static void deleteClassTestNodesAndUsers() throws Exception + { + performDeletionOfNodes(CLASS_TEST_NODES_TO_TIDY); + deleteUser(TEST_USER); + } + + /** + * Deletes the specified NodeRefs, if they exist. + * @param nodesToDelete + */ + private static void performDeletionOfNodes(final List nodesToDelete) + { + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + + for (NodeRef node : nodesToDelete) + { + if (NODE_SERVICE.exists(node)) + { + // st:site nodes can only be deleted via the SiteService + if (NODE_SERVICE.getType(node).equals(SiteModel.TYPE_SITE)) + { + + SiteInfo siteInfo = SITE_SERVICE.getSite(node); + SITE_SERVICE.deleteSite(siteInfo.getShortName()); + } + else + { + NODE_SERVICE.deleteNode(node); + } + } + } + + return null; + } + }); + } + + private static void createUser(final String userName) + { + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + if (!AUTHENTICATION_SERVICE.authenticationExists(userName)) + { + AUTHENTICATION_SERVICE.createAuthentication(userName, "PWD".toCharArray()); + } + + if (!PERSON_SERVICE.personExists(userName)) + { + PropertyMap ppOne = new PropertyMap(); + ppOne.put(ContentModel.PROP_USERNAME, userName); + ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); + ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); + ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + PERSON_SERVICE.createPerson(ppOne); + } + + return null; + } + }); + } + + private static void deleteUser(final String userName) + { + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + if (PERSON_SERVICE.personExists(userName)) + { + PERSON_SERVICE.deletePerson(userName); + } + + return null; + } + }); + } +} diff --git a/amps/share-services/src/test/java/org/alfresco/slingshot/documentlibrary/FolderTemplateTest.java b/amps/share-services/src/test/java/org/alfresco/slingshot/documentlibrary/FolderTemplateTest.java new file mode 100644 index 0000000000..916b5a5189 --- /dev/null +++ b/amps/share-services/src/test/java/org/alfresco/slingshot/documentlibrary/FolderTemplateTest.java @@ -0,0 +1,163 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.slingshot.documentlibrary; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.GUID; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * JUnit test for folder-templates API + * + * @author alex.mukha + * @since 4.2.4 + */ +public class FolderTemplateTest extends BaseWebScriptTest +{ + private AuthenticationComponent authenticationComponent; + private Repository repositoryHelper; + private NodeService nodeService; + private TransactionService transactionService; + private SearchService searchService; + private FileFolderService fileFolderService; + private NodeRef companyHome; + private NodeRef template; + private NodeRef destination; + private String templateName; + private String destinationName; + private UserTransaction txn; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + this.authenticationComponent = (AuthenticationComponent)getServer().getApplicationContext().getBean("authenticationComponent"); + this.repositoryHelper = (Repository)getServer().getApplicationContext().getBean("repositoryHelper"); + this.nodeService = (NodeService)getServer().getApplicationContext().getBean("NodeService"); + this.transactionService = (TransactionService) getServer().getApplicationContext().getBean("TransactionService"); + this.searchService = (SearchService) getServer().getApplicationContext().getBean("SearchService"); + this.fileFolderService = (FileFolderService) getServer().getApplicationContext().getBean("FileFolderService"); + + this.authenticationComponent.setSystemUserAsCurrentUser(); + + txn = transactionService.getUserTransaction(); + txn.begin(); + + companyHome = this.repositoryHelper.getCompanyHome(); + + // Create template folder + Map propsTemplate = new HashMap(1); + templateName = "templateFolder" + GUID.generate(); + propsTemplate.put(ContentModel.PROP_NAME, templateName); + template = nodeService.createNode(companyHome, ContentModel.ASSOC_CHILDREN, QName.createQName(templateName), ContentModel.TYPE_FOLDER, propsTemplate).getChildRef(); + + // Create destination + Map propsDestination = new HashMap(1); + destinationName = "destinationFolder" + GUID.generate(); + propsTemplate.put(ContentModel.PROP_NAME, destinationName); + destination = nodeService.createNode(companyHome, ContentModel.ASSOC_CHILDREN, QName.createQName(destinationName), ContentModel.TYPE_FOLDER, propsDestination).getChildRef(); + + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + } + + @Override + protected void tearDown() throws Exception + { + txn.rollback(); + } + + /** + * Test for MNT-11909 + * @throws Exception + */ + @SuppressWarnings("unchecked") + public void testFolderTemplatesPost() throws Exception + { + String url = "/slingshot/doclib/folder-templates"; + String newName = "FolderName" + GUID.generate(); + String newDescription = "FolderDescription" + GUID.generate(); + String newTitle = "FolderTitle" + GUID.generate(); + + JSONObject body = new JSONObject(); + body.put("sourceNodeRef", template.toString()); + body.put("parentNodeRef", destination.toString()); + body.put("prop_cm_name", newName); + body.put("prop_cm_description", newDescription); + body.put("prop_cm_title", newTitle); + + Response response = sendRequest(new PostRequest(url, body.toString(), "application/json"), Status.STATUS_OK); + + // Check the new folder + String newFolderQuery = "/app:company_home/" + destinationName + "/" + newName; + ResultSet result = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, newFolderQuery); + if (result.length() == 0) + { + fail("The folder with name " + newName + " was not created in " + destinationName); + } + FileInfo newFolder = fileFolderService.getFileInfo(result.getRow(0).getNodeRef()); + assertNotNull("The folder is not found.", newFolder); + assertTrue("The node should be a folder.", newFolder.isFolder()); + assertEquals("The folder's name should be " + newName + + ", but was " + newFolder.getName(), + newName, newFolder.getName()); + assertEquals("The folder's description should be " + newDescription + + ", but was " + newFolder.getProperties().get(ContentModel.PROP_DESCRIPTION), + newDescription, newFolder.getProperties().get(ContentModel.PROP_DESCRIPTION)); + assertEquals("The folder's title should be " + newTitle + + ", but was " + newFolder.getProperties().get(ContentModel.PROP_TITLE), + newTitle, newFolder.getProperties().get(ContentModel.PROP_TITLE)); + + // check the response + JSONParser jsonParser = new JSONParser(); + Object contentJsonObject = jsonParser.parse(response + .getContentAsString()); + JSONObject jsonData = (JSONObject) contentJsonObject; + String persistedObject = (String) jsonData.get("persistedObject"); + assertEquals("The response's persistedObject should be " + + newFolder.getNodeRef().toString() + " but it was " + + persistedObject, newFolder.getNodeRef().toString(), + persistedObject); + } +} diff --git a/amps/share-services/src/test/java/org/alfresco/slingshot/web/scripts/SlingshotContentGetTest.java b/amps/share-services/src/test/java/org/alfresco/slingshot/web/scripts/SlingshotContentGetTest.java new file mode 100644 index 0000000000..f500bf0cd2 --- /dev/null +++ b/amps/share-services/src/test/java/org/alfresco/slingshot/web/scripts/SlingshotContentGetTest.java @@ -0,0 +1,241 @@ +/* + * Copyright 2005 - 2020 Alfresco Software Limited. + * + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of the paid license agreement will prevail. + * Otherwise, the software is provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.slingshot.web.scripts; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.cmr.repository.*; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyMap; +import org.json.JSONObject; +import org.junit.Assert; +import org.springframework.extensions.webscripts.TestWebScriptServer; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Test for SlingshotContentGet web script + * @author alex.mukha + * @since 5.0.0 + */ +public class SlingshotContentGetTest extends BaseWebScriptTest +{ + private MutableAuthenticationService authenticationService; + private AuthenticationComponent authenticationComponent; + private PersonService personService; + private SiteService siteService; + private NodeService nodeService; + private ContentService contentService; + private PermissionService permissionService; + + private static final String USER_ONE = "SlingshotContentGetTestOne"; + private static final String URL_SITES = "/api/sites"; + private static final String URL_CONTENT_DOWNLOAD = "/slingshot/node/content/workspace/SpacesStore/"; + private List createdSites = new ArrayList(1); + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + this.authenticationService = (MutableAuthenticationService)getServer().getApplicationContext().getBean("AuthenticationService"); + this.authenticationComponent = (AuthenticationComponent)getServer().getApplicationContext().getBean("authenticationComponent"); + this.personService = (PersonService)getServer().getApplicationContext().getBean("PersonService"); + this.siteService = (SiteService)getServer().getApplicationContext().getBean("SiteService"); + this.nodeService = (NodeService)getServer().getApplicationContext().getBean("NodeService"); + this.permissionService = (PermissionService)getServer().getApplicationContext().getBean("PermissionService"); + this.contentService = (ContentService)getServer().getApplicationContext().getBean("ContentService"); + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + createUser(USER_ONE); + } + + private void createUser(String userName) + { + if (!this.authenticationService.authenticationExists(userName)) + { + this.authenticationService.createAuthentication(userName, "PWD".toCharArray()); + + PropertyMap ppOne = new PropertyMap(5); + ppOne.put(ContentModel.PROP_USERNAME, userName); + ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); + ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); + ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + this.personService.createPerson(ppOne); + } + } + private void deleteUser(String username) + { + this.personService.deletePerson(username); + if(this.authenticationService.authenticationExists(username)) + { + this.authenticationService.deleteAuthentication(username); + } + } + + + private JSONObject createSite(String sitePreset, String shortName, String title, String description, SiteVisibility visibility, int expectedStatus) + throws Exception + { + JSONObject site = new JSONObject(); + site.put("sitePreset", sitePreset); + site.put("shortName", shortName); + site.put("title", title); + site.put("description", description); + site.put("visibility", visibility.toString()); + TestWebScriptServer.Response response = sendRequest(new TestWebScriptServer.PostRequest(URL_SITES, site.toString(), "application/json"), expectedStatus); + this.createdSites.add(shortName); + return new JSONObject(response.getContentAsString()); + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + // Clear the user + deleteUser(USER_ONE); + // Tidy-up any site's create during the execution of the test + for (String shortName : this.createdSites) + { + sendRequest(new TestWebScriptServer.DeleteRequest(URL_SITES + "/" + shortName), 0); + } + // Clear the list + this.createdSites.clear(); + this.authenticationComponent.clearCurrentSecurityContext(); + } + + public void testDownloadBySiteMemberFromPrivateSite() throws Exception + { + String shortName = GUID.generate(); + // Create a new site + createSite("myPreset", shortName, "myTitle", "myDescription", SiteVisibility.PRIVATE, 200); + + // Ensure we have th document library + NodeRef docLib = siteService.createContainer(shortName, SiteService.DOCUMENT_LIBRARY, ContentModel.TYPE_FOLDER, null); + + NodeRef doc = nodeService.createNode(docLib, ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS, ContentModel.TYPE_CONTENT).getChildRef(); + nodeService.setProperty(doc, ContentModel.PROP_CONTENT, new ContentData(null, MimetypeMap.MIMETYPE_TEXT_PLAIN, 0L, null)); + nodeService.setProperty(doc, ContentModel.PROP_TITLE, "title"); + ContentWriter writer = contentService.getWriter(doc, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("test"); + + String uri = URL_CONTENT_DOWNLOAD + doc.getId() + "?a=true"; + sendRequest(new GetRequest(uri), 200); + } + + public void testDownloadByNonSiteMemberFromPrivateSite() throws Exception + { + String shortName = GUID.generate(); + // Create a new site + createSite("myPreset", shortName, "myTitle", "myDescription", SiteVisibility.PRIVATE, 200); + + NodeRef docLib = siteService.createContainer(shortName, SiteService.DOCUMENT_LIBRARY, ContentModel.TYPE_FOLDER, null); + NodeRef doc = nodeService.createNode(docLib, ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS, ContentModel.TYPE_CONTENT).getChildRef(); + nodeService.setProperty(doc, ContentModel.PROP_CONTENT, new ContentData(null, MimetypeMap.MIMETYPE_TEXT_PLAIN, 0L, null)); + nodeService.setProperty(doc, ContentModel.PROP_TITLE, "title"); + ContentWriter writer = contentService.getWriter(doc, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent("test"); + + permissionService.setPermission(doc, USER_ONE, PermissionService.CONSUMER, true); + + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + String uri = URL_CONTENT_DOWNLOAD + doc.getId() + "?a=true"; + sendRequest(new GetRequest(uri), 200); + } + + /** + * MNT-16380 + */ + public void testRelativePath() throws Exception + { + Repository repositoryHelper = (Repository) getServer().getApplicationContext().getBean("repositoryHelper"); + NodeRef companyHome = repositoryHelper.getCompanyHome(); + + NodeRef rootFolder = createNode(companyHome, "rootFolder", ContentModel.TYPE_FOLDER); + + NodeRef doc1 = createNodeWithTextContent(rootFolder, "doc1", ContentModel.TYPE_CONTENT, "doc1 file content"); + + NodeRef folderX = createNode(rootFolder, "X", ContentModel.TYPE_FOLDER); + NodeRef folderY = createNode(folderX, "Y", ContentModel.TYPE_FOLDER); + NodeRef folderZ = createNode(folderY, "Z", ContentModel.TYPE_FOLDER); + + NodeRef doc2 = createNodeWithTextContent(folderZ, "doc2", ContentModel.TYPE_CONTENT, "doc2 file content"); + + // uri with relative path at the end + String uri = URL_CONTENT_DOWNLOAD + doc1.getId() + "/X/Y/Z/doc2"; + TestWebScriptServer.Response resp = sendRequest(new GetRequest(uri), 200); + + // check if we really have doc2 as target + Assert.assertEquals("doc2 file content", resp.getContentAsString()); + + nodeService.deleteNode(rootFolder); + } + + public NodeRef createNodeWithTextContent(NodeRef parentNode, String nodeCmName, QName nodeType, String content) + { + NodeRef nodeRef = createNode(parentNode, nodeCmName, nodeType); + + // If there is any content, add it. + if (content != null) + { + ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(content); + } + return nodeRef; + + } + + private NodeRef createNode(NodeRef parentNode, String nodeCmName, QName nodeType) + { + QName childName = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, nodeCmName); + + Map props = new HashMap(); + props.put(ContentModel.PROP_NAME, nodeCmName); + ChildAssociationRef childAssoc = nodeService + .createNode(parentNode, ContentModel.ASSOC_CONTAINS, childName, nodeType, props); + return childAssoc.getChildRef(); + } +}