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

/**
 * Handles requests to redirect the browser to display different pages in either current or the new tab.
 *
 * @module alfresco/services/NavigationService
 * @extends module:alfresco/services/BaseService
 * @mixes module:alfresco/services/_NavigationServiceTopicMixin
 * @author Dave Draper
 * @author David Webster
 */
define(["dojo/_base/declare",
        "module",
        "alfresco/core/ObjectTypeUtils",
        "alfresco/core/topics",
        "alfresco/enums/urlTypes",
        "alfresco/services/BaseService",
        "alfresco/services/_NavigationServiceTopicMixin",
        "alfresco/util/hashUtils",
        "alfresco/util/urlUtils",
        "dojo/_base/array",
        "dojo/_base/lang",
        "dojo/Deferred",
        "dojo/dom-construct"],
        function(declare, module, ObjectTypeUtils, topics, urlTypes, BaseService, _NavigationServiceTopicMixin, hashUtils, urlUtils, array, lang, Deferred, domConstruct) {

   return declare([BaseService, _NavigationServiceTopicMixin], {

      /**
       * It is possible to configure the NavigationService to respond to one or more publications that result in page navigation.
       * This is the attribute that will be set to respond to those publications.
       *
       * @instance
       * @type {object[]}
       * @default
       */
      subscriptions: null,

      /**
       * Sets up the subscriptions for the NavigationService. The [navigateToPageTopic]{@link module:alfresco/services/_NavigationServiceTopicMixin#navigateToPageTopic}
       * topic is handled by the [navigateToPage]{@link module:alfresco/services/NavigationService#navigateToPage} function and the
       * [reloadPageTopic]{@link module:alfresco/services/_NavigationServiceTopicMixin#reloadPageTopic} is handled by the
       * [reloadPage]{@link module:alfresco/services/NavigationService#reloadPage} function.
       *
       * @instance
       * @listens navigateToPageTopic
       * @listens reloadPageTopic
       * @listens postToPageTopic
       * @since 1.0.32
       * @listens module:alfresco/core/topics#NAVIGATE_TO_PAGE
       * @listens module:alfresco/core/topics#RELOAD_PAGE
       * @listens module:alfresco/core/topics#POST_TO_PAGE
       */
      registerSubscriptions: function alfresco_services_NavigationService__registerSubscriptions() {
         this.alfSubscribe(this.navigateToPageTopic, lang.hitch(this, this.navigateToPage));
         this.alfSubscribe(this.reloadPageTopic, lang.hitch(this, this.reloadPage));
         this.alfSubscribe(this.postToPageTopic, lang.hitch(this, this.postToPage));
         if (this.subscriptions)
         {
            array.forEach(this.subscriptions, lang.hitch(this, this.setupNavigationSubscriptions));
         }
      },

      /**
       * Sets up a subscription to handle publications that occur on the page that should trigger navigation.
       *
       * @instance
       * @param {object} subscription The subscription to configure
       */
      setupNavigationSubscriptions: function alfresco_services_NavigationService__setupNavigationSubscriptions(subscription) {
         if (subscription && subscription.topic && subscription.url)
         {
            this.alfSubscribe(subscription.topic, lang.hitch(this, "navigateToPage", subscription));
         }
         else
         {
            this.alfLog("warn", "A NavigationService subscription was requested, but the subscription was missing one of the required attributes of 'topic' or 'url'", subscription, this);
         }
      },

      /**
       * Builds a URL from the supplied data object.
       *
       * @instance
       * @param  {object} data The data object form which to construct the URL
       * @return {string} The built URL
       */
      buildUrl: function alfresco_services_NavigationService__buildUrl(data) {
         // jshint maxcomplexity:false

         // Update the URL based on the site and type properties of the data object
         var url = data.url;
         var urlType = data.type;
         if (urlType === urlTypes.PAGE_RELATIVE)
         {
            var site = lang.getObject("site", false, data);
            if (!site || !ObjectTypeUtils.isString(site))
            {
               site = lang.getObject("site.shortName", false, data);
            }

            if (site && ObjectTypeUtils.isString(site))
            {
               var siteStem = "/site/" + site + "/";
               if (url.indexOf(siteStem) === -1)
               {
                  url = siteStem + url;
               }
            }
         }
         url = urlUtils.convertUrl(url, urlType);

         // Encode Parameters?
         var encodeParams = (typeof data.encodeParams !== "undefined")? data.encodeParams : true;

         // Add parameters supplied
         if (data.queryParams && typeof data.queryParams === "object") {
            array.forEach(Object.keys(data.queryParams), function(prop) {
               url = urlUtils.addQueryParameter(url, prop, data.queryParams[prop], encodeParams);
            });
         }

         if (data.hashParams && typeof data.hashParams === "object") {
            array.forEach(Object.keys(data.hashParams), function(prop) {
               url = urlUtils.addHashParameter(url, prop, data.hashParams[prop], encodeParams);
            });
         }

         return url;
      },

      /**
       * This is the default page navigation handler. It is called when the service receives a publication on
       * the [navigateToPageTopic]{@link module:alfresco/services/_NavigationServiceTopicMixin#navigateToPageTopic} topic. At the moment
       * it makes the assumption that the URL data will be relative to the Share page context.
       *
       * @instance
       * @param {object} data An object containing the information about the page to navigate to.
       * @todo explain what data can contain...
       */
      navigateToPage: function alfresco_services_NavigationService__navigateToPage(data) {
         // jshint maxcomplexity:false
         if (data.type !== urlTypes.HASH && !data.url)
         {
            this.alfLog("error", "A page navigation request was made without a target URL defined as a 'url' attribute", data);
         }
         else
         {
            this.alfLog("log", "Page navigation request received:", data);
            var url = this.buildUrl(data);
            if (url || url === "")
            {
               // Determine the location of the URL...
               if (data.type === urlTypes.HASH)
               {
                  hashUtils[data.modifyCurrent ? "updateHash" : "setHash"](url);
               }
               else if (!data.target || data.target === this.currentTarget)
               {
                  // Make a good attempt at ensuring that we're not trying to go back to the same page
                  // This has been added to ensure that the progress indicator is not shown when entering
                  // new search terms in the SearchBox when already on the Search page (in Share)... 
                  // This may require further enhancement...
                  var hashIndex = url.indexOf("#");
                  if ((hashIndex !== -1 && url.substring(0, hashIndex) === window.location.pathname) ||
                      (url === window.location.pathname))
                  {
                     window.location = url;
                  }
                  else if (url.indexOf("?a=true") !== -1 || 
                           url.indexOf("&a=true") !== -1)
                  {
                     window.location = url;
                  }
                  else
                  {
                     this.alfServicePublish(topics.PROGRESS_INDICATOR_ADD_ACTIVITY);
                     window.location = url;
                  }
               }
               else if (data.target === this.newTarget)
               {
                  window.open(url);
               }
               else if (data.target === this.namedTarget)
               {
                  if (data.targetName)
                  {
                     window.open(url, data.targetName);
                  }
                  else
                  {
                     this.alfLog("warn", "Tried to open named window, but did not specify 'targetName' property");
                  }
               }
            }
         }
      },

      /**
       * Sometimes we need to navigate to a page using post data (e.g. Assign Workflow action)
       *
       * @instance
       * @param data
       */
      postToPage: function alfresco_services_NavigationService__postToPage(data) {
         var url = this.buildUrl(data);
         if (url)
         {
            var form = domConstruct.create("form");
            form.method = "POST";
            form.action = url;
            if (data.target === this.newTarget)
            {
               form.target = "_blank";
            }
            var parameters = data.parameters || {};
            for (var name in parameters)
            {
               if (parameters.hasOwnProperty(name))
               {
                  var value = parameters[name];
                  if (value)
                  {
                     var input;
                     input = domConstruct.create("input");
                     input.setAttribute("name", name);
                     input.setAttribute("type", "hidden");
                     input.value = value;
                     domConstruct.place(input, form);
                  }
               }
            }
            domConstruct.place(form, document.body);
            if (!data.target || data.target === this.currentTarget)
            {
               this.alfServicePublish(topics.PROGRESS_INDICATOR_ADD_ACTIVITY);
               form.submit();
            }
            else
            {
               form.submit();
            }
         }
      },

      /**
       * Reloads the current page. Despite the simplicity of the action, page refreshes should still be handled over the
       * pub/sub layer as this provides an opportunity for additional logging and other actions required by third party
       * extensions.
       *
       * @instance
       * @param {object} data An object containing additional information. NOTE: Currently, no additional data is processed
       */
      reloadPage: function alfresco_services_NavigationService__reloadPage(data) {
         this.alfLog("log", "Page reload request received:", data);
         this.alfServicePublish(topics.PROGRESS_INDICATOR_ADD_ACTIVITY);
         window.location.reload(true);
      }
   });
});