Source: services/UserService.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 service has been provided for working with users. Currently it provides the capability to
 * list users (either in an [AlfList]{@link module:alfresco/lists/AlfList} or as options in a
 * [form control]{@link module:alfresco/forms/controls/BaseFormControl} that supports options, such
 * as a [Select control]{@link module:alfresco/forms/controls/Select}). It also provides the ability
 * to set the preferred home page of a user as well as their current status.<p>
 * <p>It is likely that this service does not yet provide all user related capabilities that may be
 * required - if a capability is missing then please raise a feature request as an issue on
 * the [GitHub project]{@link https://github.com/Alfresco/Aikau/issues}</p>
 * 
 * @module alfresco/services/UserService
 * @extends module:alfresco/services/BaseService
 * @mixes module:alfresco/core/CoreXhr
 * @mixes module:alfresco/core/NotificationUtils
 * @mixes module:alfresco/services/_UserServiceTopicMixin
 * @mixes module:alfresco/services/_PreferenceServiceTopicMixin
 * @author Dave Draper
 */
define(["dojo/_base/declare",
        "alfresco/services/BaseService",
        "alfresco/core/CoreXhr",
        "alfresco/core/NotificationUtils",
        "alfresco/services/_UserServiceTopicMixin",
        "alfresco/services/_PreferenceServiceTopicMixin",
        "alfresco/core/topics",
        "alfresco/util/urlUtils",
        "dojo/request/xhr",
        "dojo/json",
        "dojo/_base/lang",
        "dojo/_base/array",
        "service/constants/Default"],
        function(declare, BaseService, AlfXhr, NotificationUtils, 
              _UserServiceTopicMixin, _PreferenceServiceTopicMixin, topics, urlUtils, xhr, JSON, lang, array, AlfConstants) {
   
   return declare([BaseService, AlfXhr, NotificationUtils, _UserServiceTopicMixin, _PreferenceServiceTopicMixin], {
      
      /**
       * This is the dot-notation preferences property address for the user's home page
       *
       * @instance
       * @type {string}
       * @default
       */
      PREF_KEY_USER_HOME_PAGE: "org.alfresco.share.user.homePage",
      
      /**
       * An array of the i18n files to use with this widget.
       * 
       * @instance
       * @type {object[]}
       * @default [{i18nFile: "./i18n/UserService.properties"}]
       */
      i18nRequirements: [{i18nFile: "./i18n/UserService.properties"}],
      
      /**
       * Sets up the subscriptions for the UserService
       * 
       * @instance 
       * @since 1.0.32
       * @listens module:alfresco/core/topics#GET_USERS
       * @listens module:alfresco/core/topics#GET_AUTHORITIES
       * @listens module:alfresco/core/topics#GET_FOLLOWED_USERS
       * @listens module:alfresco/core/topics#GET_FOLLOWING_USERS
       * @listens module:alfresco/core/topics#FOLLOW_USERS
       * @listens module:alfresco/core/topics#UNFOLLOW_USERS
       */
      registerSubscriptions: function alfresco_services_UserService__registerSubscriptions() {
         this.alfSubscribe(this.updateUserStatusTopic, lang.hitch(this, this.updateUserStatus));
         this.alfSubscribe(this.setUserHomePageTopic, lang.hitch(this, this.onSetUserHomePage));
         this.alfSubscribe(this.setUserHomePageSuccessTopic, lang.hitch(this, this.onSetUserHomePageSuccess));
         this.alfSubscribe(this.setUserHomePageFailureTopic, lang.hitch(this, this.onSetUserHomePageFailure));
         this.alfSubscribe(topics.GET_USERS, lang.hitch(this, this.onGetUsers));
         this.alfSubscribe(topics.GET_AUTHORITIES, lang.hitch(this, this.onGetAuthorities));
         this.alfSubscribe(topics.GET_FOLLOWED_USERS, lang.hitch(this, this.onGetFollowedUsers));
         this.alfSubscribe(topics.GET_FOLLOWING_USERS, lang.hitch(this, this.onGetFollowingUsers));
         this.alfSubscribe(topics.FOLLOW_USERS, lang.hitch(this, this.onFollowUsers));
         this.alfSubscribe(topics.UNFOLLOW_USERS, lang.hitch(this, this.onUnfollowUsers));
         this.alfSubscribe(topics.GET_USER, lang.hitch(this, this.onGetUser));
      },

      /**
       * Handles requests to retrieve a single user.
       * 
       * @instance
       * @param {object} payload The published payload with the userName of the user to retrieve
       * @since 1.0.86
       */
      onGetUser: function alfresco_services_UserService__onGetUser(payload) {
         if (payload.userName)
         {
            var url = AlfConstants.PROXY_URI + "api/people?filter=" + payload.userName;
            var config = {
               userName: payload.userName,
               url: url,
               method: "GET",
               successCallback: this.onUserSuccess,
               failureCallback: this.onUserFailure,
               callbackScope: this
            };
            this.mergeTopicsIntoXhrPayload(payload, config);
            this.serviceXhr(config);
         }
         else
         {
            this.alfLog("warn", "A request was made to retrieve the details for a user, but no 'userName' attribute was provided in the payload", payload, this);
         }
      },

      /**
       * Handles successful requests to get users filtered by the supplied user name. Makes an additional
       * XHR request to determine whether or not the user is being followed by the current user.
       * 
       * @instance
       * @param {object} response The response from the request
       * @param {object} originalRequestConfig The configuration passed on the original request
       * @since 1.0.86
       */
      onUserSuccess: function alfresco_services_UserService__onUserSuccess(response, originalRequestConfig) {
         var items = lang.getObject("people", false, response);
         if (items)
         {
            var targetUser;
            array.some(items, function(item) {
               if (item.userName === originalRequestConfig.userName)
               {
                  targetUser = item;
                  if (item.firstName && item.lastName)
                  {
                     targetUser.displayName = item.firstName + " " + item.lastName;
                  }
                  else if (item.firstName)
                  {
                     targetUser.displayName = item.firstName;
                  }
                  else if (item.lastName)
                  {
                     targetUser.displayName = item.lastName;
                  }
                  else
                  {
                     targetUser.displayName = "";
                  }

                  var url = AlfConstants.PROXY_URI + "api/subscriptions/" + AlfConstants.USERNAME + "/follows";
                  var config = {
                     url: url,
                     userData: targetUser,
                     initialRequestConfig: originalRequestConfig,
                     data: [targetUser.userName],
                     method: "POST",
                     successCallback: this.publishUser,
                     failureCallback: this.onUserFailure,
                     callbackScope: this
                  };
                  this.serviceXhr(config);
               }
               return targetUser;
            }, this);
         }
      },

      /**
       * This is the success callback for the [onGetUser]{@link module:alfresco/services/UserService#onGetUser} function.
       * 
       * @instance
       * @param {object} response The response from the request
       * @param {object} originalRequestConfig The configuration passed on the original request
       * @since 1.0.86
       */
      publishUser: function alfresco_services_UserService__publishUser(response, originalRequestConfig) {
         var user = originalRequestConfig.userData;
         user.following = (response && response.length && response[0][user.userName]) || false;

         var topic = lang.getObject("initialRequestConfig.alfSuccessTopic", false, originalRequestConfig);
         if (!topic)
         {
            topic = lang.getObject("initialRequestConfig.alfResponseTopic", false, originalRequestConfig);
         }
         if (topic)
         {
            this.alfPublish(topic, {
               user: user
            });
         }
      },

      /**
       * This is the failure callback for the [onGetUser]{@link module:alfresco/services/UserService#onGetUser} function.
       * 
       * @instance
       * @param {object} response The response from the request
       * @param {object} originalRequestConfig The configuration passed on the original request
       * @since 1.0.86
       */
      onUserFailure: function alfresco_services_UserService__onUserFailure(response, originalRequestConfig) {
         this.alfLog("error", "It was not possible to retrieve the user", response, originalRequestConfig, this);
      },

      /**
       * Handles requests to follow users.
       * 
       * @instance
       * @param {object} payload The details of the users to follow
       * @since 1.0.86
       */
      onFollowUsers: function alfresco_services_UserService__onFollowUsers(payload) {
         if (payload.userNames)
         {
            var url = AlfConstants.PROXY_URI + "api/subscriptions/" + AlfConstants.USERNAME + "/follow";
            var config = {
               url: url,
               data: payload.userNames,
               method: "POST"
            };
            this.mergeTopicsIntoXhrPayload(payload, config);
            this.serviceXhr(config);
         }
         else
         {
            this.alfLog("warn", "A request was made to follow users, but no 'userName' attribute was provided in the payload", payload, this);
         }
      },

      /**
       * Handles requests to unfollow users.
       * 
       * @instance
       * @param {object} payload The details of the users to follow
       * @since 1.0.86
       */
      onUnfollowUsers: function alfresco_services_UserService__onUnfollowUsers(payload) {
         if (payload.userNames)
         {
            var url = AlfConstants.PROXY_URI + "api/subscriptions/" + AlfConstants.USERNAME + "/unfollow";
            var config = {
               url: url,
               data: payload.userNames,
               method: "POST"
            };
            this.mergeTopicsIntoXhrPayload(payload, config);
            this.serviceXhr(config);
         }
         else
         {
            this.alfLog("warn", "A request was made to unfollow users, but no 'userName' attribute was provided in the payload", payload, this);
         }
      },

      /**
       * Handles requests to retrieve authorities (users or groups).
       * 
       * @instance
       * @param {object} payload The request details
       * @since 1.0.77
       */
      onGetAuthorities: function alfresco_services_UserService__onGetAuthorities(payload) {
         var topic;
         if (payload.alfResponseTopic)
         {
            topic = (payload.alfResponseScope || "") + payload.alfResponseTopic;
         }
         else
         {
            topic = payload.responseTopic;
         }

         var url = AlfConstants.PROXY_URI + "api/forms/picker/authority/children";
         
         var searchTerm = payload.query || "";
         url = urlUtils.addQueryParameter(url, "searchTerm", searchTerm);

         var selectableType = payload.selectableType || "cm:person";
         url = urlUtils.addQueryParameter(url, "selectableType", selectableType);

         var size = payload.size || 1000;
         url = urlUtils.addQueryParameter(url, "size", size);
         
         this.serviceXhr({
            url: url,
            method: "GET",
            alfTopic: topic,
            alfResponseTopic: topic,
            alfSuccessTopic: payload.alfSuccessTopic,
            alfFailureTopic: payload.alfFailureTopic
         });
      },

      /**
       * 
       * @instance
       * @param  {object} payload Additional details for the request.
       * @since 1.0.86
       */
      onGetFollowedUsers: function alfresco_services_UserService__onGetFollowedUsers(payload) {
         var url = AlfConstants.PROXY_URI + "api/subscriptions/" + AlfConstants.USERNAME + "/following";
         var config = {
            url: url,
            method: "GET"
         };
         this.mergeTopicsIntoXhrPayload(payload, config);
         this.serviceXhr(config);
      },

      /**
       * 
       * @instance
       * @param  {object} payload Additional details for the request.
       * @since 1.0.86
       */
      onGetFollowingUsers: function alfresco_services_UserService__onGetFollowingUsers(payload) {
         var url = AlfConstants.PROXY_URI + "api/subscriptions/" + AlfConstants.USERNAME + "/followers";
         var config = {
            url: url,
            method: "GET"
         };
         this.mergeTopicsIntoXhrPayload(payload, config);
         this.serviceXhr(config);
      },

      /**
       * Handles requests to retrieve all users from the Alfresco Repository.
       * 
       * @instance
       * @param  {object} payload The payload for the request
       * @since 1.0.60
       */
      onGetUsers: function alfresco_services_UserService__onGetUsers(payload) {
         var topic;
         if (payload.alfResponseTopic)
         {
            topic = (payload.alfResponseScope || "") + payload.alfResponseTopic;
         }
         else
         {
            topic = payload.responseTopic;
         }

         var url = AlfConstants.PROXY_URI + "api/people";
         if (payload.dataFilters)
         {
            url = urlUtils.addFilterQueryParameters(url, payload);
         }
         else if (payload.filter)
         {
            url = urlUtils.addQueryParameter(url, "filter", payload.filter);
         }

         if (payload.sortField)
         {
            url = urlUtils.addQueryParameter(url, "sortBy", payload.sortField);
         }

         url = urlUtils.addQueryParameter(url, "dir", (payload.sortAscending !== false) ? "asc" : "desc");
         
         if (payload.pageSize)
         {
            url = urlUtils.addQueryParameter(url, "pageSize", payload.pageSize);
         }
         if (payload.page && payload.pageSize)
         {
            var startIndex = (payload.page - 1) * payload.pageSize;
            url = urlUtils.addQueryParameter(url, "startIndex", startIndex);
         }
         
         this.serviceXhr({
            url: url,
            method: "GET",
            alfResponseTopic: topic,
            alfSuccessTopic: payload.alfSuccessTopic,
            alfFailureTopic: payload.alfFailureTopic,
            successCallback: this.onUsersSuccess,
            failureCallback: this.onUsersFailure,
            callbackScope: this
         });
      },

      /**
       * This is the success callback for the [onGetUsers]{@link module:alfresco/services/UserService#onGetUsers} function.
       * 
       * @instance
       * @param {object} response The response from the request
       * @param {object} originalRequestConfig The configuration passed on the original request
       * @since 1.0.60
       */
      onUsersSuccess: function alfresco_services_UserService__onUsersSuccess(response, originalRequestConfig) {
         var items = lang.getObject("people", false, response);
         if (items)
         {
            array.forEach(items, function(item) {
               if (item.firstName && item.lastName)
               {
                  item.displayName = item.firstName + " " + item.lastName;
               }
               else if (item.firstName)
               {
                  item.displayName = item.firstName;
               }
               else if (item.lastName)
               {
                  item.displayName = item.lastName;
               }
               else
               {
                  item.displayName = "";
               }
            });

            this.alfPublish(originalRequestConfig.alfSuccessTopic || originalRequestConfig.alfResponseTopic, {
               items: items
            });
         }
      },

      /**
       * This is the failure callback for the [onGetUsers]{@link module:alfresco/services/UserService#onGetUsers} function.
       * 
       * @instance
       * @param {object} response The response from the request
       * @param {object} originalRequestConfig The configuration passed on the original request
       * @since 1.0.60
       */
      onUsersFailure: function alfresco_services_UserService__onUsersFailure(response, originalRequestConfig) {
         this.alfLog("error", "It was not possible to retrieve the list of users", response, originalRequestConfig, this);
      },
      
      /**
       * Handles XHR posting to a new user status mesage to the server. 
       * 
       * @instance
       * @param {object} data The payload containing the user status to post.
       */
      updateUserStatus: function alfresco_services_UserService__updateUserStatus(data) {
         var url = AlfConstants.URL_SERVICECONTEXT + "components/profile/userstatus";
         this.serviceXhr({url : url,
                          data: data,
                          method: "POST",
                          successCallback: this.userStatusUpdateSuccess,
                          failureCallback: this.userStatusUpdateFailure,
                          callbackScope: this});
      },
      
      /**
       * This handles successfully completed requests to update the user status.
       * 
       * @instance
       * @param {object} response The response from the request
       * @param {object} originalRequestConfig The configuration passed on the original request
       */
      userStatusUpdateSuccess: function alfresco_services_UserService__userStatusUpdateSuccess(response, originalRequestConfig) {
         // NOTE: The current update status API does NOT include the updated status message in the
         //       response. Ideally it would be nice to change this such that it does to ensure
         //       that the users status is correctly reflected. However, we will include the user
         //       status property here in the publication payload and set it to null to indicate
         //       that it is unknown. This is done because the UserStatus widget (at the time of 
         //       writing the only subscriber to this publication is coded to handle status updates
         //       that DO include a status message.
         this.alfLog("log", "User Status Update Success", response);
         if (typeof response === "string")
         {
            response = JSON.parse(this.cleanupJSONResponse(response));
         }

         // Display a success message...
         this.displayMessage(this.message("message.status.success"));

         this.alfPublish(this.updateUserStatusSuccessTopic, {
            userStatus: originalRequestConfig.data.status,
            userStatusTime: response.userStatusTime.iso8601
         });
      },
      
      /**
       * This handles failed attempts to update the user status.
       * 
       * @instance
       * @param {object} response The response from the request
       * @param {object} originalRequestConfig The configuration passed on the original request
       */
      userStatusUpdateFailure: function alfresco_services_UserService__userStatusUpdateFailure(response, /*jshint unused:false*/ originalRequestConfig) {
         this.alfLog("log", "User Status Update Failure", response);
         if (typeof response === "string")
         {
            response = JSON.parse(this.cleanupJSONResponse(response));
         }
         
         // Display a failure message...
         this.displayMessage(this.message("message.status.failure"));
         
         this.alfPublish(this.updateUserStatusFailureTopic, response);
      },
      
      /**
       * Sets the user's home page.
       * 
       * @instance
       * @param {object} payload
       */
      onSetUserHomePage: function alfresco_services_UserService__onSetUserHomePage(payload) {
         if (payload && payload.homePage)
         {
            this.alfPublish(this.setPreferenceTopic,{
               preference: this.PREF_KEY_USER_HOME_PAGE,
               value: payload.homePage,
               alfResponseTopic: this.setUserHomePageTopic
            });
         }
      },
      
      /**
       * Displays a message indicating successful save of user's home page.
       * 
       * @instance
       * @listens setUserHomePageTopicSuccess
       */
      onSetUserHomePageSuccess: function alfresco_services_UserService__onSetUserHomePageSuccess() {
         // Display a success message...
         this.displayMessage(this.message("message.homePage.success"));
      },
      
      /**
       * Displays a message indicating failure to save of user's home page.
       * 
       * @instance
       * @listens setUserHomePageTopicFailure
       */
      onSetUserHomePageFailure: function alfresco_services_UserService__onSetUserHomePageFailure() {
         // Display a failure message...
         this.displayMessage(this.message("message.homePage.failure"));
      }
   });
});