Source: services/PageService.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/>.
 */

/**
 * @module alfresco/services/PageService
 * @extends module:alfresco/services/BaseService
 * @mixes module:alfresco/core/CoreXhr
 * @mixes module:alfresco/services/_PageServiceTopicMixin
 * @author Dave Draper
 */
define(["dojo/_base/declare",
        "alfresco/services/BaseService",
        "alfresco/core/CoreXhr",
        "alfresco/services/_PageServiceTopicMixin",
        "alfresco/core/NotificationUtils",
        "alfresco/core/ObjectTypeUtils",
        "alfresco/util/objectProcessingUtil",
        "dojo/_base/lang",
        "dojo/_base/array",
        "dojo/dom-construct",
        "service/constants/Default"],
        function(declare, BaseService, CoreXhr, _PageServiceTopicMixin, NotificationUtils, ObjectTypeUtils, objectProcessingUtil, lang, array, domConstruct, AlfConstants) {
   
   return declare([BaseService, CoreXhr, _PageServiceTopicMixin, NotificationUtils], {
      
      /**
       * An array of the i18n files to use with this widget.
       * 
       * @instance
       * @type {object[]}
       * @default [{i18nFile: "./i18n/PageService.properties"}]
       */
      i18nRequirements: [{i18nFile: "./i18n/PageService.properties"}],
      
      /**
       * Sets up the subscriptions for the PageService
       * 
       * @instance
       * @since 1.0.32
       */
      registerSubscriptions: function alfresco_services_PageService__registerSubscriptions() {
         this.alfSubscribe("ALF_AVAILABLE_PAGE_DEFINITIONS", lang.hitch(this, this.loadPages));
         this.alfSubscribe("ALF_GET_ALL_REMOTE_PAGES", lang.hitch(this, this.getPages));
         this.alfSubscribe(this.createPageTopic, lang.hitch(this, this.createPage));
         this.alfSubscribe(this.updatePageTopic, lang.hitch(this, this.updatePage));
         this.alfSubscribe("ALF_EXPORT_PAGE_DEFINITION", lang.hitch(this, this.exportPageModel));
      },
      

      /**
       * Exports the JSON model in a format that can be used in a WebScript.
       *
       * @instance
       * @param {object} payload Details of the page to export
       */
      exportPageModel: function alfresco_services_PageService__exportPageModel(payload) {
         var pageDef = this.getPageDefinitionFromPayload(payload);

         // Stringify the model in a nice format...
         var exportString = "model.jsonModel = " + JSON.stringify(pageDef, null, "   ");

         // Remove the quotes from the attributes (to make more consistent with WebScript controller style)...
         exportString = exportString.replace(/\"([^\"]*)\":/g, "$1:");
         this.generateWsDownload(payload.pageName + ".get.js", exportString);

         // Generate the desc.xml file...
         // TODO: Maybe we should be launching a dialog to collect the additional data, e.g. the URL, etc
         var descXmlString = 
         "<webscript>\n" + 
         "  <shortname></shortname>\n" + 
         "  <description></description>\n" + 
         "  <family></family>\n" + 
         "  <url></url>\n " + 
         "</webscript>";
         this.generateWsDownload(payload.pageName + ".get.desc.xml", descXmlString);

         // Generate the HTML template...
         var htmlString = "<@processJsonModel/>";
         this.generateWsDownload(payload.pageName + ".get.html.ftl", htmlString);
      },

      /**
       * 
       * @instance
       * @param {string} fileName The name of the file to create
       * @param {string} content The file contents to create
       */
      generateWsDownload: function alfresco_services_PageService__generateWsDownload(fileName, content) {
         var downloadLink = domConstruct.create("a", {
            href: "data:text/plain;charset=utf-8," + encodeURIComponent(content),
            download: fileName
         });
         downloadLink.click();
      },

      /**
       * PLEASE NOTE: This only works with the Horizon3-Repo-AMP REST APIs
       *
       * @instance
       * @param {object} payload The payload for the page request
       * @since 1.0.49
       */
      getPages: function alfresco_services_PageService__loadPages(/*jshint unused:false */ payload) {
         this.serviceXhr({
            url: AlfConstants.PROXY_URI + "horizon3/pages",
            method: "GET",
            alfTopic: payload.alfResponseTopic,
            successCallback: this.getPagesSuccess,
            callbackScope: this
         });
      },

      /**
       * Success handler for [getPages]{@link module:alfresco/services/PageService#getPages}.
       *
       * @instance
       * @param {object} response
       * @param {object} originalRequestConfig
       * @since 1.0.49
       */
      getPagesSuccess: function alfresco_services_PageService__getPagesSuccess(response, originalRequestConfig) {
         if (response && response.items && ObjectTypeUtils.isArray(response.items))
         {
            var pageDefs = [];
            array.forEach(response.items, lang.hitch(this, function(item) {
               pageDefs.push(
                  {
                     type: [ "widget"],
                     label: item.name,
                     value: {
                        templateModel: JSON.parse(item.content),
                        isTemplate: true
                     }
                  }
               );
            }));

            response.items = pageDefs;
            var topic = originalRequestConfig.alfTopic + "_SUCCESS";
            this.alfPublish(topic, {
               response: response,
               originalRequestConfig: originalRequestConfig
            });
         }
         else
         {
            this.alfLog("error", "The request to retrieve available page definitions returned a response that could not be interpreted", response, originalRequestConfig, this);
         }
      },

      /**
       * Makes an XHR request to retrieve the pages that are available. The pages returned are those
       * that have been created and stored in the Data Dictionary on the Alfresco repository. 
       *
       * @instance
       * @param {object} The paylod containing additional data. This can contain a "responseTopic" to 
       * publish the options back on.
       */
      loadPages: function alfresco_services_PageService__loadPages(payload) {
         this.serviceXhr({
            url: AlfConstants.PROXY_URI + "remote-share/pages",
            method: "GET",
            responseTopic: payload.responseTopic,
            successCallback: this.loadPagesSuccess,
            failureCallback: this.loadPagesFailure,
            callbackScope: this
         });
      },
      
      /**
       * This processes the results returned from the XHR request to obtain the available pages that have been
       * defined on the repository. This is necessary because the REST API doesn't return the data in an way
       * that fits with the typical use case (e.g. for providing selectable options in form controls). This function
       * has been written in respect of the [onPubSubOptions]{@link module:alfresco/forms/controls/BaseFormControl#onPubSubOptions}
       * function.
       *
       * @instance
       * @param {object} response
       * @param {object} originalRequestConfig
       */
      loadPagesSuccess: function alfresco_services_PageService__loadPagesSuccess(response, originalRequestConfig) {
         if (response && response.items && ObjectTypeUtils.isArray(response.items))
         {
            var topic = originalRequestConfig.responseTopic || this.availablePagesLoadSuccess;
            var pageDefs = [];
            array.forEach(response.items, lang.hitch(this, "processAvailablePageDefResults", pageDefs));

            // NOTE: This is something of an assumption that we want to set the processed "pageDefs" as the "options" attribute
            //       here, but this has been written in respect of retrieving options to be displayed in a drop-down menu (e.g
            //       a form control extending alfresco/forms/controls/BaseFormControl) so it needs to be set in the "options"
            //       attribute although this should arguably be configurable.
            this.alfPublish(topic, {
               options: pageDefs,
               response: response,
               originalRequestConfig: originalRequestConfig
            });
         }
         else
         {
            this.alfLog("error", "The request to retrieve available page definitions returned a response that could not be interpreted", response, originalRequestConfig, this);
         }
         
      },

      /**
       * This updates the supplied array of page defintion with the current page definition. This checks that the supplied
       * definition has both "name" and "nodeRef" attributes - the "name" attribute is converted to a "label" attribute and
       * the "nodeRef" attribute is converted to a "value" attribute in order to make the ultimately returned array be 
       * compatible with options for form controls. If the definition does not have these attributes then it is not added to
       * the array.
       * 
       * @instance
       * @param {object[]} pageDefs The array of page definitions to add the current definition to
       * @param {object} def The current definition to add to the supplied array
       * @param {number} index The index of the page def in the original results set
       */
      processAvailablePageDefResults: function alfresco_services_PageService__processAvailablePageDefResults(pageDefs, def, index) {
         // jshint unused:false
         if (def.name && def.nodeRef)
         {
            var processedDef = {
               label: def.name,
               value: def.nodeRef
            };
            pageDefs.push(processedDef);
         }
         else
         {
            this.alfLog("error", "Missing attributes from page definition", def, this);
         }
      },
      
      /**
       * @instance
       * @param {object} response
       * @param {object} originalRequestConfig
       */
      loadPagesFailure: function alfresco_services_PageService__loadPagesFailure(response, originalRequestConfig) {
         this.availablePagesLoadFailure(this.availablePagesTopic, {
            response: response,
            originalRequestConfig: originalRequestConfig
         });
      },
      
      /**
       * Extracts the page definition from the supplied payload. If the payload contains a "pageDefinition"
       * attribute then it is expected that the value is "stringified" JSON, but if it is supplied as
       * individual "publishOnReady", "services" and "widgets" attributes then they will need to be combined
       * and stringified.
       *
       * @instance
       * @param {object} payload The payload from which to retrieve the page definition.
       */
      getPageDefinitionFromPayload: function alfresco_services_PageService__getPageDefinitionFromPayload(payload) {
         var pageDefinition = {
            publishOnReady: payload.publishOnReady,
            services: payload.services,
            widgets: payload.widgets
         };
         return pageDefinition;
      },

      /**
       * Removes unnecessary attributes from a template before it is passed for previewing.
       * 
       * @instance
       * @param {object} parameters
       * @since 1.0.49
       */
      cleanUpTemplateConfig: function alfresco_services_PageService__cleanUpTemplateConfig(parameters) {
         if (parameters.object === true)
         {
            var parent = parameters.ancestors[parameters.ancestors.length-1];
            objectProcessingUtil.findObject(parent.config, {
               prefix: "isTemplate",
               processFunction: lang.hitch(this, this.cleanUpTemplateConfig),
               config: null
            });

            parent._alfTemplateName = parent.label;
            delete parent.label;
            delete parent.isTemplate;
            delete parent.templateModel;
            delete parent.type;
         }
      },

      /**
       * 
       * @instance
       * @param {object} payload The details of the page to create
       */
      createPage: function alfresco_services_PageService__createPage(payload) {
         if (payload && payload.pageName)
         {
            var data = {
               name: payload.pageName,
               json: this.getPageDefinitionFromPayload(payload)
            };

            // Clean up template data to set "_alfTemplateName" and remove superfluous attributes...
            objectProcessingUtil.findObject(data, {
               prefix: "isTemplate",
               processFunction: lang.hitch(this, this.cleanUpTemplateConfig),
               config: null
            });

            this.serviceXhr({
               url : AlfConstants.PROXY_URI + "remote-share/page-definition",
               data: data,
               method: "POST",
               successCallback: this.pageCreateSuccess,
               failureCallback: this.pageCreateFailure,
               callbackScope: this
            });
         }
         else
         {
            this.alfLog("warn", "A request was made to save a page definition to the repository but not enough information was provided", payload);
         }
      },
      
      /**
       * @instance
       * @param {object} response
       * @param {object} originalRequestConfig
       */
      pageCreateSuccess: function alfresco_services_PageService__pageCreateSuccess(response, originalRequestConfig) {
         this.alfLog("log", "Successfully created page", response, originalRequestConfig);
         this.displayMessage(this.message("page.creation.success", [originalRequestConfig.data.name]));
         this.alfPublish(this.createPageSuccessTopic, {
            response: response,
            originalRequestConfig: originalRequestConfig
         });
      },
      
      /**
       * @instance
       * @param {object} response
       * @param {object} originalRequestConfig
       */
      pageCreateFailure: function alfresco_services_PageService__pageCreateFailure(response, originalRequestConfig) {
         this.alfLog("error", "Failed to create page", response, originalRequestConfig);
         this.displayMessage(this.message("page.creation.failure", [originalRequestConfig.data.name]));
         this.alfPublish(this.createPageFailureTopic, {
            response: response,
            originalRequestConfig: originalRequestConfig
         });
      },
      
      /**
       * 
       * @instance
       * @param {object} payload The details of the page to update
       */
      updatePage: function alfresco_services_PageService__updatePage(payload) {
         if (payload && payload.pageName)
         {
            var data = {
               name: payload.pageName,
               json: this.getPageDefinitionFromPayload(payload)
            };
            this.serviceXhr({
               url : AlfConstants.PROXY_URI + "remote-share/page-definition/" + payload.pageName,
               data: data,
               method: "PUT",
               successCallback: this.pageUpdateSuccess,
               failureCallback: this.pageUpdateFailure,
               callbackScope: this
            });
         }
         else
         {
            this.alfLog("warn", "A request was made to update a page definition to the repository but not enough information was provided", payload);
         }
      },
      
      /**
       * @instance
       * @param {object} response
       * @param {object} originalRequestConfig
       */
      pageUpdateSuccess: function alfresco_prototyping_ScratchPad__pageUpdateSuccess(response, originalRequestConfig) {
         this.alfLog("log", "Successfully updated page", response, originalRequestConfig);
         this.displayMessage(this.message("page.update.success", [originalRequestConfig.data.name]));
         this.alfPublish(this.updatePageSuccessTopic, {
            response: response,
            originalRequestConfig: originalRequestConfig
         });
      },
      
      /**
       * @instance
       * @param {object} response
       * @param {object} originalRequestConfig
       */
      pageUpdateFailure: function alfresco_prototyping_ScratchPad__pageUpdateFailure(response, originalRequestConfig) {
         this.alfLog("error", "Failed to update page", response, originalRequestConfig);
         this.displayMessage(this.message("page.update.failure", [originalRequestConfig.data.name]));
         this.alfPublish(this.updatePageFailureTopic, {
            response: response,
            originalRequestConfig: originalRequestConfig
         });
      }
   });
});