diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/admin/support-tools/admin-nodebrowser.get.html.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/admin/support-tools/admin-nodebrowser.get.html.ftl index ad7c4f2f0b..999733e14a 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/admin/support-tools/admin-nodebrowser.get.html.ftl +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/admin/support-tools/admin-nodebrowser.get.html.ftl @@ -40,13 +40,13 @@ ${url.serviceContext}/api/node/${nodeRef?replace("://","/")}/content;${prop?url}
<@section label=msg("nodebrowser.store") /> - <@options name="nodebrowser-store" style="display:inline" valueStyle="display:inline" onchange="AdminConsole_execute('root')" value="${args.store!'workspace://SpacesStore'}"> + <@options name="nodebrowser-store" style="display:inline" valueStyle="display:inline" onchange="AdminConsole_action('root')" value="${args.store!'workspace://SpacesStore'}"> <#list stores as s> <@option label=s value=s /> - <@button label=msg("nodebrowser.root") onclick="AdminConsole_execute('root')" /> - <#if action??><@button label=msg("nodebrowser.refresh") onclick="AdminConsole_execute('${action?html}')" class="input" style="position:absolute;top:60px;left:1042px" /> + <@button label=msg("nodebrowser.root") onclick="AdminConsole_action('root')" /> + <#if action??><@button label=msg("nodebrowser.refresh") onclick="AdminConsole_action('${action?html}')" class="input" style="position:absolute;top:60px;left:1042px" /> <@section label=msg("nodebrowser.query") /> <@options name="nodebrowser-search" style="display:inline" valueStyle="display:inline" value="${args.searcher!''}"> @@ -61,7 +61,7 @@ ${url.serviceContext}/api/node/${nodeRef?replace("://","/")}/content;${prop?url} <@option label="db-cmis" value="db-cmis" /> <@text id="query" name="nodebrowser-query" label="" value="${query!''}" style="display:inline" valueStyle="display:inline" controlStyle="width:50em" /> - <@button label=msg("nodebrowser.execute") onclick="AdminConsole_execute('search')" /> + <@button label=msg("nodebrowser.execute") onclick="AdminConsole_action('search')" /> <@tsection label=msg("nodebrowser.search-settings")>
<@text name="nodebrowser-query-maxresults" label=msg("nodebrowser.maxresults") value="${args.maxResults!''}" /> @@ -74,6 +74,8 @@ ${url.serviceContext}/api/node/${nodeRef?replace("://","/")}/content;${prop?url} <@hidden name="nodebrowser-action" id="action" /> <@hidden name="nodebrowser-action-value" id="action-value" value="${actionValue!''}" /> + <@hidden name="nodebrowser-execute" id="execute" /> + <@hidden name="nodebrowser-execute-value" id="execute-value" />
<#if result??> @@ -101,6 +103,7 @@ ${url.serviceContext}/api/node/${nodeRef?replace("://","/")}/content;${prop?url} ${msg("nodebrowser.type")} ${msg("nodebrowser.value")} ${msg("nodebrowser.residual")} + ${msg("nodebrowser.actions")} <#list result.properties as p> @@ -108,6 +111,9 @@ ${url.serviceContext}/api/node/${nodeRef?replace("://","/")}/content;${prop?url} <#if p.typeName??>${p.typeName.prefixedName}<#else>${none} <#if (p.values?size > 1)>${collection} (${p.values?size?c})
<#list p.values as v><#if v.content><@propValue v/><#if v.content><#if v_has_next>
${p.residual?string} + + ${msg("nodebrowser.delete")} + @@ -136,6 +142,7 @@ ${url.serviceContext}/api/node/${nodeRef?replace("://","/")}/content;${prop?url} ${msg("nodebrowser.primary")} ${msg("nodebrowser.association-type")} ${msg("nodebrowser.index")} + ${msg("nodebrowser.actions")} <#list result.children as n> @@ -144,6 +151,16 @@ ${url.serviceContext}/api/node/${nodeRef?replace("://","/")}/content;${prop?url} ${n.primary?string} ${n.typeQName} ${n_index} + <#assign isarchive=(args.store!"")?starts_with("archive://")> + + ${msg("nodebrowser.delete")} + <#if isarchive>| ${msg("nodebrowser.restore")} + <#else>| ${msg("nodebrowser.force-delete")} + | ${msg("nodebrowser.take-ownership")} + | ${msg("nodebrowser.delete-permissions")} + <#if n.childLocked>| ${msg("nodebrowser.unlock")} + + @@ -250,16 +267,16 @@ ${url.serviceContext}/api/node/${nodeRef?replace("://","/")}/content;${prop?url} Admin.addEventListener(window, 'load', function() { // bind Enter key press to call the Execute search button event handler Admin.addEventListener(el("query"), 'keypress', function(e) { - if (e.keyCode === 13) AdminConsole_execute('search'); + if (e.keyCode === 13) AdminConsole_action('search'); return true; }); <#if args.nodeRef??> - AdminConsole_execute('search'); + AdminConsole_action('search'); }); -function AdminConsole_execute(action) +function AdminConsole_action(action) { el("action").value = action; el("${FORM_ID}").submit(); @@ -269,13 +286,28 @@ function AdminConsole_execute(action) function AdminConsole_childClick(ref) { el("action-value").value = ref; - AdminConsole_execute("children"); + AdminConsole_action("children"); } function AdminConsole_parentClick(ref) { el("action-value").value = ref; - AdminConsole_execute("parent"); + AdminConsole_action("parent"); +} + +function AdminConsole_execute(value, execute) +{ + el("execute-value").value = value; + el("execute").value = execute; + AdminConsole_action('${(action!"")?html}'); +} + +function AdminConsole_confirmExecute(value, execute) +{ + if (confirm("${msg("nodebrowser.confirm")}")) + { + AdminConsole_execute(value, execute); + } } //]]> diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/admin/support-tools/admin-nodebrowser.get.properties b/config/alfresco/templates/webscripts/org/alfresco/repository/admin/support-tools/admin-nodebrowser.get.properties index a9f9ea6446..267bf738a9 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/admin/support-tools/admin-nodebrowser.get.properties +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/admin/support-tools/admin-nodebrowser.get.properties @@ -40,4 +40,25 @@ nodebrowser.permission=Permission nodebrowser.authority=Authority nodebrowser.access=Access nodebrowser.inherits=Inherits -nodebrowser.owner=Owner \ No newline at end of file +nodebrowser.owner=Owner +nodebrowser.actions=Actions +nodebrowser.delete=Delete +nodebrowser.delete.tip=Delete the node and any children. +nodebrowser.force-delete=Force Delete +nodebrowser.force-delete.tip=Delete the node and any children. The node will not be archived. Rules will not be run. +nodebrowser.restore=Restore +nodebrowser.restore.tip=Restore the node and any children to its original location. +nodebrowser.delete-property.tip=Delete the property from this node. +nodebrowser.take-ownership=Take Ownership +nodebrowser.take-ownership.tip=Take owner permission of the node. +nodebrowser.delete-permissions=Revert Permissions +nodebrowser.delete-permissions.tip=Delete all permissions that have been explicitly assigned to this node and restore permission inheritance. +nodebrowser.unlock=Unlock +nodebrowser.unlock.tip=Remove any locks held by this node. Cancels the checkout for any associated working copy and the working copy is removed. Note that all modifications made to the working copy will be lost. +nodebrowser.confirm=Are you sure? +nodebrowser.message.delete=Successfully deleted node. +nodebrowser.message.restore=Successfully restored node. +nodebrowser.message.take-ownership=Successfully took ownership. +nodebrowser.message.delete-permissions=Successfully removed permissions. +nodebrowser.message.delete-property=Successfully deleted property. +nodebrowser.message.unlock=Successfully unlocked node. \ No newline at end of file diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml index 5d8807345a..fdcdd0b458 100644 --- a/config/alfresco/web-scripts-application-context.xml +++ b/config/alfresco/web-scripts-application-context.xml @@ -1807,12 +1807,15 @@ - - - - - + + + + + + + + diff --git a/source/java/org/alfresco/repo/web/scripts/admin/NodeBrowserPost.java b/source/java/org/alfresco/repo/web/scripts/admin/NodeBrowserPost.java index 533eb83960..55687b378c 100644 --- a/source/java/org/alfresco/repo/web/scripts/admin/NodeBrowserPost.java +++ b/source/java/org/alfresco/repo/web/scripts/admin/NodeBrowserPost.java @@ -32,9 +32,13 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; @@ -85,6 +89,8 @@ public class NodeBrowserPost extends DeclarativeWebScript implements Serializabl transient private NamespaceService namespaceService; transient private PermissionService permissionService; transient private OwnableService ownableService; + transient private LockService lockService; + transient private CheckOutCheckInService cociService; /** * @param transactionService transaction service @@ -173,6 +179,26 @@ public class NodeBrowserPost extends DeclarativeWebScript implements Serializabl { return ownableService; } + + public void setLockService(LockService lockService) + { + this.lockService = lockService; + } + + protected LockService getLockService() + { + return this.lockService; + } + + public void setCheckOutCheckInService(CheckOutCheckInService cociService) + { + this.cociService = cociService; + } + + protected CheckOutCheckInService getCheckOutCheckInService() + { + return this.cociService; + } /** * Gets the list of repository stores @@ -425,8 +451,141 @@ public class NodeBrowserPost extends DeclarativeWebScript implements Serializabl long timeStart = System.currentTimeMillis(); String actionValue = req.getParameter("nodebrowser-action-value"); String action = req.getParameter("nodebrowser-action"); + final String execute = req.getParameter("nodebrowser-execute"); + final String executeValue = req.getParameter("nodebrowser-execute-value"); + String message = null; try { + // 'execute' is an action that performs an operation on a node e.g. delete + // the 'executeValue' param provides context + // this is done before the view action to ensure node state is correct + if (execute != null) + { + switch (execute) + { + case "delete": + { + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + // delete the node using the standard NodeService + nodeService.deleteNode(new NodeRef(executeValue)); + return null; + } + }, false, true); + message = "nodebrowser.message.delete"; + break; + } + case "fdelete": + { + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + // delete the node - but ensure that it is not archived + NodeRef ref = new NodeRef(executeValue); + nodeService.addAspect(ref, ContentModel.ASPECT_TEMPORARY, null); + nodeService.deleteNode(ref); + return null; + } + }, false, true); + message = "nodebrowser.message.delete"; + break; + } + case "restore": + { + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + nodeService.restoreNode(new NodeRef(executeValue), null, null, null); + return null; + } + }, false, true); + message = "nodebrowser.message.restore"; + break; + } + case "take-ownership": + { + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + ownableService.takeOwnership(new NodeRef(executeValue)); + return null; + } + }, false, true); + message = "nodebrowser.message.take-ownership"; + break; + } + case "delete-permissions": + { + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + NodeRef ref = new NodeRef(executeValue); + permissionService.deletePermissions(ref); + permissionService.setInheritParentPermissions(ref, true); + return null; + } + }, false, true); + message = "nodebrowser.message.delete-permissions"; + break; + } + case "delete-property": + { + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + // argument value contains "NodeRef|QName" packed string + String[] parts = executeValue.split("\\|"); + nodeService.removeProperty(new NodeRef(parts[0]), QName.createQName(parts[1])); + return null; + } + }, false, true); + message = "nodebrowser.message.delete-property"; + break; + } + case "unlock": + { + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + NodeRef ref = new NodeRef(executeValue); + if (cociService.isCheckedOut(ref)) + { + NodeRef wcRef = cociService.getWorkingCopy(ref); + if (wcRef != null) + { + cociService.cancelCheckout(wcRef); + } + } + else + { + lockService.unlock(ref); + } + return null; + } + }, false, true); + message = "nodebrowser.message.unlock"; + break; + } + } + } + + // 'action' is a view action that request an update of the admin console view state e.g. 'search' or 'children' + // the 'actionValue' param provides context as may other parameters such as 'query' switch (action) { // on Execute btn press and query present, perform search @@ -580,13 +739,14 @@ public class NodeBrowserPost extends DeclarativeWebScript implements Serializabl returnParams.put("skipCount", skipCount); returnParams.put("in", Long.toString(System.currentTimeMillis()-timeStart)); returnParams.put("e", error); + returnParams.put("m", message); // redirect as all admin console pages do (follow standard pattern) // The logic to generate the navigation section and server meta-data is all tied into alfresco-common.lib.js // which is great for writing JS based JMX surfaced pages, but not so great for Java backed WebScripts. status.setCode(301); status.setRedirect(true); - status.setLocation(buildUrl(req, returnParams, action)); + status.setLocation(buildUrl(req, returnParams, execute != null && execute.length() != 0 ? execute : action)); return null; } @@ -884,6 +1044,11 @@ public class NodeBrowserPost extends DeclarativeWebScript implements Serializabl { return ref.isPrimary(); } + + public boolean isChildLocked() + { + return lockService != null && lockService.getLockType(ref.getChildRef()) != null; + } } /**