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",
This class extends "org.springframework.extensions.webscripts.AbstractWebScript"
- 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.
<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.