Source: navigation/TreeStore.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/navigation/TreeStore
 * @extends external:dojo/store/JsonRest
 * @mixes module:alfresco/core/CoreXhr
 * @author Dave Draper
 */
define(["dojo/_base/declare",
        "dojo/store/JsonRest", 
        "alfresco/core/CoreXhr",
        "dojo/Deferred",
        "dojo/_base/lang",
        "dojo/_base/array"], 
        function(declare, JsonRest, CoreXhr, Deferred, lang, array) {
   
   return declare([JsonRest, CoreXhr], {
      
      /**
       * This is an array of Regular Expressions that should match pathes to show. At least one of the 
       * Regular Expressions in the array needs to pass true for the tree node to be displayed. To show
       * all paths this should be left as null (the default value).
       *
       * @instance
       * @type {array}
       * @default
       */
      filterPaths: null,

      /**
       * This is a topic that can be published to request child data. This an alternative to the 
       * [target]{@link module:alfresco/navigation/TreeStore#target} URL prefix that can also be configured.
       *
       * @instance
       * @type {string}
       * @default
       * @since 1.0.39
       * @event
       */
      publishTopic: null,

      /**
       * This is the payload that will be published to request child data when a
       * [publishTopic]{@link module:alfresco/navigation/TreeStore#publishTopic} has been configured.
       * 
       * @instance
       * @type {object}
       * @default
       * @since 1.0.39
       */
      publishPayload: null,

      /**
       * Indicates whether or not requests to get child data will be published globally using the 
       * [publishTopic]{@link module:alfresco/navigation/TreeStore#publishTopic} that has been configured.
       * 
       * @instance
       * @type {boolean}
       * @default
       * @since 1.0.39
       */
      publishGlobal: true,

      /**
       * The URL prefix to use for requesting child data. This will only be used if a 
       * [publishTopic]{@link module:alfresco/navigation/TreeStore#publishTopic} has not been configured.
       *
       * @instance
       * @type {string}
       * @default
       */
      target: null,

      /**
       * Makes an asynchronous request to retrieve the children of the supplied parent object. It immediately returns
       * a Deferred object that is also passed as part of the request configuration. The Deferred object is resolved
       * by either the success or failure callback handler depending upon the result of the request.
       * 
       * @instance
       * @param {object} object The parent to retrieve the children for
       * @param {object} options The options for the query (these are currently ignored)
       */
      getChildren: function alfresco_navigation_TreeStore__getChildren(object, /*jshint unused:false*/ options) {
         var deferred = new Deferred();
         if (this.publishTopic)
         {
            var responseTopic = this.generateUuid();
            var subscriptionHandles = [];

            if (!this.publishPayload)
            {
               this.publishPayload = {};
            }
            this.publishPayload.alfResponseTopic = responseTopic;
            this.publishPayload.path = object.path;

            subscriptionHandles.push(this.alfSubscribe(responseTopic + "_SUCCESS", lang.hitch(this, this.onChildren, deferred, parent, subscriptionHandles)));
            subscriptionHandles.push(this.alfSubscribe(responseTopic, lang.hitch(this, this.onChildren, deferred, object, subscriptionHandles)));
            this.alfPublish(this.publishTopic, this.publishPayload, this.publishGlobal);
         }
         else
         {
            var config = {
               url: this.target + object.path,
               query: this.targetQueryObject,
               method: "GET",
               deferred: deferred,
               parent: object,
               successCallback: this.onChildRequestSuccess,
               failureCallback: this.onChildRequestFailure,
               callbackScope: this
            };
            this.serviceXhr(config);
         }
         return deferred;
      },
      
      /**
       * Handles child data provided when a [publishTopic]{@link module:alfresco/navigation/TreeStore#publishTopic} 
       * has been configured to request child data.
       * 
       * @instance
       * @param {object} deferred The deferred object to resolve with the child data
       * @param {object} parent The parent node in the tree
       * @param {object[]} subscriptionHandles Subscription handles created for the request that need to be removed
       * @param {object} payload The payload containing the child data.
       * @since 1.0.39
       */
      onChildren: function alfresco_navigation_TreeStore__onChildren(deferred, parent, subscriptionHandles, payload) {
         this.alfUnsubscribeSaveHandles(subscriptionHandles);
         array.forEach(payload.response.items, lang.hitch(this, this.updateChild, payload.requestConfig.data));
         
         // If required, filter the items based on their paths...
         var filteredResponse = payload.response.items;
         if (this.filterPaths !== null)
         {
            filteredResponse = array.filter(payload.response.items, lang.hitch(this, this.filterChildren));
         }

         // Resolve the promise...
         deferred.resolve(filteredResponse);
      },

      /**
       * This is the success callback from the [getChildren function]{@link module:alfresco/navigation/TreeStore#getChildren}
       * and iterates over the results calling the [updateChild function]{@link module:alfresco/navigation/TreeStore#updateChild} for 
       * each child. Once the data has been processed the Deferred object included in the original request configuration is
       * resolved to indicate to the original caller that the results are available.
       * 
       * @instance
       * @param {object} response The successful response object containing the child items
       * @param {object} originalRequestConfig The configuration object passed when making the request
       */
      onChildRequestSuccess: function alfresco_navigation_TreeStore__onChildRequestSuccess(response, originalRequestConfig) {
         // Update each item to set it's path...
         array.forEach(response.items, lang.hitch(this, this.updateChild, originalRequestConfig.parent));
         
         // If required, filter the items based on their paths...
         var filteredResponse = response.items;
         if (this.filterPaths !== null)
         {
            filteredResponse = array.filter(response.items, lang.hitch(this, this.filterChildren));
         }

         // Resolve the promise...
         originalRequestConfig.deferred.resolve(filteredResponse);
      },

      /**
       * Filters the children based on whether or not their "path" attribute matches any of Regular Expression configured in 
       * the [hidePaths]{@link module:alfresco/navigation/TreeStore#hidePaths} attribute.
       *
       * @instance
       * @param {object} item The current item to check
       * @param {number} index The index of the item in the original array
       * @returns {boolean} true if the item should be kept
       */
      filterChildren: function alfresco_navigation_TreeStore__filterChildren(item, /*jshint unused:false*/ index) {
         var include = array.some(this.filterPaths, function(filter) {
            var matches = true;
            try
            {
               var re = new RegExp(filter);
               matches = re.test(item.path);
            }
            catch (e)
            {
               // Ignore failing Regular Expressions
            }
            return matches;
         });
         return include;
      },
      
      /**
       * This is called from the [onChildRequestSuccess function]{@link module:alfresco/navigation/TreeStore#onChildRequestSuccess] for
       * each child retrieved. It allows the opportunity to update the child. By default this will set the "id" attribute of the child
       * as the "nodeRef", the "value" as the "name" and the "path" as the concatenation of the parent path and the child name. Setting
       * the path information is important as it allows further child queries to be processed accurately. 
       * 
       * @instance
       * @param {object} parent The child objects parent
       * @param {object} child The child object to update
       * @param {number} index The index of the child
       */
      updateChild: function alfresco_navigation_TreeStore__updateChild(parent, child, /*jshint unused:false*/ index) {
         child.id = child.nodeRef;
         child.value = child.name;
         child.path = (parent.path || "/") + child.name + "/";
         
         // Modify the name to be the description for site containers...
         if (child.description && 
             child.aspects && 
             array.some(child.aspects, function(item) { return item === "st:siteContainer"; }))
         {
            child.name = child.description;
         }
      },
      
      /**
       * @instance
       * @param {object} response The failure response object containing the child items
       * @param {object} originalRequestConfig The configuration object passed when making the request
       */
      onChildRequestFailure: function alfresco_navigation_TreeStore__onChildRequestFailure(response, originalRequestConfig) {
         this.alfLog("error", "Could not load children");
         originalRequestConfig.deferred.resolve([]);
      }
   });
});