Source: forms/controls/Picker.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/>.
 */

/**
 * <p>This is the root widget that should be extended for all "picker" controls. By default it will effectively
 * render a [Document Picker]{@link module:alfresco/forms/controls/DocumentPicker} (as this was the original
 * picker module that the code was abstracted from) but extending pickers (such as the 
 * [Container Picker]{@link module:alfresco/forms/controls/ContainerPicker} and the [Property Picker]{@link module:alfresco/forms/controls/PropertyPicker})
 * can change the display of picked items and the behaviour of the picker itself by overriding the 
 * [configForPickedItems]{@link module:alfresco/forms/controls/Picker#configForPickedItems} and
 * [configForPicker]{@link module:alfresco/forms/controls/Picker#configForPicker} respectively.
 * Pickers are designed to be used in forms to allow the user to select multiple "complex items".</p>
 * 
 * @module alfresco/forms/controls/Picker
 * @extends module:alfresco/forms/controls/BaseFormControl
 * @mixes module:alfresco/core/CoreWidgetProcessing
 * @author Dave Draper
 */
define(["alfresco/forms/controls/BaseFormControl",
        "alfresco/core/CoreWidgetProcessing",
        "dojo/_base/declare",
        "dojo/_base/lang",
        "dojo/_base/array",
        "dojo/when"], 
        function(BaseFormControl, CoreWidgetProcessing, declare, lang, array, when) {
   
   return declare([BaseFormControl, CoreWidgetProcessing], {
      
      /**
       * An array of the i18n files to use with this widget.
       * 
       * @instance
       * @type {Array}
       */
      i18nRequirements: [{i18nFile: "./i18n/Picker.properties"}],
      
      /**
       * The value to use as a key for each item. Each picked item should have an attribute with the name defined.
       *
       * @instance
       * @type {string}
       * @default
       */
      itemKey: "name",

      /**
       * Whether to use the currentItem as the initial value of the picker
       *
       * @instance
       * @type {boolean}
       * @default
       */
      useCurrentItemAsValue: false,

      /**
       * Run after widget and all children created
       *
       * @instance
       * @override
       */
      startup: function alfresco_forms_controls_Picker__startup() {
         this.inherited(arguments);
         if (this.useCurrentItemAsValue && this.currentItem) {
            var newValue = this.currentItem instanceof Array ? this.currentItem : [this.currentItem];
            this.setValue(newValue);
         }
      },

      /**
       * @instance
       */
      getWidgetConfig: function alfresco_forms_controls_Picker__getWidgetConfig() {
         // Return the configuration for the widget
         return {
            id : this.id + "_CONTROL",
            name: this.name
         };
      },
      
      /**
       * Overrides the [inherited function]{@link module:alfresco/forms/controls/BaseFormControl#createFormControl}
       * to create the picked items display and the picker itself. This should not need to be overridden by extending
       * pickers.
       * 
       * @instance
       * @param {object} config The configuration object for instantiating the picker form control
       */
      createFormControl: function alfresco_forms_controls_Picker__createFormControl(config) {

         this.itemSelectionPubSubScope = this.generateUuid();
         this.alfSubscribe(this.itemSelectionPubSubScope + "ALF_ITEMS_SELECTED", lang.hitch(this, "onItemsSelected"), true);
         
         // Update the model to set the main picked items display and the overall picker config
         var clonedWidgetsForControl = lang.clone(this.widgetsForControl);
         this.processObject(["processInstanceTokens"], clonedWidgetsForControl);
         if (this.configForPickedItems)
         {
            this.setModelPickedItemsConfig(lang.clone(this.configForPickedItems), config.value, clonedWidgetsForControl);
         }
         if (this.configForPicker)
         {
            this.setModelPickerConfig(lang.clone(this.configForPicker), clonedWidgetsForControl);
         }
         
         // Set the value...
         this.setModelPickerWidgetValue(config.value, clonedWidgetsForControl);
         return this.processWidgets(clonedWidgetsForControl, this._controlNode);
      },

      /**
       * Updates the model to set a value of the currently selected items. It is necessary to do this because
       * the model cannot contain variable information (at least none that can be defined when the widget is
       * instantiated) so it is necessary to perform this before the [processWidgets]{@link module:alfresco/core/Core#processWidgets}
       * function is called.
       *
       * @instance
       * @param {object} config The configuration to use
       * @param {array} value The value to set
       * @param {object} widgetsForControl A cloned copy of the defined widgets for the picked items
       */
      setModelPickedItemsConfig: function alfresco_forms_controls_Picker__setModelPickedItemsConfig(config, value, widgetsForControl) {
         config.pubSubScope = this.itemSelectionPubSubScope;
         lang.setObject("0.config.widgets.0.config", config, widgetsForControl);
         lang.setObject("0.config.widgets.0.config.value", value, widgetsForControl);
      },

      /**
       * Updates the model to set a value of the currently selected items. It is necessary to do this because
       * the model cannot contain variable information (at least none that can be defined when the widget is
       * instantiated) so it is necessary to perform this before the [processWidgets]{@link module:alfresco/core/Core#processWidgets}
       * function is called.
       *
       * @instance
       * @param {object} value The value to set
       */
      setModelPickerConfig: function alfresco_forms_controls_Picker__setModelPickerConfig(value, widgetsForControl) {
         lang.setObject("0.config.widgets.1.config.publishPayload.widgetsContent.0.config", value, widgetsForControl);
      },

      /**
       * The value returned is an array where each element is the value of the [itemKey]{@link module:alfresco/forms/controls/Picker#itemKey}
       * of each item returned by calling the [getPickedItemsWidget function]{@link module:alfresco/forms/controls/Picker#getPickedItemsWidget}. 
       *
       * @instance
       * @returns {object}
       */
      getValue: function alfresco_forms_controls_Picker__getValue() {
         var processedItems = [];
         var pickedItemsWidget = this.getPickedItemsWidget();
         if (pickedItemsWidget)
         {
             var items = pickedItemsWidget.currentData.items;
             array.forEach(items, function(item) {
                processedItems.push(item[this.itemKey]);
             }, this);
         }
         return processedItems;
      },
      
      /**
       * Calls the [setModelPickerWidgetValue]{@link module:alfresco/forms/controls/DocumentPicker#setModelPickerWidgetValue}
       * function.
       *
       * @instance
       * @param {object} value
       */
      setValue: function alfresco_forms_controls_Picker__setValue(value) {
         var pickedItemsWidget = this.getPickedItemsWidget();
         pickedItemsWidget && pickedItemsWidget.setPickedItems(value);
      },

      /**
       * Updates the model to set a value of the currently selected items. It is necessary to do this because
       * the model cannot contain variable information (at least none that can be defined when the widget is
       * instantiated) so it is necessary to perform this before the [processWidgets]{@link module:alfresco/core/Core#processWidgets}
       * function is called.
       *
       * @instance
       * @param {object} value The value to set
       */
      setModelPickerWidgetValue: function alfresco_forms_controls_Picker__setModelPickerWidgetValue(value, widgetsForControl) {
         lang.setObject("0.config.widgets.1.config.widgets.0.config.value", value, widgetsForControl);
      },

      /**
       * When items are picked it is necessary to update the [AlfFormDialogButton]{@link module:alfresco/buttons/AlfFormDialogButton} 
       * that generates the [form]{@link module:alfresco/forms/Form} containing the [Picker]{@link module:alfresco/pickers/Picker} because
       * the [DialogService]{@link module:alfresco/services/DialogService} will destroy the dialog when it is closed. 
       *
       * @instance
       * @param {object} value The items to set.
       */
      updateFormDialogButton: function alfresco_forms_controls_Picker__updateFormDialogButton(value) {
         var button = lang.getObject("verticalWidgets.formDialogButton", false, this);
         if (button)
         {
            lang.setObject("publishPayload.widgetsContent.0.config.value", value, button);
         }
         else
         {
            this.alfLog("warn", "The button to request a form does not exist", this);
         }
      },

      /**
       * This is the dot-notation property address where to find the picked items from the dialog that was displayed.
       * It can be overridden as necessary.
       *
       * @instance
       * @type {string}
       * @default
       */
      dialogPickedItemsProperty: "pickedItemsWidget.currentData.items",

      /**
       * This is called when the [form]{@link module:alfresco/forms/Form} generated by the [AlfFormDialogButton]{@link module:alfresco/buttons/AlfFormDialogButton}
       * is submitted. The payload will contain the picked items captured by the [Picker]{@link module:alfresco/pickers/Picker}
       * widget. The items should then be rendered in the local [PickedItems]{@link module:alfresco/pickers/PickedItems} widget.
       *
       * @instance
       * @param {object} payload The details of the selected items
       */
      onItemsSelected: function alfresco_forms_controls_Picker__onItemsSelected(payload) {
         var pickedItemsWidget = this.getPickedItemsWidget();
         if (pickedItemsWidget && payload)
         {
            // Check if a "pickedItems" attribute was provided in the payload. This would be provided
            // when using the remove or remove all buttons in the main picker...
            var pickedItems = payload.pickedItems;
            if (!pickedItems)
            {
               // ...if a "pickedItems" attribute was not provided then attempt to get the picked items
               // directly from the dialog.
               
               if (payload.dialogContent)
               {
                  when(payload.dialogContent, lang.hitch(this, function(content) {
                     if (content && content.length)
                     {
                        pickedItems = lang.getObject(this.dialogPickedItemsProperty, false, content[0]);
                        this.processPickedItems(pickedItems);
                     }
                  }));
               }
            }
            this.processPickedItems(pickedItems);
         }
         else
         {
            this.alfLog("warn", "PickedItems widget is not created, it is not possible to set items", this);
         }
      },

      /**
       * Called from the [onItemsSelected]{@link module:alfresco/forms/controls/Picker#onItemsSelected} when
       * items have been picked. This will update the current view of the picked items widget and validate
       * the form control.
       * 
       * @instance
       * @param  {object[]} pickedItems The picked items to process
       * @since 1.0.36
       */
      processPickedItems: function alfresco_forms_controls_Picker__processPickedItems(pickedItems) {
         if (pickedItems)
         {
            var pickedItemsWidget = this.getPickedItemsWidget();
            pickedItemsWidget.currentData.items = pickedItems;
            pickedItemsWidget.renderView(false);
            this.updateFormDialogButton(pickedItems);
         }
         else
         {
            this.alfLog("warn", "No items provided to assign to PickedItems widget, nothing will be set", this);
         }
         this.validate();
      },

      /**
       * Retrieves the [PickedItems widget]{@link module:alfresco/pickers/PickedItems} defined in the 
       * [widgetsForControl]{@link module:alfresco/forms/controls/DocumentPicker#widgetsForControl} model.
       * This is defined in a separate function to support functions that override the model and need to
       * obtain the widget by a different means.
       *
       * @instance
       * @returns {object} The [PickedItems widget]{@link module:alfresco/pickers/PickedItems}
       */
      getPickedItemsWidget: function alfresco_forms_controls_Picker__getPickedItemsWidget() {
         var widget = lang.getObject("verticalWidgets.pickedItemsWidget", false, this);
         return widget;
      },

      /**
       * This should be overridden to define the widget model for rendering the picked items.
       *
       * @instance
       * @type {object}
       * @default []
       */
      configForPickedItems: null,

      /**
       * This should be overridden to define the widget model for rendering the picker that appears within the 
       * dialog.
       *
       * @instance
       * @type {object}
       * @default []
       */
      configForPicker: null,

      /**
       * This is the widget to use as the root picker.
       *
       * @instance
       * @type {string}
       * @default
       */
      pickerWidget: "alfresco/pickers/Picker",

      /**
       * 
       * @instance
       * @type {object}
       * @default
       */
      widgetsForControl: [
         {
            name: "alfresco/layout/VerticalWidgets",
            assignTo: "verticalWidgets",
            config: {
               widgets: [
                  {
                     name: "alfresco/pickers/PickedItems",
                     assignTo: "pickedItemsWidget",
                     config: {
                        pubSubScope: "{itemSelectionPubSubScope}"
                     }
                  },
                  {
                     name: "alfresco/buttons/AlfButton",
                     assignTo: "formDialogButton",
                     config: {
                        label: "picker.add.label",
                        publishTopic: "ALF_CREATE_DIALOG_REQUEST",
                        publishPayload: {
                           dialogTitle: "picker.select.title",
                           handleOverflow: false,
                           widgetsContent: [
                              {
                                 name: "{pickerWidget}",
                                 config: {}
                              }
                           ],
                           widgetsButtons: [
                              {
                                 name: "alfresco/buttons/AlfButton",
                                 config: {
                                    label: "picker.ok.label",
                                    publishTopic: "ALF_ITEMS_SELECTED",
                                    pubSubScope: "{itemSelectionPubSubScope}",
                                    additionalCssClasses: "call-to-action"
                                 }
                              },
                              {
                                 name: "alfresco/buttons/AlfButton",
                                 config: {
                                    label: "picker.cancel.label",
                                    publishTopic: "NO_OP"
                                 }
                              }
                           ]
                        },
                        publishGlobal: true
                     }
                  },
                  {
                     name: "alfresco/buttons/AlfButton",
                     config: {
                        label: "picker.removeAll.label",
                        additionalCssClasses: "cancelButton",
                        publishTopic: "ALF_ITEMS_SELECTED",
                        publishPayload: {
                           pickedItems: []
                        },
                        pubSubScope: "{itemSelectionPubSubScope}"
                     }
                  }
               ]
            }
         }
      ]
   });
});