If you have to change the content type (basically cm:content to a custom content type), then there is an OOTB action provided in Share document library.
This is very simple use case. But there could be a situation where you have multiple custom content types or folder types such as:
<types> <type name="demo:whitePaper"> <title>WhitePaper document</title> <description>A WhitePaper document</description> <parent>cm:content</parent> </type> <type name="demo:supportingDoc"> <title>Supporting document</title> <description>A supporting document</description> <parent>cm:content</parent> </type> </types>Here, both demo:whitePaper and demo:supportingDoc custom content types are defined by extending "cm:content" type.
-----------------------------------------------------------------With OOTB box change type action, if you have created a content/file which is of type "cm:content" then
you can change the type from cm:content to demo:whitePaper or demo:supportingDoc based on the configuration made
in share config (share-config-custom.xml).
Example of share-config-custom.xml config:
<config evaluator="string-compare" condition="DocumentLibrary"> <aspects> <visible/> <addable/> <removable/> </aspects> <types> <type name="cm:content"> <subtype name="demo:whitePaper"/> <subtype name="demo:supportingDoc"/> </type> </types> </config>Change Type action uses a repository webscript, it calls:
Document List Component - type submit at "/slingshot/doclib/type/node/{store_type}/{store_id}/{id}" url.
But if you have a requirement to allow changing from demo:whitePaper to demo:supportingDoc or vice-versa,
then OOTB box action will not work. However, you can create a custom webscript/action to achieve this use case
or extend the OOTB "Document List Component - type submit" webscript to allow handling custom content types.
Here, we will try to extend the "Document List Component - type submit" webscript to achieve our goal.
Follow the below given steps to extend the Change Type webscript as mentioned above:
- Create "type.post.desc.xml" webscript definition file under extension/templates directory in following directory structure:
extension/templates/webscripts/org/alfresco/slingshot/documentlibrary/type.post.desc.xml
- Add following content in the "type.post.desc.xml" file:
<!-- Extended the webScript for custom handling of change-type action --> <webscript> <shortname>type</shortname> <description>Document List Component - type submit</description> <url>/slingshot/doclib/type/node/{store_type}/{store_id}/{id}</url> <format default="json">argument</format> <authentication>user</authentication> <transaction>required</transaction> <lifecycle>internal</lifecycle> </webscript>
- Create a java class, name it e.g.: "ChangeTypeWebscript",
- Add following code in this class:
/* * Created By: Abhinav Kumar Mishra * Copyright © 2020. Abhinav Kumar Mishra. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.abhinavmishra14.webscript; import java.io.IOException; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpStatus; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.extensions.webscripts.AbstractWebScript; import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptResponse; /** * The Class ChangeTypeWebscript.<br> * Extension of OOTB change type WebScript. */ public class ChangeTypeWebscript extends AbstractWebScript { /** The Constant LOGGER. */ private static final Logger LOGGER = LoggerFactory.getLogger(ChangeTypeWebscript.class); /** The Constant TYPE_PARAM_KEY. */ private static final String TYPE_PARAM_KEY = "type"; /** The Constant STORE_TYPE. */ private static final String STORE_TYPE = "store_type"; /** The Constant STORE_ID. */ private static final String STORE_ID = "store_id"; /** The Constant PARAM_ID. */ private static final String PARAM_ID = "id"; /** The Constant CONTENT_LENGTH. */ private static final String CONTENT_LENGTH = "Content-Length"; /** The Constant ENCODING_UTF_8. */ private static final String ENCODING_UTF_8 = "UTF-8"; /** The Constant CURRENT_KEY. */ private static final String CURRENT_KEY = "current"; /** The Constant CACHE_CONTROL. */ private static final String CACHE_CONTROL = "cache-Control"; /** The Constant NO_CACHE. */ private static final String NO_CACHE = "no-cache"; /** The Constant EXPIRES. */ private static final String EXPIRES = "Expires"; /** The Constant PRAGMA. */ private static final String PRAGMA = "Pragma"; /** The namespace service. */ private final transient NamespaceService namespaceService; /** The node service. */ private final transient NodeService nodeService; /** * The Constructor. * * @param namespaceService the namespace service * @param nodeService the node service */ public ChangeTypeWebscript(final NamespaceService namespaceService, final NodeService nodeService) { super(); this.namespaceService = namespaceService; this.nodeService = nodeService; } /* * (non-Javadoc) * * @see org.springframework.extensions.webscripts.WebScript#execute(org. * springframework.extensions.webscripts.WebScriptRequest, * org.springframework.extensions.webscripts.WebScriptResponse) */ @Override public void execute(final WebScriptRequest request, final WebScriptResponse response) throws IOException { QName targetType = null; try { final JSONObject requestPayload = new JSONObject(request.getContent().getContent()); targetType = getTargetType(requestPayload); LOGGER.info("Changing type, the targetType is: {}", targetType); final NodeRef nodeRef = getNodeRef(request); nodeService.setType(nodeRef, targetType); final QName changedType = nodeService.getType(nodeRef); writeResponse(response, changedType.getPrefixedQName(namespaceService)); } catch (JSONException excp) { LOGGER.error("Error occurred while changing the type {}", targetType, excp); throw new WebScriptException("Change type failed!, targetType was: " + targetType, excp); } } /** * Gets the target type. * * @param payload the payload * @return the target type */ private QName getTargetType(final JSONObject payload) { try { final String type = payload.getString(TYPE_PARAM_KEY); QName qname; if (type.indexOf(String.valueOf(QName.NAMESPACE_BEGIN)) != -1) { qname = QName.createQName(type); } else { qname = QName.createQName(type, namespaceService); } return qname; } catch (JSONException jsonex) { throw new WebScriptException(Status.STATUS_INTERNAL_SERVER_ERROR, "Error occurred while extracting the target type from input json payload", jsonex); } } /** * Write response. * * @param response the response * @param changedType the changed type * @throws IOException the IO exception */ private void writeResponse(final WebScriptResponse response, final QName changedType) throws IOException { try { final JSONObject responsePayload = new JSONObject(); responsePayload.put(CURRENT_KEY, changedType.getPrefixString()); writeResponse(response, responsePayload, false, HttpStatus.SC_OK); } catch (JSONException jsonex) { throw new WebScriptException(Status.STATUS_INTERNAL_SERVER_ERROR, "Error occurred while writing json response for changedType: " + changedType, jsonex); } } /** * Gets the node ref. * * @param request the request * @return the node ref */ private NodeRef getNodeRef(final WebScriptRequest request) { final String storeType = getParam(request, STORE_TYPE); final String storeId = getParam(request, STORE_ID); final String identifier = getParam(request, PARAM_ID); return new NodeRef(storeType, storeId, identifier); } /** * Gets the param. * * @param request the request * @param paramName the param name * @return the param */ private String getParam(final WebScriptRequest request, final String paramName) { final String value = StringUtils.trimToNull(request.getServiceMatch().getTemplateVars().get(paramName)); if (StringUtils.isBlank(value)) { throw new WebScriptException(Status.STATUS_BAD_REQUEST, String.format("Value for param '%s' is missing, Please provide a valid input", paramName)); } return value; } /** * Write response. * @param response the response * @param jsonObject the json object * @param clearCache the clear cache * @param responseStatusCode the response status code * @throws IOException Signals that an I/O exception has occurred. */ private void writeResponse(final WebScriptResponse response, final JSONObject jsonObject, final boolean clearCache, final int responseStatusCode) throws IOException { final int length = jsonObject.toString().getBytes(ENCODING_UTF_8).length; response.setContentType(MimetypeMap.MIMETYPE_JSON); response.setContentEncoding(ENCODING_UTF_8); response.addHeader(CONTENT_LENGTH, String.valueOf(length)); if (clearCache) { response.addHeader(CACHE_CONTROL, NO_CACHE); //Calculate the expires date as per you need, i have kept a regular date for example purpose. response.addHeader(EXPIRES, "Thu, 04 Jan 2020 00:00:00 EDT"); response.addHeader(PRAGMA, NO_CACHE); } response.setStatus(responseStatusCode); response.getWriter().write(jsonObject.toString()); } }
- Add the following bean definition in *-context.xml (e.g. webscript-context.xml):
<bean id="webscript.org.alfresco.slingshot.documentlibrary.type.post" class="com.github.abhinavmishra14.webscript.ChangeTypeWebscript" parent="webscript"> <constructor-arg ref="NamespaceService" /> <constructor-arg ref="NodeService" /> </bean>
- To test whether change type is working or not, let's update the share-config-custom.xml file under
<config evaluator="string-compare" condition="DocumentLibrary"> to add type,sub-type config.Add following config:<config evaluator="string-compare" condition="DocumentLibrary"> <aspects> <visible/> <addable/> <removable/> </aspects> <types> <type name="cm:content"> <subtype name="demo:whitePaper"/> <subtype name="demo:supportingDoc"/> </type> <type name="demo:whitePaper"> <subtype name="demo:supportingDoc"/> </type> <type name="demo:supportingDoc"> <subtype name="demo:whitePaper"/> </type> </types> </config>
- Restart the repository and share. Select a file in your document library and click "Change Type" to use extended action.
Find a demo project here: https://github.com/abhinavmishra14/specialize-type-demo-----------------------------------------------------------------------------------------------------------------------------If you prefer to change the type via Repository JavaScript webscript, you can use the below given script as well:Assuming you know the nodeRef of the actionable node for which you want to change the type. You can find more info here
- Create "change-type.post.js" under "Repository> Data Dictionary> Web Scripts Extensions" folder.
function main(){ var nodeRefStr = args["nodeRef"]; //Node ref of the corrupted folder (An assumption for the script) var qNameToChangeType = args["qNameToChangeType"]; //Full qualified QName e.g.: {http://www.alfresco.org/model/content/1.0}folder try { var node=search.findNode(nodeRefStr); logger.log("NodeRef: "+ node.nodeRef+" | Name: "+node.name) var ctxt = Packages.org.springframework.web.context.ContextLoader.getCurrentWebApplicationContext(); var nodeService = ctxt.getBean('NodeService', org.alfresco.service.cmr.repository.NodeService); var QName = Packages.org.alfresco.service.namespace.QName; var nodeTypeFolder = QName.createQName(qNameToChangeType); nodeService.setType(node.nodeRef, nodeTypeFolder); model.result = "Success!"; } catch (ex) { logger.log("Exception occurred: " + ex.message); model.result = ex.message; } } main();- Create "change-type.post.desc.xml" under "Repository> Data Dictionary> Web Scripts Extensions" folder.
<webscript> <shortname>Change Type</shortname> <description>Change type of a node <![CDATA[ Sample request: http://localhost:8080/alfresco/service/change-type?nodeRef=workspace://SpacesStore/e0df9384-9472-472e-95c0-2e091f452700&qNameToChangeType={http://www.alfresco.org/model/content/1.0}folder]]> </description> <url>/change-type?nodeRef={nodeRef}&qNameToChangeType={qNameToChangeType}</url> <authentication>admin</authentication> </webscript>
- Create "change-type.post.html.ftl" under "Repository> Data Dictionary> Web Scripts Extensions" folder.
<html> <head> <title>Change type result</title> </head> <body> <#if result??> ${result} </#if> </body> </html>-----------------------------------------------------------------------------------------------------------------------------If you prefer to change the type via Javascript console and want to change the type of multiple nodes in a given site,
you can use the below given script:var inputNodetype = "cm:content"; //Input content type or folder type. In this example it's cm:content and target is demo:whitePaper content type var siteShortName = "test"; //Site short name, Site name is 'Test', ShortName: 'test' var targetFullQualifiedNodetype = "{http://www.github.com/abhinavmishra14/model/demo/1.0}whitePaper"; //This must be fully qualified var folderPath = ""; //If you want to drill down search to a specific folder path. e.g. 'Assets/images' folder In documentLibrary var additionalQuery = ""; //for example, you can also pass any query like '=@cm\\:name:"*Test" (node name ends with Test)'. //This query is appended to final search query var skipCount = 0; var maxCount = 1000; var query = buildQuery(siteShortName, inputNodetype, folderPath, additionalQuery); var page = { skipCount: parseInt(skipCount), maxItems: parseInt(maxCount) }; var searchQuery = { query: query, language: "fts-alfresco", page: page }; logger.log("Executing SearchQuery: " + query); var foundNodes = search.query(searchQuery); logger.log("Total nodes found: " + foundNodes.length); for each(node in foundNodes) { var nodeName = node.properties.name; var nodeType = node.type; logger.log("NodeName: " + nodeName + " | NodeType: " + nodeType); if(node.typeShort == inputNodetype) { logger.log("Changing type for node: '"+nodeName+"'"); //Get the node service context var ctxt = Packages.org.springframework.web.context.ContextLoader.getCurrentWebApplicationContext(); var nodeService = ctxt.getBean('NodeService', org.alfresco.service.cmr.repository.NodeService); var QName = Packages.org.alfresco.service.namespace.QName; var targetNodeType = QName.createQName(targetFullQualifiedNodetype); nodeService.setType(node.nodeRef, targetNodeType); logger.log("Change type to: '"+targetNodeType+"' has been completed."); } } function buildQuery(siteShortName, inputNodetype, folderPath, additionalQuery) { var query = 'PATH:"/app:company_home/st:sites/cm:' + siteShortName; if (!!folderPath) { //if not null then process query = query + '/cm:documentLibrary/'; var pathTokens = folderPath.split('/'); for (var each = 0; each < pathTokens.length; each++) { query = query + 'cm:' + search.ISO9075Encode(pathTokens[each].trim()) + '/'; } query = query + '/*"'; } else { query = query + '/cm:documentLibrary//*"'; } query = query + ' AND (TYPE:"'+inputNodetype+'")'; //Append additionalQuery query if any if ( !! additionalQuery) { //if not null then append query = query + ' AND ' + additionalQuery; } return query; }Sample Output:DEBUG - Executing SearchQuery: PATH:"/app:company_home/st:sites/cm:test/cm:documentLibrary//*" AND (TYPE:"cm:content") DEBUG - Total nodes found: 17 DEBUG - NodeName: xyz.jpeg | NodeType: {http://www.alfresco.org/model/content/1.0}content DEBUG - Changing type for node: 'xyz.jpeg' DEBUG - Change type to: '{http://www.github.com/abhinavmishra14/model/demo/1.0}whitePaper' has been completed. DEBUG - NodeName: abc.jpg | NodeType: {http://www.alfresco.org/model/content/1.0}content DEBUG - Changing type for node: 'abc.jpg' DEBUG - Change type to: '{http://www.github.com/abhinavmishra14/model/demo/1.0}whitePaper' has been completed. DEBUG - NodeName: download.png | NodeType: {http://www.alfresco.org/model/content/1.0}content DEBUG - Changing type for node: 'download.png' DEBUG - Change type to: '{http://www.github.com/abhinavmishra14/model/demo/1.0}whitePaper' has been completed.
No comments:
Post a Comment
Thanks for your comments/Suggestions.