();
+ 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
+
+
+
+
\ 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}"
+}
+#escape>
\ 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>,#if>
+ #list>]
+}
+#escape>
\ 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}"
+}
+#escape>
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\\._]+$")>
+
+#if>
+
+
\ 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\\._]+$")>
+
+#if>
+
+
\ 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"
+ }
+}
+#escape>
\ 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>
+<#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>#list>],
+ <#if item.site??>
+ "site":
+ {
+ "shortName": "${item.site.shortName}",
+ "title": "${item.site.title}"
+ },
+ "container": "${item.container}"
+ #if>
+}
+#escape>
+#macro>
+<#escape x as jsonUtils.encodeJSONString(x)>
+{
+ "blogPosts":
+ {
+ "items":
+ [
+ <#list data.blogPosts.items as item>
+ <@renderItem item /><#if item_has_next>,#if>
+ #list>
+ ]
+ },
+ "wikiPages":
+ {
+ "items":
+ [
+ <#list data.wikiPages.items as item>
+ <@renderItem item /><#if item_has_next>,#if>
+ #list>
+ ]
+ },
+ "forumPosts":
+ {
+ "items":
+ [
+ <#list data.discussions.items as item>
+ <@renderItem item /><#if item_has_next>,#if>
+ #list>
+ ]
+ }
+}
+#escape>
\ 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>/#if>${path}#list>"
+ #if>
+#macro>
+<#--
+ Render a task
+-->
+<#macro dateFormat date>${date?datetime?string("yyyy-MM-dd HH:mm:ss 'GMT'Z '('zzz')'")!""}#macro>
+<#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 />#if>",
+ "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>,#if>
+#list>
+ ],
+ "transitions":
+ [
+<#list task.transitions as transition>
+ {
+ "id": "${transition.id!""}",
+ "label": "${transition.label!""}"
+ }<#if transition_has_next>,#if>
+#list>
+ ]
+ }
+#macro>
+
+<#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 />#if>",
+ "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}",
+ #if>
+ "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>,#if>
+#list>
+ ]
+ }
+#macro>
+
+<#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 />#if>",
+ "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}",
+ #if>
+ "userName": "${theInviter.properties.userName}"
+ },
+ "inviteeRole": "${task.properties["inwf:inviteeRole"]}"
+ },
+ #if>
+ "transitions":
+ [
+<#list task.transitions as transition>
+ {
+ "id": "${transition.id!""}",
+ "label": "${transition.label!""}"
+ }<#if transition_has_next>,#if>
+#list>
+ ]
+ }
+#macro>
+
+
+<#--
+ 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}#if>#assign>
+ <#switch filter>
+ <#case "all">
+ <#assign filteredTasks = filteredTasks + [task]>
+ <#break>
+
+ <#case "today">
+ <#if (dateCompare(date?date, dueDate?date, 0, "==") == 1)>
+ <#assign filteredTasks = filteredTasks + [task]>
+ #if>
+ <#break>
+
+ <#case "tomorrow">
+ <#if (dateCompare(tomorrow?date, dueDate?date, 0, "==") == 1)>
+ <#assign filteredTasks = filteredTasks + [task]>
+ #if>
+ <#break>
+
+ <#case "this-week">
+ <#if ((dateCompare(lastSunday?date, dueDate?date) == 0) && (dateCompare(sunday?date, dueDate?date) == 1))>
+ <#assign filteredTasks = filteredTasks + [task]>
+ #if>
+ <#break>
+
+ <#case "next-week">
+ <#if ((dateCompare(sunday?date, dueDate?date) == 0) && (dateCompare(nextSunday?date, dueDate?date) == 1))>
+ <#assign filteredTasks = filteredTasks + [task]>
+ #if>
+ <#break>
+
+ <#case "overdue">
+ <#if (dateCompare(date?date, dueDate?date) == 1)>
+ <#assign filteredTasks = filteredTasks + [task]>
+ #if>
+ <#break>
+
+ <#case "no-due-date">
+ <#if !hasDate>
+ <#assign filteredTasks = filteredTasks + [task]>
+ #if>
+ <#break>
+
+ <#case "invites">
+ <#if task.properties["bpm:package"]??>
+ <#if inviteWorkflowDefinitionNames?seq_contains(task.properties["bpm:package"].properties["bpm:workflowDefinitionName"])>
+ <#assign filteredTasks = filteredTasks + [task]>
+ #if>
+ #if>
+ <#break>
+ #switch>
+#list>
+
+<#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>
+ #if>
+ <#if task_has_next>,#if>
+#list>
+ ]
+}
+#escape>
\ 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>,#if>
+ <#else>
+ "${key}": "${value}"<#if key_has_next>,#if>
+ #if>
+ #list>
+ }<#if r_has_next>,#if>
+ #list>
+ ]
+}
+ #escape>
+#macro>
\ 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>,#if>
+ #if>
+ #list>
+ }
+ }
+ #if>
+ }
+ },
+ "items":
+ [
+ <#list data.items as item>
+ {
+ <@itemLib.itemJSON item />
+ }<#if item_has_next>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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>,#if>#list>#assign>
+ "nodeRef": "${node.nodeRef}",
+ "createdOn": "${xmldate(node.properties.created)}",
+ "createdBy":
+ {
+ "value": "${item.createdBy.userName}",
+ "displayValue": "${item.createdBy.displayName}"
+ },
+ "modifiedOn": "${xmldate(node.properties.modified)}",
+ "modifiedBy":
+ {
+ "value": "${item.modifiedBy.userName}",
+ "displayValue": "${item.modifiedBy.displayName}"
+ },
+ "actionSet": "${item.actionSet}",
+ "tags": <#noescape>[${tags}]#noescape>,
+ "permissions":
+ {
+ "userAccess":
+ {
+ <#list item.actionPermissions?keys as actionPerm>
+ <#if item.actionPermissions[actionPerm]?is_boolean>
+ "${actionPerm?string}": ${item.actionPermissions[actionPerm]?string}<#if actionPerm_has_next>,#if>
+ #if>
+ #list>
+ }
+ },
+ <#if item.custom??>"custom": <#noescape>${item.custom}#noescape>,#if>
+ "actionLabels":
+ {
+ <#if item.actionLabels??>
+ <#list item.actionLabels?keys as actionLabel>
+ "${actionLabel?string}": "${item.actionLabels[actionLabel]}"<#if actionLabel_has_next>,#if>
+ #list>
+ #if>
+ },
+ "itemData":
+ {
+ <#list item.nodeData?keys as key>
+ <#assign itemData = item.nodeData[key]>
+ "${key}":
+ <#if itemData?is_sequence>
+ [
+ <#list itemData as data>
+ <@renderData data /><#if data_has_next>,#if>
+ #list>
+ ]
+ <#else>
+ <@renderData itemData />
+ #if><#if key_has_next>,#if>
+ #list>
+ }
+ #escape>
+#macro>
+
+<#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>
+ <#if data.metadata??>
+ "metadata": "${data.metadata}",
+ #if>
+ <#if data.displayValue?is_boolean>
+ "displayValue": ${data.displayValue?string}
+ <#elseif data.displayValue?is_number>
+ "displayValue": ${data.displayValue?c}
+ <#else>
+ "displayValue": "${data.displayValue}"
+ #if>
+}
+ #escape>
+#macro>
\ 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>,#if>
+ #if>
+ #list>
+ }
+ }
+ #if>
+ }
+ },
+ "item":
+ {
+ <@itemLib.itemJSON data.item />
+ }
+}
+#escape>
\ 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}
+ }
+}
+ #escape>
+#macro>
\ 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>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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},
+ #if>
+ "startIndex": ${doclist.paging.startIndex?c},
+ "metadata":
+ {
+ "repositoryId": "${server.id}",
+ <#if (doclist.container.nodeRef)??>"container": "${doclist.container.nodeRef}",#if>
+ <#if (doclist.parent.nodeJSON)??>"parent": <#noescape>${doclist.parent.nodeJSON},#noescape>#if>
+ <#if doclist.customJSON??>"custom": <#noescape>${doclist.customJSON},#noescape>#if>
+ "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}#noescape>,
+ <#if item.parent??>"parent": <#noescape>${item.parent.nodeJSON},#noescape>#if>
+ <@itemLib.itemJSON item=item />
+ }<#if item_has_next>,#if>
+ #list>
+ ]
+}
+#escape>
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"]!"">#if>
+ <#escape x as jsonUtils.encodeJSONString(x)>
+ "version": "${version}",
+ "webdavUrl": "${node.webdavUrl}",
+ <#if item.activeWorkflows?? && (item.activeWorkflows?size > 0)>"activeWorkflows": ${item.activeWorkflows?size?c},#if>
+ <#if item.isFavourite??>"isFavourite": ${item.isFavourite?string},#if>
+ <#if (item.workingCopyJSON?length > 2)>"workingCopy": <#noescape>${item.workingCopyJSON}#noescape>,#if>
+ <#if item.likes??>"likes":
+ {
+ "isLiked": ${item.likes.isLiked?string},
+ "totalLikes": ${item.likes.totalLikes?c}
+ },#if>
+ "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>
+ <#if item.location.container??>
+ "container":
+ {
+ "name": "${(item.location.container)!""}",
+ "type": "${(item.location.containerType)!""}",
+ "nodeRef": "${(item.location.containerNode.nodeRef)!""}"
+ },
+ #if>
+ "path": "${(item.location.path)!""}",
+ "repoPath": "${(item.location.repoPath)!""}",
+ "file": "${(item.location.file)!""}",
+ "parent":
+ {
+ <#if (item.location.parent.nodeRef)??>
+ "nodeRef": "${item.location.parent.nodeRef}"
+ #if>
+ }
+ }
+ #escape>
+#macro>
\ 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>,#if>#list>
+ ]
+}
+#escape>
\ 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},#noescape>#if>
+ <#if doclist.customJSON??>"custom": <#noescape>${doclist.customJSON},#noescape>#if>
+ "onlineEditing": ${doclist.onlineEditing?string},
+ "workingCopyLabel": "${workingCopyLabel}",
+ "shareURL": "${site.getShareUrl()}",
+ "serverURL": "${url.server}"
+ },
+ <#if doclist.content??>"itemContent": "${doclist.content}",#if>
+ "item":
+ {
+ <#if doclist.thumbnailDefinitions??>"thumbnailDefinitions": [<#list doclist.thumbnailDefinitions as thumbnail>"${thumbnail}"<#if thumbnail_has_next>,#if>#list>],#if>
+ <#if doclist.item??>
+ <#assign item = doclist.item>
+ "node": <#noescape>${item.nodeJSON}#noescape>,
+ <#if item.parent?? && item.parent.nodeJSON??>"parent": <#noescape>${item.parent.nodeJSON},#noescape>#if>
+ <@itemLib.itemJSON item=item />
+ #if>
+ }
+}
+#escape>
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>,#if>
+ <#else>
+ "${key}": "${value}"<#if key_has_next>,#if>
+ #if>
+ #list>
+ }<#if r_has_next>,#if>
+ #list>
+ ]
+}
+ #escape>
+#macro>
\ 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>,#if>#list>]
+}
+#escape>
\ 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>,#if>
+ #list>
+ ]
+}
+#escape>
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}"
+ }
+}
+#escape>
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>,#if>
+ #list>
+ ]
+}
+#escape>
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},
+ #if>
+ "startIndex": ${doclist.paging.startIndex?c},
+ "metadata":
+ {
+ "repositoryId": "${server.id}",
+ <#if doclist.container??>"container": "${doclist.container.nodeRef}",#if>
+ "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>,#if>
+ #if>
+ #list>
+ }
+ }
+ #if>
+ },
+ "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>,#if>
+ #list>
+ ]
+}
+#escape>
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>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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}"
+}
+#escape>
\ 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)}#macro>
+<#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>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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}
+ #if>
+#if>
+#macro>
+
+<#macro dateFormat date=""><#if date?is_date>${xmldate(date)}#if>#macro>
+
+<#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>
+ <#if item.createdBy??>
+ <#local createdBy = item.createdBy.displayName>
+ <#local createdByUser = item.createdBy.userName>
+ <#else>
+ <#local createdBy="" createdByUser="">
+ #if>
+ <#if item.modifiedBy??>
+ <#local modifiedBy = item.modifiedBy.displayName>
+ <#local modifiedByUser = item.modifiedBy.userName>
+ <#else>
+ <#local modifiedBy="" modifiedByUser="">
+ #if>
+ <#if item.lockedBy??>
+ <#local lockedBy = item.lockedBy.displayName>
+ <#local lockedByUser = item.lockedBy.userName>
+ <#else>
+ <#local lockedBy="" lockedByUser="">
+ #if>
+ <#local tags><#list item.tags as tag>"${tag}"<#if tag_has_next>,#if>#list>#local>
+ "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}#if>,
+ "isLink": ${(item.isLink!false)?string},
+<#if item.linkedNode??>
+ "linkedNodeRef": "${item.linkedNode.nodeRef?string}",
+#if>
+ "fileName": "<#if item.linkedNode??>${item.linkedNode.name}<#else>${node.name}#if>",
+ "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}#if>",
+ "status": "<#list item.status?keys as s><#if item.status[s]?is_boolean && item.status[s] == true>${s}<#if s_has_next>,#if>#if>#list>",
+ "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}",
+ #if>
+ #list>
+ #if>
+ #if>
+ "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}]#noescape>,
+ <#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>#list>],
+ #if>
+ <#if item.activeWorkflows??>"activeWorkflows": "<#list item.activeWorkflows as aw>${aw}<#if aw_has_next>,#if>#list>",#if>
+ <#if item.isFavourite??>"isFavourite": ${item.isFavourite?string},#if>
+ "likes":<#if item.likes??>
+ {
+ "isLiked": ${item.likes.isLiked?string},
+ "totalLikes": ${item.likes.totalLikes?c}
+ }<#else>null#if>,
+ "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>
+ #if>
+ }
+ },
+ <#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>
+ <#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>
+ <#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"]!""}"
+ },#if>
+ "permissions":
+ {
+ "inherited": ${node.inheritsPermissions?string},
+ "roles":
+ [
+ <#list node.fullPermissions as permission>
+ "${permission?string}"<#if permission_has_next>,#if>
+ #list>
+ ],
+ "userAccess":
+ {
+ <#list item.actionPermissions?keys as actionPerm>
+ <#if item.actionPermissions[actionPerm]?is_boolean>
+ "${actionPerm?string}": ${item.actionPermissions[actionPerm]?string}<#if actionPerm_has_next>,#if>
+ #if>
+ #list>
+ }
+ },
+ <#if item.custom??>"custom": <#noescape>${item.custom}#noescape>,#if>
+ "actionLabels":
+ {
+<#if item.actionLabels??>
+ <#list item.actionLabels?keys as actionLabel>
+ "${actionLabel?string}": "${item.actionLabels[actionLabel]}"<#if actionLabel_has_next>,#if>
+ #list>
+#if>
+ }
+ #escape>
+#macro>
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!""}"
+ },#if>
+ "repo":
+ {
+ "path": "${locations.repo.path!""}",
+ "file": "${locations.repo.file!""}"
+ }
+}
+#escape>
\ 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>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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}"
+}
+#escape>
\ 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>,#if>
+ #if>
+ #list>
+ }
+ }
+ #if>
+ },
+ "onlineEditing": ${doclist.onlineEditing?string}
+ },
+ "item":
+ {
+ <#if doclist.items??><@itemLib.itemJSON item=doclist.items[0] />#if>
+ }
+}
+#escape>
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"}",
+ #if>
+ "name": "${perm.authority.name}",
+ "displayName": "${perm.authority.displayName!perm.authority.name}"
+ },
+ "role": "${perm.role}"
+ }<#if perm_has_next>,#if>
+ #list>
+]
+ #escape>
+#macro>
+
+<#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>,#if>#list>
+ ]
+}
+#escape>
\ 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>
+ #list>
+ ]
+ }<#if item_has_next>,#if>
+ #list>
+ ]
+}
+#escape>
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}"
+}
+#escape>
\ 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}"
+ }
+#escape>
+#macro>
+{
+ <#if parent??><@nodeInfo parent "parent" />,#if>
+ <@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')'")}#macro>
+<#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 + ", "/>
+ #if>
+ <#assign result = result + "${key} = ${p.value[key]}" />
+ <#assign first = false/>
+ #list>
+ <#assign result = result + "}"/>
+ <#-- output the result -->
+ "${result}"
+ #if>
+ <#else>
+ null
+ #if>
+#macro>
+{
+ <#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}#if>",
+ "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>,#if>
+ #list>
+ ],
+ "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>,#if>
+ #list>
+ ],
+ "type": {
+ "name": "<#if p.typeName??>${p.typeName.name}#if>",
+ "prefixedName": "<#if p.typeName??>${p.typeName.prefixedName}#if>"
+ },
+ "multiple": ${p.collection?string},
+ "residual": ${p.residual?string}
+ }<#if p_has_next>,#if>
+ #list>
+ ],
+ "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>,#if>
+ #list>
+ ],
+ "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>,#if>
+ #list>
+ ],
+ "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>,#if>
+ #list>
+ ],
+ "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>,#if>
+ #list>
+ #if>
+ ],
+ "permissions": {
+ "entries": [
+ <#list permissions.entries as p>
+ {
+ "permission": "${p.permission}",
+ "authority": "${p.authority}",
+ "rel": "${p.accessStatus}"
+ }<#if p_has_next>,#if>
+ #list>
+ ],
+ "masks": [
+ <#list permissions.storePermissions as p>
+ {
+ "permission": "${p.permission}",
+ "authority": "${p.authority}",
+ "rel": "${p.accessStatus}"
+ }<#if p_has_next>,#if>
+ #list>
+ ],
+ "inherit": ${permissions.inherit?string},
+ "owner": "<#if permissions.owner?exists>${permissions.owner}#if>"
+ }
+ <#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>"
+ }<#if result_has_next>,#if>
+ #list>
+ ],
+ "searchElapsedTime": ${(searchElapsedTime!0)?c}
+ <#elseif stores??>
+ "stores": [
+ <#list stores as store>"${store}"<#if store_has_next>,#if>
+ #list>
+ ]
+ #if>
+}
+#escape>
\ 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\\._]+$")>
+
+#if>
+
+
\ 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\\._]+$")>
+
+#if>
+
+
\ 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"
+ }
+}
+#escape>
\ 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>
+<#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}"
+ },#if>
+ "container": "${item.container!""}",
+ "tags": [<#list item.tags as tag>"${tag}"<#if tag_has_next>,#if>#list>]
+ }<#if item_has_next>,#if>
+ #list>
+ ]
+}
+#escape>
+#macro>
+{
+ "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)}"},
+#if>
+ "success": ${success?string}
+}
+#escape>
\ 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}"
+}
+#if>
\ 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>
+#macro>
+<#escape x as jsonUtils.encodeJSONString(x)>
+{
+ "items":
+ [
+ <#list data as item>
+ <@renderItem item /><#if item_has_next>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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}"
+}
+#if>
\ 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>
+#macro>
+<#escape x as jsonUtils.encodeJSONString(x)>
+{
+ "items":
+ [
+ <#list data as item>
+ <@renderItem item /><#if item_has_next>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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}"
+}
+#if>
\ 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}"
+}
+#if>
\ 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>
+#macro>
+<#escape x as jsonUtils.encodeJSONString(x)>
+{
+ "items":
+ [
+ <#list data as item>
+ <@renderItem item /><#if item_has_next>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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>
+#macro>
+<#escape x as jsonUtils.encodeJSONString(x)>
+{
+ "services":
+ [
+ <#list data as item>
+ <@renderItem item /><#if item_has_next>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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>
+#macro>
+<#escape x as jsonUtils.encodeJSONString(x)>
+{
+ "widgets":
+ [
+ <#list data as item>
+ <@renderItem item /><#if item_has_next>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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>
+ <#if item.lastThumbnailModification??>
+ "lastThumbnailModification": "${item.lastThumbnailModification}",
+ #if>
+ "size": ${item.size?c},
+ "mimetype": "${item.mimetype!''}"
+ }<#if item_has_next>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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>
+#macro>
+<#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>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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>
+ #list>
+ ]<#if field_has_next>,#if>
+ #list>#if>
+ },
+ "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>
+ #list>
+ }<#if nodeRef_has_next>,#if>
+ #list>#if>
+ },
+ "items":
+ [
+ <#list data.items as item>
+ {
+ "node": <#noescape>${item.nodeJSON}#noescape>,
+ "nodeRef": "${item.nodeRef}",
+ "type": "${item.type}",
+ "name": "${item.name!''}",
+ "displayName": "${item.displayName!''}",
+ <#if item.title??>
+ "title": "${item.title}",
+ #if>
+ "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>
+ <#if item.path??>
+ "path": "${item.path}",
+ #if>
+ "lastThumbnailModification":
+ [
+ <#if item.lastThumbnailModification??>
+ <#list item.lastThumbnailModification as lastThumbnailMod>
+ "${lastThumbnailMod}"
+ <#if lastThumbnailMod_has_next>,#if>
+ #list>
+ #if>
+ ],
+ "tags": [<#list item.tags as tag>"${tag}"<#if tag_has_next>,#if>#list>],
+ "highlighting":
+ {
+ <#list item.highlighting?keys as property>
+ "${property}": "${item.highlighting[property][0]}"
+ <#if property_has_next>,#if>
+ #list>
+ }
+ }<#if item_has_next>,#if>
+ #list>
+ ],
+ "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>
+ #list>
+ <#else>
+ "searchSuggestions": [
+ <#list data.spellcheck.results as suggestion>
+ "${suggestion?string}"<#if suggestion_has_next>,#if>
+ #list>
+ ]
+ #if>
+ #if>
+ }
+}
+#escape>
\ 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}"
+#if>
+}
+#escape>
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}
+ }
+}
+#escape>
\ 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)}#macro>
+<#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("_", " ")}#if>",
+ "pagetext": "${page.contents}",
+ "pageList": [
+ <#list result.pageList as p>
+ "${p}"<#if p_has_next>,#if>
+ #list>
+ ]<#if !result.minWikiData>,#if>
+ <#if !result.minWikiData>
+ "tags": [
+ <#list result.tags as tag>
+ "${tag}"<#if tag_has_next>,#if>
+ #list>
+ ],
+ "links": [
+ <#list result.links as link>
+ "${link}"<#if link_has_next>,#if>
+ #list>
+ ],
+ <#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>,#if>
+ #list>
+ ],
+ #if>
+ "permissions":
+ {
+ "create": ${result.container.hasPermission("CreateChildren")?string},
+ "edit": ${node.hasPermission("Write")?string},
+ "delete": ${node.hasPermission("Delete")?string}
+ }
+ #if>
+<#else>
+ "error" : "${result.error!""}"
+#if>
+}
+#escape>
\ 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!""}
+#if>
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>,#if>#list>#assign>
+ "tags": <#noescape>[${tags}]#noescape>
+}
+#escape>
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)}#macro>
+<#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>",
+ }<#if p_has_next>,#if>
+ #list>
+ <#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("_", " ")}#if>",
+ "text": "${page.contents}",
+ "tags": [
+ <#list p.tags as tag>
+ "${tag}"<#if tag_has_next>,#if>
+ #list>
+ ],
+ "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="">
+ #if>
+ "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="">
+ #if>
+ "modifiedBy": "${modifiedBy}",
+ "modifiedByUser": "${modifiedByUser}",
+ "permissions":
+ {
+ "edit": ${node.hasPermission("Write")?string},
+ "delete": ${node.hasPermission("Delete")?string}
+ }
+ }<#if p_has_next>,#if>
+ #list>
+ #if>
+ ],
+ "pageTitles":
+ [
+ <#if wiki.pageTitles??>
+ <#list wiki.pageTitles as title>
+ "${title}"<#if title_has_next>,#if>
+ #list>
+ #if>
+ ]
+}
+#escape>
+
+<#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 -->
+#function>
\ 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}
+
+#list>
+
+
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();
+ }
+}