Source: services/DragAndDropModelCreationService.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 is a service that is dedicated to creating JavaScript and NLS properties library files that provide
 * localized models for creating drag-and-drop based modelling pages. It was written with this single use
 * case in mind however it could potentially be used in other contexts.
 * 
 * @module alfresco/services/DragAndDropModelCreationService
 * @extends module:alfresco/services/BaseService
 * @author Dave Draper
 */
define(["dojo/_base/declare",
        "alfresco/services/BaseService",
        "alfresco/core/ObjectTypeUtils",
        "dojo/_base/lang",
        "dojo/_base/array",
        "dojo/dom-construct"],
        function(declare, BaseService, ObjectTypeUtils, lang, array, domConstruct) {
   
   return declare([BaseService], {
      
      /**
       * Sets up the subscriptions for the service
       * 
       * @instance
       * @since 1.0.32
       */
      registerSubscriptions: function alfresco_services_DragAndDropModelCreationService__registerSubscriptions() {
         this.alfSubscribe("ALF_DND_EXPORT_MODEL_LIBRARY_FILES", lang.hitch(this, this.onFormExport));
         this.alfSubscribe("ALF_DND_PREVIEW_FORM_MODELS", lang.hitch(this, this.onFormPreview));
      },

      /**
       * Handles requests to generate previews of the form models.
       *
       * @instance
       * @param {object} payload The model to preview.
       */
      onFormPreview: function alfresco_services_DragAndDropModelCreationService__onFormPreview(payload) {
         if (payload && payload.widgetsForConfig)
         {
            this.alfPublish("MAIN_ALF_GENERATE_PAGE_PREVIEW", {
               publishOnReady: [],
               services: [],
               widgets: payload.widgetsForConfig
            });
         }
         if (payload && payload.widgetsForNestedConfig)
         {
            this.alfPublish("NESTED_ALF_GENERATE_PAGE_PREVIEW", {
               publishOnReady: [],
               services: [],
               widgets: payload.widgetsForNestedConfig
            });
         }
      },
      
      /**
       * 
       * @instance
       * @param {object} payload Details of the form configuration to export
       */
      onFormExport: function alfresco_services_DragAndDropModelCreationService__onFormExport(payload) {
         var prefix = payload.nlsPrefix || "dummy.prefix";
         var nlsProps = [];

         // We want to clone the payload here because as we process the NLS properties we are going to substitute
         // the generated NLS keys in place of the values that have been entered, however we don't want the modeller
         // UI to immediately reflect that change in case they want to edit and export again...
         var clonedPayload = lang.clone(payload);
         if (clonedPayload.widgetsForConfig)
         {
            this.processNlsData(nlsProps, prefix, clonedPayload.widgetsForConfig);
         }
         if (clonedPayload.widgetsForNestedConfig)
         {
            this.processNlsData(nlsProps, prefix, clonedPayload.widgetsForNestedConfig);
         }

         // Output the NLS properties...
         var nlsContent = "";
         array.forEach(nlsProps, function(nlsProp) {
            nlsContent += nlsProp.key + "=" + nlsProp.value + "\n";
         }); 
         this.generateDownload(payload.modelName + ".lib.properties", nlsContent);

         // Output the actual model file...
         var configSuffix = "Config",
             nestedConfigSuffix = "NestedConfig",
             displaySuffix = "Display";
         
         var libContent = "";
         libContent += this.createFunctionContent(payload.modelName, clonedPayload.widgetsForConfig, configSuffix);
         libContent += this.createFunctionContent(payload.modelName, clonedPayload.widgetsForNestedConfig, nestedConfigSuffix);
         libContent += this.createFunctionContent(payload.modelName, clonedPayload.widgetsForDisplay, displaySuffix);

         var targetValues = JSON.stringify(payload.targetValues);
         libContent += this.createModelFunction(payload.modelName, 
                                                payload.property, 
                                                targetValues,
                                                configSuffix,
                                                nestedConfigSuffix,
                                                displaySuffix);

         this.generateDownload(payload.modelName + ".lib.js", libContent);

         // NOTE: This publication is largely included for test purposes...
         this.alfPublish("ALF_DND_MODEL_LIBRARIES", {
            nls: nlsContent,
            js: libContent
         });
      },

      /**
       * Creates a new function declaration using the supplied arguments.
       *
       * @instance
       * @param {string} name The name to to use for the function prefix
       * @param {object} model The model to process
       * @param {string} suffix The function suffix
       * @return {string} A function declaration as a string
       */
      createFunctionContent: function alfresco_services_DragAndDropModelCreationService__createFunctionContent(name, model, suffix) {
         // This is a replacement function for ensuring that unrequired attributes are filtered out
         // of the output model...
         var replacer = function(key, value) {
            switch (key) {
               case "selectOptionType":
               case "showDynamicBehaviourConfig":
                  return undefined;
               default:
                  return value;
            }
         };

         var content = "function get" + name + suffix + "() {\n" +
            "   return " + JSON.stringify(model, replacer, "   ") + ";\n" + 
            "}\n\n";

         // Convert the JSON output to a JavaScript object literal style...
         var re = /"([^"]*)":/g;
         content = content.replace(re, "$1:");

         // Make sure that name is first...
         // NOTE: This is probably going to be quite brittle
         // CURRENTLY COMMENTED OUT BECAUSE NOT WORKING FOR NESTED ITEMS
         // var re2 = /(config: {(.|\n)*?}),\n(.*)(\s)(name:\s"([^"]*)")/g;
         // content = content.replace(re2, "$5,\n$3 $1"); 
         return content;
      },

      /**
       * Creates the JavaScript function string that can be called to generate a single model. This
       * is what would be called by the WebScript JavaScript controller that imports the library file.
       *
       * @instance
       * @param  {string} name The model name
       * @param  {string} property The property of a dropped item that should be compared against the target values
       * @param  {string} targetValues A JSON stringified array of target values to match against the property
       * @param  {string} configWidgetsSuffix The suffix used for the function that gets the widgetsForConfig
       * @param  {string} nestedWidgetsSuffix The suffix used for the function that gets the widgetsForNestedConfig
       * @param  {string} displayWidgetsSuffix The suffix used for the function that gets the widgetsForDisplay
       * @return {string} The function definition
       */
      createModelFunction: function alfresco_services_DragAndDropModelCreationService__createModelFunction(name, property, targetValues, configWidgetsSuffix, nestedWidgetsSuffix, displayWidgetsSuffix) {
         var content = "function getDefault" + name + "Model() {\n" + 
            "   return {\n" + 
            "      property: \"" + property + "\",\n" + 
            "      targetValues: " + targetValues + ",\n" + 
            "      widgetsForConfig: addCommonConfigTabs(get" + name + configWidgetsSuffix + "(),[]),\n" +
            "      widgetsForNestedConfig: get" + name + nestedWidgetsSuffix + "(),\n" + 
            "      widgetsForDisplay: get" + name + displayWidgetsSuffix + "()\n" + 
            "   };\n" + 
            "}\n";
         return content;
      },
      
      /**
       * Processes the supplied model to generate an NLS lib file. The generated NLS keys are then
       * swapped into the model in place of the actual values.
       *
       * @instance
       * @param {array} nlsProps An array to update with the generated NLS properties
       * @param {string} prefix The prefix for each NLS key.
       * @param {object} o The current object in the model being processed
       */
      processNlsData: function alfresco_services_DragAndDropModelCreationService__processNlsData(nlsProps, prefix, o) {
         // jshint maxcomplexity:false
         for (var key in o)
         {
            if (o.hasOwnProperty(key)) {
               
               // Get the value of the object...
               var v = o[key];
               if (ObjectTypeUtils.isString(v))
               {
                  var identifier = o.name || o.fieldId || o.id || o.value;
                  if (identifier)
                  {
                     // If the object is a string, check the key and if it matches an NLS key then convert it
                     // into an NLS property object and push it into the supplied array...
                     switch (key) {
                        case "title":
                        case "label":
                        case "description":
                        case "unitsLabel":
                           var nlsKey = prefix + "." + identifier + "." + key;
                           var nlsProp = {
                              key: nlsKey,
                              value: v
                           };
                           nlsProps.push(nlsProp);

                           // Replace the value with the key...
                           o[key] = nlsKey;
                           break;
                     }
                  }
                  else
                  {
                     this.alfLog("warn", "No sensible identifier to use for current NLS prop", key);
                  }
               }
               else if (ObjectTypeUtils.isArray(v))
               {
                  array.forEach(v, lang.hitch(this, this.processNlsData, nlsProps, prefix));
               }
               else if (ObjectTypeUtils.isObject(v))
               {
                  this.processNlsData(nlsProps, prefix, v);
               }
            }
         }
      },

      /**
       * This is only currently supported by Chrome.
       * 
       * @instance
       * @param {string} fileName The name of the file to create
       * @param {string} content The file contents to create
       */
      generateDownload: function alfresco_services_DragAndDropModelCreationService__generateDownload(fileName, content) {
         var downloadLink = domConstruct.create("a", {
            href: "data:text/plain;charset=utf-8," + encodeURIComponent(content),
            download: fileName
         });
         downloadLink.click();
      }
   });
});