Source: services/actions/ChangeTypeService.js

/**
 * Copyright (C) 2005-2016 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 <http://www.gnu.org/licenses/>.
 */

/**
 * This service handles requests to change the type of a particular node. The types that are available
 * are expected to be provided as [a configuration attribute]{@link module:alfresco/services/actions/ChangeTypeService#types} 
 * when instantiating this service. When used on Alfresco Share the types can be found in the XML configuration 
 * files.
 *
 * @example <caption>This is an example configuration:</caption>
 * {
 *   name: "alfresco/services/actions/ChangeTypeService",
 *   config: {
 *     types: [
 *       {
 *         name: "cm:folder"
 *       },
 *       {
 *         name: "cm:content",
 *         label: "Content",
 *         subTypes: [
 *           {
 *             name: "cm:sub-content",
 *             label: "Sub-Content",
 *               subTypes: [
 *                 {
 *                   name: "cm:super-sub-content"
 *                 }
 *               ]
 *            }
 *         ]
 *       }
 *     ]
 *   }
 * }
 *
 * @module alfresco/services/actions/ChangeTypeService
 * @extends module:alfresco/services/BaseService
 * @mixes module:alfresco/core/CoreXhr
 * @author Dave Draper
 * @since 1.0.58
 */
define(["dojo/_base/declare",
        "alfresco/services/BaseService",
        "alfresco/core/CoreXhr",
        "alfresco/core/topics",
        "service/constants/Default",
        "dojo/_base/lang",
        "dojo/_base/array",
        "alfresco/forms/controls/Select"],
        function(declare, BaseService, AlfCoreXhr, topics, AlfConstants, lang, array) {

   return declare([BaseService, AlfCoreXhr], {

      /**
       * An array of the i18n files to use with this widget.
       *
       * @instance
       * @type {object[]}
       * @default [{i18nFile: "./i18n/ChangeTypeService.properties"}]
       */
      i18nRequirements: [{i18nFile: "./i18n/ChangeTypeService.properties"}],

      /**
       * This is the array of available types that are available. 
       *
       * @instance
       * @type {array}
       * @default
       */
      types: null,

      /**
       * Registers change type subscriptions.
       *
       * @instance
       * @listens module:alfresco/core/topics#CHANGE_TYPE_REQUEST
       */
      registerSubscriptions: function alfresco_services_actions_ChangeTypeService__registerSubscriptions() {
         this.alfSubscribe(topics.CHANGE_TYPE_REQUEST, lang.hitch(this, this.onChangeTypeRequest));
      },

      /**
       * Handles requests to change the type of a node. This will perform an XHR request if necessary to load the full
       * metadata for the node to be upated in order to retrieve its current type.
       * 
       * @instance
       * @param {object} item The item to perform the action on
       * @fires module:alfresco/core/topics#GET_DOCUMENT
       */
      onChangeTypeRequest: function alfresco_services_actions_ChangeTypeService__onChangeTypeRequest(payload) {
         if (payload && payload.node && payload.node.type)
         {
            // We have all the information to check what types to list...
            this.showTypeSelectionDialog(payload);
         }
         else if ((payload.node && payload.node.nodeRef) || payload.nodeRef)
         {
            // We have a NodeRef so we can request the full node data...
            var nodeRef = payload.node.nodeRef || payload.nodeRef;
            var responseTopic = this.generateUuid();
            var subscriptionHandles = [];
            subscriptionHandles.push(this.alfSubscribe(responseTopic + "_SUCCESS", lang.hitch(this, this.onNodeDataSuccess), true));
            subscriptionHandles.push(this.alfSubscribe(responseTopic + "_FAILURE", lang.hitch(this, this.onNodeDataFailure), true));
            this.alfPublish(topics.GET_DOCUMENT, {
               subscriptionHandle: subscriptionHandles,
               alfResponseTopic: responseTopic,
               nodeRef: nodeRef
            }, true);
         }
         else
         {
            this.alfLog("warning", "A request was made to manage aspects, but no 'node' was provided in the 'payload' object", payload, this);
         }
      },

      /**
       * Handles requests to retrieve the full metadata for a Node.
       * 
       * @instance
       */
      onNodeDataSuccess: function alfresco_services_actions_ChangeTypeService__onNodeDataSuccess(payload) {
         if (payload.subscriptionHandles)
         {
            this.alfUnsubscribeSaveHandles([payload.subscriptionHandles]);
         }
         if (lang.exists("response.item", payload)) 
         {
            this.showTypeSelectionDialog(payload.response.item);
         }
         else
         {
            this.alfLog("warn", "Node data was provided but the 'response.item' attribute was not found", payload, this);
         }
      },

      /**
       * Handles requests to retrieve the full metadata for a Node.
       * 
       * @instance
       */
      onNodeDataFailure: function alfresco_services_actions_ChangeTypeService__onNodeDataFailure(payload) {
         this.alfLog("error", "It was not possible to retrieve the metadata for the requested Node", payload);
      },

      /**
       * Called from [updateApplicableTypes]{@link module:alfresco/services/actions/ChangeTypeService#updateApplicableTypes}
       * to add all applicable sub-types of the current node type.
       *
       * @instance
       * @param {object[]} applicableTypes An array to be populates with type options.
       * @param {string}   type The type of the node to be updated
       * @param {object[]} types An array of types to process.
       */
      addSubTypes: function alfresco_services_actions_ChangeTypeService__addSubTypes(applicableTypes, subType) {
         if (subType.name)
         {
            var label = subType.name;
            if (subType.label)
            {
               label = subType.label + " (" + subType.name + ")";
            }
            applicableTypes.push({
               value: subType.name,
               label: label
            });

            // Add all nested sub-types as well (as these are further specializations of the current node type)...
            array.forEach(subType.subTypes, lang.hitch(this, this.addSubTypes, applicableTypes));
         }
         else
         {
            this.alfLog("No 'name' attribute provided for sub-type", subType, this);
         }
      },

      /**
       * Updates the supplied array with applicable sub-types for the supplied type.
       *
       * @instance
       * @param {object[]} applicableTypes An array to be populates with type options.
       * @param {string}   type The type of the node to be updated
       * @param {object[]} types An array of types to process.
       */
      updateApplicableTypes: function alfresco_services_actions_ChangeTypeService__updateApplicableTypes(applicableTypes, currNodeType, types) {
         array.forEach(types, function(type) {
            if (type.name === currNodeType)
            {
               array.forEach(type.subTypes, lang.hitch(this, this.addSubTypes, applicableTypes));
            }
            else
            {
               array.forEach(type.subTypes, lang.hitch(this, this.getAvailableTypes, applicableTypes, currNodeType));
            }
         }, this);
      },

      /**
       * Shows the type selection dialog. The available types for selection are generated by calling 
       * [updateApplicableTypes]{@link module:alfresco/services/actions/ChangeTypeService#updateApplicableTypes}
       * with the type of the current node and the 
       * [configured types]{@link module:alfresco/services/actions/ChangeTypeService#types}.
       * 
       * @instance
       * @param {object} item The node to change the type of
       * @fires module:alfresco/core/topics#CREATE_FORM_DIALOG
       */
      showTypeSelectionDialog: function alfresco_services_actions_ChangeTypeService__showTypeSelectionDialog(item) {
         var responseTopic = this.generateUuid();
         var subscriptionHandle = this.alfSubscribe(responseTopic, lang.hitch(this, this.onTypeSelection), true);
         
         // Get the appropriate title for the dialog...
         var dialogTitle = this.message("change-type.dialog.title", {
            0: item.displayName
         });

         // Work out the applicable types for the node to be updated...
         var applicableTypes = [];
         this.updateApplicableTypes(applicableTypes, item.node.type, this.types);

         // Request a new dialog for selecting a new type...
         this.alfPublish(topics.CREATE_FORM_DIALOG, {
            dialogId: "ALF_CHANGE_TYPE_DIALOG",
            dialogTitle: dialogTitle,
            formSubmissionTopic: responseTopic,
            formSubmissionPayloadMixin: {
               item: item,
               subscriptionHandle: subscriptionHandle
            },
            widgets: [
               {
                  id: "CHANGE_TYPE_DIALOG_SELECT",
                  name: "alfresco/forms/controls/Select",
                  config: {
                     name: "type",
                     label: "change-type.select.label",
                     description: "change-type.select.desription",
                     requirementConfig: {
                        initialValue: true
                     },
                     optionsConfig: {
                        fixed: applicableTypes
                     }
                  }
               }
            ]
         });
      },

      /**
       * This function is called when a new type is selected for the node and it makes an XHR request to 
       * the Alfresco Repository to attempt to perform the requested change.
       * 
       * @instance
       * @param {object} The payload for generated when selecting a new type.
       */
      onTypeSelection: function aalfresco_services_actions_ChangeTypeService__onTypeSelection(payload) {
         // Clean up any subscription handles
         if (payload && payload.subscriptionHandle)
         {
            this.alfUnsubscribe(payload.subscriptionHandle);
            delete payload.subscriptionHandle;
         }

         // Post the update...
         var processedNodeRef = payload.item.node.nodeRef.replace("://", "/");
         this.serviceXhr({url: AlfConstants.PROXY_URI + "slingshot/doclib/type/node/" + processedNodeRef,
                          method: "POST",
                          item: payload.item,
                          data: {
                             type: payload.type
                          },
                          successCallback: this.onUpdateSuccess,
                          failureCallback: this.onUpdateFailure,
                          callbackScope: this});
      },

      /**
       * Handles successful requests to change a node type.
       *
       * @instance
       * @param {object} response The response object from the XHR request
       * @param  {object} originalRequestConfig The object passed when making the original XHR request
       * @fires module:alfresco/core/topics#DISPLAY_NOTIFICATION
       */
      onUpdateSuccess: function  alfresco_services_actions_ChangeTypeService__onUpdateSuccess(response, originalRequestConfig) {
         this.alfServicePublish(topics.DISPLAY_NOTIFICATION, {
            message: this.message("change-type.success", {
               "0": originalRequestConfig.item.displayName
            })
         });
      },

      /**
       * Handles failed requests to change a node type.
       *
       * @instance
       * @param {object} response The response object from the XHR request
       * @param  {object} originalRequestConfig The object passed when making the original XHR request
       * @fires module:alfresco/core/topics#DISPLAY_PROMPT
       */
      onUpdateFailure: function  alfresco_services_actions_ChangeTypeService__onUpdateFailure(response, originalRequestConfig) {
         this.alfPublish(topics.DISPLAY_PROMPT, {
            message: this.message("change-type.failure", {
               "0": originalRequestConfig.item.displayName
            })
         });
      }
   });
});