Source: documentlibrary/AlfDocumentList.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/documentlibrary/AlfDocumentList
 * @extends module:alfresco/lists/AlfFilteredList
 * @author Dave Draper
 */
define(["dojo/_base/declare",
        "alfresco/lists/AlfFilteredList",
        "alfresco/core/JsNode",
        "alfresco/core/topics",
        "dojo/_base/array",
        "dojo/_base/lang",
        "alfresco/util/hashUtils",
        "dojo/io-query",
        "dojo/dom-class"],
        function(declare, AlfFilteredList, JsNode, topics, array, lang, hashUtils, ioQuery, domClass) {

   return declare([AlfFilteredList], {

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

      /**
       * Indicates whether or not folders should be shown in the document library.
       *
       * @instance
       * @type {boolean}
       * @default
       */
      showFolders: true,

      /**
       * This indicates whether or not [clicking on a folder]{@link module:alfresco/documentlibrary/AlfDocumentList#onFolderClick} 
       * will perform a globally scoped publication. This is true by default as the as the default
       * [topic for folder clicks]{@link module:alfresco/documentlibrary/_AlfDocumentListTopicMixin#pathChangeTopic} is
       * expected to be subscribed to by the [NavigationService]{@link module:alfresco/services/NavigationService} which
       * will typically be subscribing globally.
       *
       * @instance
       * @type {boolean}
       * @default
       */
      folderClickPublishGlobal: true,

      /**
       * Indicates whether or not documents should be shown in the document library.
       *
       * @instance
       * @type {boolean}
       * @default
       */
      showDocuments: true,

      /**
       * @instance
       * @type {object}
       * @default
       */
      currentFilter: null,

      /**
       * Used to trigger an event to navigate up a directory.
       *
       * @instance
       * @type {String}
       * @default
       */
      parentNavTopic: "ALF_DOCLIST_PARENT_NAV",

      /**
       * Overrides the [default configuration]{@link module:alfresco/lists/AlfList#suppressDndUploading}
       * to ensure that document lists support drag-and-drop uploading (unless otherwise configured).
       * 
       * @instance
       * @type {boolean}
       * @default
       * @since 1.0.39
       */
      suppressDndUploading: false,
    
      /**
       * Overrides the [inherited default]{@link module:alfresco/lists/AlfHashList#updateInstanceValues}
       * to ensure that instance values should be updated from the hash. This only appliees when
       * [useHash]{@link module:alfresco/lists/AlfHashList#useHash} is configured to be true.
       *
       * @instance
       * @type {boolean}
       * @default
       */
      updateInstanceValues: true,

      /**
       * Extends the [inherited function]{@link module:alfresco/lists/AlfList#copyViewData} to set the
       * drag-and-drop upload capabilities based on the 
       * [currentFilter]{@link module:alfresco/documentlibrary/AlfDocumentList#currentFilter} value.
       * If the filter has a path value then
       * [addUploadDragAndDrop]{@link module:alfresco/documentlibrary/_AlfDndDocumentUploadMixin#addUploadDragAndDrop}
       * is called, otherwise 
       * [removeUploadDragAndDrop]{@link module:alfresco/documentlibrary/_AlfDndDocumentUploadMixin#removeUploadDragAndDrop}
       * is called.
       * 
       * @instance
       * @since 1.0.51
       */
      copyViewData: function alfresco_lists_AlfList__copyViewData(/*jshint unused:false*/oldView, newView) {
         this.inherited(arguments);

         // See AKU-954: Ensure DND upload permissions are set correctly...
         var userPermissions = lang.getObject("currentData.metadata.parent.permissions.user", false, this);
         var isContainer = lang.getObject("currentData.metadata.parent.isContainer", false, this);
         if (userPermissions &&
             ((isContainer === true && userPermissions.CreateChildren === true) ||
             (isContainer === false && userPermissions.Write === true)))
         {
            newView.addUploadDragAndDrop(newView.dragAndDropNode);
            domClass.add(newView.domNode, "alfresco-documentlibrary-AlfDocumentList--upload-enabled");
         }
         else
         {
            newView.removeUploadDragAndDrop(newView.dragAndDropNode);
            domClass.remove(newView.domNode, "alfresco-documentlibrary-AlfDocumentList--upload-enabled");
         }
      },

      /**
       * Extends the [inherited function]{@link module:alfresco/lists/AlfSortablePaginatedListt#postMixInProperties}
       * to set a default filter to be a root path.
       *
       * @instance
       */
      postMixInProperties: function alfresco_documentlibrary_AlfDocumentList__postMixInProperties() {
         this.inherited(arguments);

         if (this.useHash === true)
         {
            // Push the core hash update variables into the array configured by the extended AlfSortablePaginatedList
            this._coreHashVars.push("path","filter","tag","category");
         }

         if (!this.currentFilter)
         {
            this.currentFilter = {
               path: "/"
            };
         }
      },

      /**
       * Run after widget created
       *
       * @instance
       * @override
       * @since 1.0.48
       */
      postCreate: function alfresco_documentlibrary_AlfDocumentList__postCreate() {
         this.inherited(arguments);
         domClass.add(this.domNode, "alfresco-documentlibrary-AlfDocumentList");
      },

      /**
       * This function sets up the subscriptions that the Document List relies upon to manage its
       * internal state and request documents.
       *
       * @instance
       * @listens ALF_DOCUMENTLIST_PATH_CHANGED
       * @listens ALF_DOCUMENTLIST_CATEGORY_CHANGED
       * @listens module:alfresco/core/topics#DOCUMENTLIST_TAG_CHANGED
       * @listens filterSelectionTopic
       * @listens documentSelectionTopic
       * @listens parentNavTopic
       */
      setupSubscriptions: function alfresco_documentlibrary_AlfDocumentList__setupSubscriptions() {
         this.inherited(arguments);
         this.alfSubscribe(topics.PATH_CHANGED, lang.hitch(this, this.onPathChanged));
         this.alfSubscribe("ALF_DOCUMENTLIST_CATEGORY_CHANGED", lang.hitch(this, this.onCategoryChanged));
         this.alfSubscribe(this.docListTagChangedTopic, lang.hitch(this, this.onTagChanged));
         this.alfSubscribe(this.filterSelectionTopic, lang.hitch(this, this.onFilterChanged));
         this.alfSubscribe(this.documentSelectionTopic, lang.hitch(this, this.onDocumentSelection));
         this.alfSubscribe(this.showFoldersTopic, lang.hitch(this, this.onShowFolders));
         this.alfSubscribe(this.parentNavTopic, lang.hitch(this, this.onParentNav));
      },

      /**
       * Handles requests to update the current path.
       *
       * @instance
       * @param {object} payload The details of the new path
       * @fires ALF_NAVIGATE_TO_PAGE
       */
      onPathChanged: function alfresco_documentlibrary_AlfDocumentList__onPathChanged(payload) {
         if (payload.path)
         {
            // Clear any old data when infinite scroll is enabled (see AKU-358)
            this.useInfiniteScroll && this.clearViews();

            if (this.useHash === true)
            {
               var currHash = hashUtils.getHash();
               currHash.path = payload.path;
               currHash.currentPage = 1;
               delete currHash.filter;
               delete currHash.category;
               delete currHash.tag;
               this.alfPublish("ALF_NAVIGATE_TO_PAGE", {
                  url: ioQuery.objectToQuery(currHash),
                  type: "HASH"
               }, true);
            }
            else
            {
               this.currentPage = 1;
               this.currentFilter = {
                  path: payload.path
               };
               this.loadData();
            }
         }
         else
         {
            this.alfLog("warn", "A request was made to change the displayed path of a Document List, but no 'path' attribute was provided", payload, this);
         }
      },

      /**
       * Handles requests to update the current category.
       *
       * @instance
       * @param {object} payload The details of the new category path
       * @fires ALF_NAVIGATE_TO_PAGE
       */
      onCategoryChanged: function alfresco_documentlibrary_AlfDocumentList__onCategoryChanged(payload) {
         if (payload.path)
         {
            // Clear any old data when infinite scroll is enabled (see AKU-358)
            this.useInfiniteScroll && this.clearViews();

            if (this.useHash === true)
            {
               var currHash = hashUtils.getHash();
               currHash.category = payload.path;
               currHash.currentPage = 1;
               delete currHash.filter;
               delete currHash.path;
               delete currHash.tag;
               this.alfPublish("ALF_NAVIGATE_TO_PAGE", {
                  url: ioQuery.objectToQuery(currHash),
                  type: "HASH"
               }, true);
            }
            else
            {
               this.currentPage = 1;
               this.currentFilter = {
                  category: payload.path
               };
               this.loadData();
            }
         }
         else
         {
            this.alfLog("warn", "A request was made to change the displayed path of a Document List, but no 'path' attribute was provided", payload, this);
         }
      },

      /**
       *
       *
       * @instance
       * @param {object} payload The details of the changed filter
       * @fires ALF_NAVIGATE_TO_PAGE
       */
      onFilterChanged: function alfresco_documentlibrary_AlfDocumentList__onFilterChanged(payload) {
         if (payload.value)
         {
            // Clear any old data when infinite scroll is enabled (see AKU-358)
            this.useInfiniteScroll && this.clearViews();

            if (this.useHash === true)
            {
               var currHash = hashUtils.getHash();
               currHash.filter = payload.value;
               currHash.currentPage = 1;
               delete currHash.category;
               delete currHash.path;
               delete currHash.tag;
               this.alfPublish("ALF_NAVIGATE_TO_PAGE", {
                  url: ioQuery.objectToQuery(currHash),
                  type: "HASH"
               }, true);
            }
            else
            {
               this.currentPage = 1;
               this.currentFilter = {
                  filter: payload.value
               };
               this.loadData();
            }
         }
         else
         {
            this.alfLog("warn", "A request was made to change the filter for a Document List, but no 'value' attribute was provided", payload, this);
         }
      },

      /**
       *
       *
       * @instance
       * @param {object} payload The details of the changed tag
       * @fires ALF_NAVIGATE_TO_PAGE
       */
      onTagChanged: function alfresco_documentlibrary_AlfDocumentList__onTagChanged(payload) {
         if (payload.value)
         {
            // Clear any old data when infinite scroll is enabled (see AKU-358)
            this.useInfiniteScroll && this.clearViews();

            if (this.useHash === true)
            {
               var currHash = hashUtils.getHash();
               currHash.tag = payload.value;
               currHash.currentPage = 1;
               delete currHash.category;
               delete currHash.path;
               delete currHash.filter;
               this.alfPublish("ALF_NAVIGATE_TO_PAGE", {
                  url: ioQuery.objectToQuery(currHash),
                  type: "HASH"
               }, true);
            }
            else
            {
               this.currentPage = 1;
               this.currentFilter = {
                  tag: payload.value
               };
               this.loadData();
            }
         }
         else
         {
            this.alfLog("warn", "A request was made to change the tag filter for a Document List, but no 'value' attribute was provided", payload, this);
         }
      },

      /**
       * This is the topic that will be subscribed to for responding to item clicks unless [useHash]{@link module:alfresco/documentlibrary/AlfDocumentList#useHash}
       * is set to true. [Views]{@link module:alfresco/lists/views/AlfListView} that defined
       * renderers that provide links using the [_ItemLinkMixin]{@link module:alfresco/renderers/_ItemLinkMixin} should
       * be configured to set a matching [linkClickTopic][_ItemLinkMixin]{@link module:alfresco/renderers/_ItemLinkMixin#linkClickTopic}
       * attribute in order to have their actions processed.
       *
       * @instance
       * @type {string}
       * @default
       */
      linkClickTopic: "ALF_DOCLIST_NAV",

      /**
       * This function is called whenever the [linkClickTopic]{@link module:alfresco/documentlibrary/AlfDocumentList#linkClickTopic}
       * is published. It processes the payload and updates the current filter and then refreshes the current
       * data by calling [loadData]{@link module:alfresco/documentlibrary/AlfDocumentList#loadData}.
       *
       * @instance
       * @param {object} payload
       */
      onItemLinkClick: function alfresco_documentlibrary_AlfDocumentList__onItemLinkClick(payload) {
         var node = lang.getObject("item.node", false, payload) || payload.node;
         if (node.isContainer === true || node.isLink === true)
         {
            this.onFolderClick(payload);
         }
         else
         {
            this.onDocumentClick(payload);
         }
      },

      /**
       *
       * @instance
       * @param {object} payload
       */
      onFolderClick: function alfresco_documentlibrary_AlfDocumentList__onFolderClick(payload) {
         if (payload.url)
         {
            this.currentFilter = this.processHashFilter(payload.url);
            if (!this.useHash && this.currentFilter.path)
            {
               this.alfPublish(this.pathChangeTopic, this.currentFilter, this.folderClickPublishGlobal);
            }
         }
         else
         {
            this.alfLog("warn", "A 'url' attribute was expected to be provided for an item click", payload, this);
         }
      },

      /**
       *
       * @instance
       * @param {object} payload
       */
      onDocumentClick: function alfresco_documentlibrary_AlfDocumentList__onDocumentClick(payload) {
         // jshint unused:false
         // No action for the moment
      },

      /**
       * Checks the hash for updates relating to pagination and sorting.
       *
       * @instance
       * @param {object} hashParameters An object containing the current hash parameters
       */
      _updateCoreHashVars: function alfresco_documentlibrary_AlfDocumentList___updateCoreHashVars(hashParameters) {
         this.inherited(arguments);
         this.currentFilter = hashParameters;
      },

      /**
       * Triggered when a request to navigate to the current items parent is shown. If the
       * [currentFilter]{@link module:alfresco/documentlibrary/AlfDocumentList#currentFilter}
       * is a non-root path then it will be updated to remove the last element of the current path.
       * In all other cases a publication will be made requesting the retrieval of the parent node.
       *
       * @param payload
       * @fires ALF_DOC_GET_PARENT_NODEREF
       */
      onParentNav: function alfresco_documentlibrary_AlfDocumentList__onParentNav(/*jshint unused:false*/payload) {
         if (this.currentFilter.path && this.currentFilter.path !== "/")
         {
            // NOTE: It's coded like this to support IE8 (which doesn't support lastIndexOf)...
            var p = this.currentFilter.path;
            var a = p.split("/");
            a.pop();
            this.currentFilter.path = a.join("/");
            this.resetPaginationData();
            this.loadData();
         }
         else
         {
            // Get current item's parent:
            var responseTopic = this.generateUuid() + "_parentNavSuccess",
               subscriptionHandles = [this.alfSubscribe(responseTopic, lang.hitch(this, this.onParentNavSuccess), true)];

            // We want to get the parent's parent of the current items that are displayed.
            var parentNodeRef = lang.getObject("currentData.metadata.parent.nodeRef", false, this);

            if (parentNodeRef) {
               this.alfPublish("ALF_DOC_GET_PARENT_NODEREF", {
                  nodeRef: parentNodeRef,
                  rawData: this.rawData,
                  originalResponseTopic: responseTopic, // not alfResponseTopic - as this needs passing through another response cycle.
                  subscriptionHandles: subscriptionHandles
               }, true);
            }
            else
            {
               this.alfLog("error", "Cannot retrieve parent nodeRef", this);
            }
         }
      },

      /**
       * Triggered when we have the parent node object. (call back from onParentNav)
       *
       * @param payload
       */
      onParentNavSuccess: function alfresco_documentlibrary_AlfDocumentList__onParentNavSuccess(payload) {
         // We've received the nodeRef back, let's send it on to the click handler.
         this.alfPublish(this.linkClickTopic, payload);
      },

      /**
       * Set this attribute to be true if the Document List should retrieve data directly from the 
       * Alfresco repository rather than going through an intermediary client REST API. For example
       * the [DocumentService]{@link module:alfresco/services/DocumentService} will request data from
       * a Share REST API rather than going directly to the repository.
       *
       * @instance
       * @type {boolean}
       * @default
       */
      rawData: false,

      /**
       * Extends the [inherited function]{@link module:alfresco/lists/AlfSortablePaginatedList#updateLoadDataPayload} to
       * add the additional document library related data.
       *
       * @instance
       * @param {object} payload The payload object to update
       */
      updateLoadDataPayload: function alfresco_documentlibrary_AlfDocumentList__updateLoadDataPayload(payload) {
         this.inherited(arguments);

         var type = "all";
         if (this.showFolders === false && this.showDocuments === false)
         {
            // Someone has been silly...
            this.alfLog("warn", "An AlfDocumentList has been configured to neither show folders nor documents, so showing both", this);
         }
         else if (this.showFolders === false)
         {
            type = "documents";
         }
         else if (this.showDocuments === false)
         {
            type = "folders";
         }

         payload.type = type;
         payload.site = this.siteId;
         payload.container = this.containerId;
         payload.filter = this.currentFilter;
         payload.libraryRoot = this.rootNode;
         payload.rawData = this.rawData;

         if (!this.siteId && this.nodeRef)
         {
            // Repository mode (don't resolve Site-based folders)
            payload.nodeRef = this.nodeRef.toString();
         }
      },

      /**
       * This is an extension point function for extending modules to perform processing on the loaded
       * data once it's existence has been verified
       *
       * @instance
       * @param {object} response The original response.
       */
      processLoadedData: function alfresco_documentlibrary_AlfDocumentList__processLoadedData(response) {
         array.forEach(this.currentData, function(item) {
            item.jsNode = new JsNode(item.node);
         }, this);

         // Publish the details of the metadata returned from the data request...
         if (response.metadata)
         {
            this.alfPublish(this.metadataChangeTopic, {
               node: response.metadata
            });

            // Publish the details of the permissions for the current user. This will
            // only be available when the a specific node is shown rather than a set
            // of results across multiple nodes (e.g. the result of a filter request)
            if (response.metadata.parent &&
                response.metadata.parent.permissions &&
                response.metadata.parent.permissions.user)
            {
               this.alfPublish(this.userAccessChangeTopic, {
                  userAccess: response.metadata.parent.permissions.user
               });
            }
         }
         this.inherited(arguments);
      },

      /**
       * @instance
       * @param {object} payload The published details of the selected items
       */
      onDocumentSelection: function alfresco_documentlibrary_AlfDocumentList__onDocumentSelection(payload) {
         this.alfLog("log", "Documents Selected: ", payload);
         // TODO!
      },

      /**
       * @instance
       * @param {object} payload The details of the request
       */
      onShowFolders: function alfresco_documentlibrary_AlfDocumentList__onShowFolders(payload) {
         this.alfLog("log", "Show Folders Request: ", payload);
         if (payload && (payload.selected || payload.selected === false))
         {
            this.showFolders = payload.selected;
            if (this._readyToLoad)
            {
               this.loadData();
            }
         }
      },

      /**
       * Overrides the [default filters]{@link module:alfresco/lists/AlfFilteredList#widgetsForFilters} as 
       * there should be no filters for document lists unless explicitly configured.
       *
       * @instance
       * @type {object[]}
       * @default
       * @since 1.0.42
       */
      widgetsForFilters: null
   });
});