1 /**
  2  * @fileOverview JavaScript representation of the Finesse User object
  3  *
  4  * @name finesse.restservices.User
  5  * @requires finesse.clientservices.ClientServices
  6  * @requires Class
  7  * @requires finesse.FinesseBase
  8  * @requires finesse.restservices.RestBase
  9  */
 10 
 11 var finesse = finesse || {};
 12 finesse.restservices = finesse.restservices || {};
 13 
 14 /** @private */
 15 finesse.restservices.User = finesse.restservices.RestBase.extend(/** @lends finesse.restservices.User.prototype */{
 16 
 17     _dialogs : null,
 18     _wrapUpReasons : null,
 19     _mediaPropertiesLayout : null,
 20 	
 21     /**
 22      * @class
 23      * JavaScript representation of a User object. Also exposes methods to operate
 24      * on the object against the server.
 25      *
 26 	 * @param {Object} options
 27 	 *     An object with the following properties:<ul>
 28      *         <li><b>id:</b> The id of the object being constructed</li>
 29      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
 30      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
 31      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
 32      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
 33      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
 34      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
 35      *             <li><b>content:</b> {String} Raw string of response</li>
 36      *             <li><b>object:</b> {Object} Parsed object of response</li>
 37      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
 38      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
 39      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
 40      *             </ul></li>
 41      *         </ul></li>
 42      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
 43      * @constructs finesse.restservices.User
 44      **/
 45     init: function (options) {
 46         this._super(options);
 47     },
 48 
 49     /**
 50      * @private
 51      * Gets the REST class for the current object - this is the User object.
 52      */
 53     getRestClass: function () {
 54         return finesse.restservices.User;
 55     },
 56 
 57     /**
 58     * @private
 59      * Gets the REST type for the current object - this is a "User".
 60      */
 61     getRestType: function () {
 62         return "User";
 63     },
 64 
 65     /**
 66     * @private
 67      * Returns whether this object supports subscriptions
 68      */
 69     supportsSubscriptions: function () {
 70         return true;
 71     },
 72 
 73     /**
 74      * Getter for the firstName of this User
 75      * @returns {String}
 76      *     The firstName for this User
 77      */
 78     getFirstName: function () {
 79         this.isLoaded();
 80         return this.getData().firstName;
 81     },
 82 
 83     /**
 84      * Getter for the lastName of this User
 85      * @returns {String}
 86      *     The lastName for this User
 87      */
 88     getLastName: function () {
 89         this.isLoaded();
 90         return this.getData().lastName;
 91     },
 92 
 93     /**
 94      * Getter for the extension of this User
 95      * @returns {String}
 96      *     The extension, if any, of this User
 97      */
 98     getExtension: function () {
 99         this.isLoaded();
100         return this.getData().extension;
101     },
102 
103     /**
104      * Is user an agent?
105      * @returns {boolean} if user has role of agent
106      */
107     hasAgentRole: function () {
108         this.isLoaded();
109         return this.hasRole("Agent");
110     },
111 
112     /**
113      * Is user a supervisor?
114      * @returns {boolean} if user has role of supervisor
115      */
116     hasSupervisorRole: function () {
117         this.isLoaded();
118         return this.hasRole("Supervisor");
119     },
120 
121     /**
122      * Checks to see if user has "theRole"
123      * @returns {boolean}
124      */
125     hasRole: function (theRole) {
126         this.isLoaded();
127         var result = false, i, roles, len;
128 
129         roles = this.getData().roles.role;
130         len = roles.length;
131         if (typeof roles === 'string') {
132             if (roles === theRole) {
133                 result = true;
134             }
135         } else {
136             for (i = 0; i < len ; i = i + 1) {
137                 if (roles[i] === theRole) {
138                     result = true;
139                     break;
140                 }
141             }
142         }
143 
144         return result;
145     },
146 
147     /**
148      * Getter for the state of this User
149      * @returns {String}
150      *     The current (or last fetched) state of this User
151      */
152     getState: function () {
153         this.isLoaded();
154         return this.getData().state;
155     },
156 
157     /**
158      * Getter for a Dialogs collection object that is associated with User.
159      * @returns {Dialogs}
160      *     A Dialogs collection object.
161      */
162     getDialogs: function (callbacks) {
163         var options = callbacks || {};
164         options.parentObj = this;
165         this.isLoaded();
166 
167         if (this._dialogs === null) {
168             this._dialogs = new finesse.restservices.Dialogs(options);
169         }
170 
171         return this._dialogs;
172     },
173 	
174     /**
175      * Getter for a WrapUpReasons collection object that is associated with User.
176      * @param handlers
177      * @returns {WrapUpReasons}
178      *     A WrapUpReasons collection object.
179      */
180     getWrapUpReasons: function (callbacks) {
181         var options = callbacks || {};
182         options.parentObj = this;
183         this.isLoaded();
184 
185         if (this._wrapUpReasons === null) {
186             this._wrapUpReasons = new finesse.restservices.WrapUpReasons(options);
187         }
188 
189         return this._wrapUpReasons;
190     },
191 
192     /**
193      * Getter for a MediaPropertiesLayout object that is associated with User.
194      * @returns {finesse.restservices.MediaPropertiesLayout}
195      *     The MediaPropertiesLayout object associated with this user
196      */
197     getMediaPropertiesLayout: function (callbacks) {
198         var options = callbacks || {};
199         options.parentObj = this;
200 		options.id = this._id;
201 		
202         this.isLoaded();
203         if (this._mediaPropertiesLayout === null) {
204             this._mediaPropertiesLayout = new finesse.restservices.MediaPropertiesLayout(options);
205         }
206         return this._mediaPropertiesLayout;
207     },
208 
209     /**
210      * Getter for the supervised Teams this User (Supervisor) supervises, if any
211      * @returns {Array}
212      *     An array of teams supervised by this User (Supervisor)
213      */
214     getSupervisedTeams: function () {
215         this.isLoaded();
216 
217         try {
218             return finesse.utilities.Utilities.getArray(this.getData().teams.Team);
219         } catch (e) {
220             return [];
221         }
222 
223     },
224 
225     /**
226      * Perform an agent login for this user, associating him with the
227      * specified extension.
228      * @param {String} extension
229      *     The extension to associate with this user
230      * @param {Object} handlers
231      *     An object containing the following (optional) handlers for the request:<ul>
232      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
233      *         response object as its only parameter:<ul>
234      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
235      *             <li><b>content:</b> {String} Raw string of response</li>
236      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
237      *         <li>A error callback function for an unsuccessful request to be invoked with the
238      *         error response object as its only parameter:<ul>
239      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
240      *             <li><b>content:</b> {String} Raw string of response</li>
241      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
242      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
243      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
244      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
245      *             </ul></li>
246      *         </ul>
247      * @returns {finesse.restservices.User}
248      *     This User object to allow cascading
249      */
250     login: function (extension, handlers) {
251         this.isLoaded();
252 
253         var contentBody = {};
254         contentBody[this.getRestType()] = {
255             "state": finesse.restservices.User.States.LOGIN,
256             "extension": extension
257         };
258 
259         // Protect against null dereferencing of options allowing its (nonexistant) keys to be read as undefined
260         handlers = handlers || {};
261 
262         this.restRequest(this.getRestUrl(), {
263             method: 'PUT',
264             success: handlers.success,
265             error: handlers.error,
266             content: contentBody
267         });
268 
269         return this; // Allow cascading
270     },
271 
272     /**
273      * Perform an agent logout for this user.
274      * @param {String} reasonCode
275      *     The reason this user is logging out.  Pass null for no reason.
276      * @param {Object} handlers
277      *     An object containing the following (optional) handlers for the request:<ul>
278      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
279      *         response object as its only parameter:<ul>
280      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
281      *             <li><b>content:</b> {String} Raw string of response</li>
282      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
283      *         <li>A error callback function for an unsuccessful request to be invoked with the
284      *         error response object as its only parameter:<ul>
285      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
286      *             <li><b>content:</b> {String} Raw string of response</li>
287      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
288      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
289      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
290      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
291      *             </ul></li>
292      *         </ul>
293      * @returns {finesse.restservices.User}
294      *     This User object to allow cascading
295      */
296     logout: function (reasonCode, handlers) {
297         return this.setState("LOGOUT", reasonCode, handlers);
298     },
299 
300     /**
301      * Set the state of the user.
302      * @param {String} newState
303      *     The state you are setting
304      * @param {ReasonCode} reasonCode
305      *     The reason this user is logging out.  Pass null for no reason.
306      * @param {Object} handlers
307      *     An object containing the following (optional) handlers for the request:<ul>
308      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
309      *         response object as its only parameter:<ul>
310      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
311      *             <li><b>content:</b> {String} Raw string of response</li>
312      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
313      *         <li>A error callback function for an unsuccessful request to be invoked with the
314      *         error response object as its only parameter:<ul>
315      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
316      *             <li><b>content:</b> {String} Raw string of response</li>
317      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
318      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
319      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
320      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
321      *             </ul></li>
322      *         </ul>
323      * @returns {finesse.restservices.User}
324      *     This User object to allow cascading
325      */
326     setState: function (newState, reasonCode, handlers) {
327         this.isLoaded();
328 
329         var options, contentBody = {};
330 
331         if (!reasonCode) {
332             contentBody[this.getRestType()] = {
333                 "state": newState
334             };
335         } else {
336             contentBody[this.getRestType()] = {
337                 "state": newState,
338                 "reasonCodeId": reasonCode.id
339             };
340         }
341 
342         // Protect against null dereferencing of options allowing its (nonexistant) keys to be read as undefined
343         handlers = handlers || {};
344 
345         options = {
346             method: 'PUT',
347             success: handlers.success,
348             error: handlers.error,
349             content: contentBody
350         };
351 
352 		// After removing the selective 202 handling, we should be able to just use restRequest
353         this.restRequest(this.getRestUrl(), options);
354 
355         return this; // Allow cascading
356     },
357 
358     /**
359      * Make call to a particular phone number.
360      *
361      * @param {String} The number to call
362      * @param {Object} handlers
363      *     An object containing the following (optional) handlers for the request:<ul>
364      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
365      *         response object as its only parameter:<ul>
366      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
367      *             <li><b>content:</b> {String} Raw string of response</li>
368      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
369      *         <li>A error callback function for an unsuccessful request to be invoked with the
370      *         error response object as its only parameter:<ul>
371      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
372      *             <li><b>content:</b> {String} Raw string of response</li>
373      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
374      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
375      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
376      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
377      *             </ul></li>
378      *         </ul>
379      */
380     makeCall: function (number, handlers) {
381         this.isLoaded();
382 
383         this.getDialogs().createNewCallDialog(number, this.getExtension(), handlers);
384 
385         return this; // Allow cascading
386     },
387 
388     /**
389      * Make a silent monitor call to a particular agent's phone number.
390      *
391      * @param number to call
392      * @param {Object} handlers
393      *     An object containing the following (optional) handlers for the request:<ul>
394      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
395      *         response object as its only parameter:<ul>
396      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
397      *             <li><b>content:</b> {String} Raw string of response</li>
398      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
399      *         <li>A error callback function for an unsuccessful request to be invoked with the
400      *         error response object as its only parameter:<ul>
401      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
402      *             <li><b>content:</b> {String} Raw string of response</li>
403      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
404      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
405      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
406      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
407      *             </ul></li>
408      *         </ul>
409      */
410     makeSMCall: function (number, handlers) {
411         this.isLoaded();
412 
413         var actionType = "SILENT_MONITOR";
414 
415         this.getDialogs().createNewSuperviseCallDialog(number, this.getExtension(), actionType, handlers);
416 
417         return this; // Allow cascading
418     },
419 
420     /**
421      * Returns true if the user is in the HOLD or TALKING state.
422      */
423     isOnHoldOrTalking: function () {
424         var state = this.getState();
425         return state && ((state === finesse.restservices.User.States.TALKING) || (state === finesse.restservices.User.States.HOLD));
426     },
427 
428     /**
429      * Parses a uriString to retrieve the id portion
430      * @param {String} uriString
431      * @return {String} id
432      */
433     _parseIdFromUriString : function (uriString) {
434         return finesse.utilities.Utilities.getId(uriString);
435     },
436 
437     /**
438      * Gets the user's Not Ready reason code.
439      * @return undefined if not set or indeterminate
440      */
441     getNotReadyReasonCodeId : function () {
442         this.isLoaded();
443 
444         var reasoncodeIdResult, finesseServerReasonCodeId;
445         finesseServerReasonCodeId = this.getData().reasonCodeId;
446 
447         //FinesseServer will give "-l" => we will set to undefined (for convenience)
448         if (finesseServerReasonCodeId !== "-1") {
449             reasoncodeIdResult = finesseServerReasonCodeId;
450         }
451 
452         return reasoncodeIdResult;
453     },
454 
455     /**
456      * Performs a GET against the Finesse server looking up the reasonCodeId specified.
457      * @param {Object} handlers
458      *     An object containing the following (optional) handlers for the request:<ul>
459      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
460      *         response object as its only parameter:<ul>
461      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
462      *             <li><b>content:</b> {String} Raw string of response</li>
463      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
464      *         <li>A error callback function for an unsuccessful request to be invoked with the
465      *         error response object as its only parameter:<ul>
466      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
467      *             <li><b>content:</b> {String} Raw string of response</li>
468      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
469      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
470      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
471      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
472      *             </ul></li>
473      *         </ul>
474      * @param {String} reasonCodeId is the id for the reason code to lookup
475      */
476     getReasonCodeById : function (handlers, reasonCodeId)
477     {
478         var self = this, contentBody, reasonCode, url;
479         contentBody = {};
480 
481         url = this.getRestUrl() + "/ReasonCode/" + reasonCodeId;
482         this.restRequest(url, {
483             method: 'GET',
484             success: function (rsp) {
485                 reasonCode = {
486                     uri: rsp.object.ReasonCode.uri,
487                     label: rsp.object.ReasonCode.label,
488                     id: self._parseIdFromUriString(rsp.object.ReasonCode.uri)
489                 };
490                 handlers.success(reasonCode);
491             },
492             error: function (rsp) {
493                 handlers.error(rsp);
494             },
495             content: contentBody
496         });
497     },
498 
499 	/**
500      * Performs a GET against Finesse server retrieving all the specified type of reason codes.
501      * @param {String} type (LOGOUT or NOT_READY)
502      * @param {Object} handlers
503      *     An object containing the following (optional) handlers for the request:<ul>
504      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
505      *         response object as its only parameter:<ul>
506      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
507      *             <li><b>content:</b> {String} Raw string of response</li>
508      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
509      *         <li>A error callback function for an unsuccessful request to be invoked with the
510      *         error response object as its only parameter:<ul>
511      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
512      *             <li><b>content:</b> {String} Raw string of response</li>
513      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
514      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
515      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
516      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
517      *             </ul></li>
518      *         </ul>
519      */
520     _getReasonCodesByType : function (type, handlers)
521     {
522         var self = this, contentBody = {}, result = [], url, reasonCodes, i, reasonCodeArray;
523 
524         url = this.getRestUrl() + "/ReasonCodes?category=" + type;
525         this.restRequest(url, {
526             method: 'GET',
527             success: function (rsp) {
528                 reasonCodes = [];
529 
530                 reasonCodeArray = rsp.object.ReasonCodes.ReasonCode;
531                 if (reasonCodeArray === undefined) {
532                     reasonCodes = undefined;
533                 } else if (reasonCodeArray[0] !== undefined) {
534                     for (i = 0; i < reasonCodeArray.length; i = i + 1) {
535                         reasonCodes[i] = {
536                             label: rsp.object.ReasonCodes.ReasonCode[i].label,
537                             id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode[i].uri)
538                         };
539                     }
540                 } else {
541                     reasonCodes[0] = {
542                         label: rsp.object.ReasonCodes.ReasonCode.label,
543                         id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode.uri)
544                     };
545                 }
546                 handlers.success(reasonCodes);
547             },
548             error: function (rsp) {
549                 handlers.error(rsp);
550             },
551             content: contentBody
552         });
553     },
554 	
555     /**
556      * Performs a GET against Finesse server retrieving all the Signout reason codes.
557      * @param {Object} handlers
558      *     An object containing the following (optional) handlers for the request:<ul>
559      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
560      *         response object as its only parameter:<ul>
561      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
562      *             <li><b>content:</b> {String} Raw string of response</li>
563      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
564      *         <li>A error callback function for an unsuccessful request to be invoked with the
565      *         error response object as its only parameter:<ul>
566      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
567      *             <li><b>content:</b> {String} Raw string of response</li>
568      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
569      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
570      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
571      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
572      *             </ul></li>
573      *         </ul>
574      */
575     getSignoutReasonCodes : function (handlers)
576     {
577         this._getReasonCodesByType("LOGOUT", handlers);
578     },
579 
580     /**
581      * Performs a GET against Finesse server retrieving all the Not Ready reason codes.
582      * @param {Object} handlers
583      *     An object containing the following (optional) handlers for the request:<ul>
584      *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
585      *         response object as its only parameter:<ul>
586      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
587      *             <li><b>content:</b> {String} Raw string of response</li>
588      *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
589      *         <li>A error callback function for an unsuccessful request to be invoked with the
590      *         error response object as its only parameter:<ul>
591      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
592      *             <li><b>content:</b> {String} Raw string of response</li>
593      *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
594      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
595      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
596      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
597      *             </ul></li>
598      *         </ul>
599      */
600     getNotReadyReasonCodes : function (handlers)
601     {
602 		this._getReasonCodesByType("NOT_READY", handlers);
603     }
604 
605 });
606 
607 /**
608  * State constants
609  */
610 finesse.restservices.User.States = {
611     LOGIN: "LOGIN",
612     LOGOUT: "LOGOUT",
613     NOT_READY: "NOT_READY",
614     READY: "READY",
615     RESERVED: "RESERVED",
616     TALKING: "TALKING",
617     HOLD: "HOLD",
618     WORK: "WORK",
619     WORK_READY: "WORK_READY"
620 };
621