1 /**
  2  * Copyright (C) 2005-2010 Alfresco Software Limited.
  3  *
  4  * This file is part of Alfresco
  5  *
  6  * Alfresco is free software: you can redistribute it and/or modify
  7  * it under the terms of the GNU Lesser General Public License as published by
  8  * the Free Software Foundation, either version 3 of the License, or
  9  * (at your option) any later version.
 10  *
 11  * Alfresco is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14  * GNU Lesser General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU Lesser General Public License
 17  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 18  */
 19 
 20 /**
 21  * ConsoleUsers tool component.
 22  * 
 23  * @namespace Alfresco
 24  * @class Alfresco.ConsoleUsers
 25  */
 26 (function()
 27 {
 28    /**
 29     * YUI Library aliases
 30     */
 31    var Dom = YAHOO.util.Dom,
 32        Event = YAHOO.util.Event,
 33        Element = YAHOO.util.Element;
 34    
 35    /**
 36     * Alfresco Slingshot aliases
 37     */
 38    var $html = Alfresco.util.encodeHTML;
 39 
 40    /**
 41     * ConsoleUsers constructor.
 42     * 
 43     * @param {String} htmlId The HTML id ´┐Żof the parent element
 44     * @return {Alfresco.ConsoleUsers} The new ConsoleUsers instance
 45     * @constructor
 46     */
 47    Alfresco.ConsoleUsers = function(htmlId)
 48    {
 49       this.name = "Alfresco.ConsoleUsers";
 50       Alfresco.ConsoleUsers.superclass.constructor.call(this, htmlId);
 51 
 52       /* Register this component */
 53       Alfresco.util.ComponentManager.register(this);
 54       
 55       /* Load YUI Components */
 56       Alfresco.util.YUILoaderHelper.require(["button", "container", "datasource", "datatable", "json", "history"], this.onComponentsLoaded, this);
 57       
 58       /* Decoupled event listeners */
 59       YAHOO.Bubbling.on("viewUserClick", this.onViewUserClick, this);
 60 
 61       /* Define panel handlers */
 62       var parent = this;
 63       
 64       // NOTE: the panel registered first is considered the "default" view and is displayed first
 65       
 66       /* Search Panel Handler */
 67       SearchPanelHandler = function SearchPanelHandler_constructor()
 68       {
 69          SearchPanelHandler.superclass.constructor.call(this, "search");
 70       };
 71       
 72       YAHOO.extend(SearchPanelHandler, Alfresco.ConsolePanelHandler,
 73       {
 74 
 75          /**
 76           * INSTANCE VARIABLES
 77           */
 78 
 79          /**
 80           * Keeps track if this panel is searching or not
 81           *
 82           * @property isSearching
 83           * @type Boolean
 84           */
 85          isSearching: false,
 86 
 87 
 88          /**
 89           * PANEL LIFECYCLE CALLBACKS
 90           */
 91 
 92          /**
 93           * Called by the ConsolePanelHandler when this panel shall be loaded
 94           *
 95           * @method onLoad
 96           */
 97          onLoad: function onLoad()
 98          {
 99             // Buttons
100             parent.widgets.searchButton = Alfresco.util.createYUIButton(parent, "search-button", parent.onSearchClick);
101             parent.widgets.newuserButton = Alfresco.util.createYUIButton(parent, "newuser-button", parent.onNewUserClick);
102             parent.widgets.uploadUsersButton = Alfresco.util.createYUIButton(parent, "uploadusers-button", parent.onUploadUsersClick);
103             
104             var newuserSuccess = function(res)
105             {
106                if (!res.json.data.creationAllowed)
107                {
108                   parent.widgets.newuserButton.set("disabled", true);
109                }
110             };
111             
112             // make an ajax call to get authentication mutability - "creationAllowed" will be returned as true
113             // in the response if the administrator is able to create new users on the alfresco server 
114             Alfresco.util.Ajax.jsonGet(
115             {
116                url: Alfresco.constants.PROXY_URI + "api/authentication",
117                successCallback:
118                {
119                   fn: newuserSuccess,
120                   scope: this
121                },
122                failureMessage: parent._msg("message.authenticationdetails-failure", $html(parent.group))
123             });
124             
125             // DataTable and DataSource setup
126             parent.widgets.dataSource = new YAHOO.util.DataSource(Alfresco.constants.PROXY_URI + "api/people",
127             {
128                responseType: YAHOO.util.DataSource.TYPE_JSON,
129                responseSchema:
130                {
131                   resultsList: "people",
132                   metaFields:
133                   {
134                      recordOffset: "startIndex",
135                      totalRecords: "totalRecords"
136                   }
137                }
138             });
139             
140             var me = this;
141             
142             // Work to be performed after data has been queried but before display by the DataTable
143             parent.widgets.dataSource.doBeforeParseData = function PeopleFinder_doBeforeParseData(oRequest, oFullResponse)
144             {
145                var updatedResponse = oFullResponse;
146                
147                if (oFullResponse)
148                {
149                   var items = oFullResponse.people;
150                   
151                   // remove GUEST(s)
152                   for (var i = 0; i < items.length; i++)
153                   {
154                       if (items[i].userName == "guest" || items[i].userName.indexOf("guest&") == 0)
155                       {
156                          items.splice(i, 1);
157                       }
158                   }
159                   
160                   // initial sort by username field
161                   items.sort(function(a, b)
162                   {
163                      return (a.userName > b.userName);
164                   });
165                   
166                   // we need to wrap the array inside a JSON object so the DataTable gets the object it expects
167                   updatedResponse =
168                   {
169                      "people": items
170                   };
171                }
172                
173                // update Results Bar message with number of results found
174                if (items.length < parent.options.maxSearchResults)
175                {
176                   me._setResultsMessage("message.results", $html(parent.searchTerm), items.length);
177                }
178                else
179                {
180                   me._setResultsMessage("message.maxresults", parent.options.maxSearchResults);
181                }
182                
183                return updatedResponse;
184             };
185             
186             // Setup the main datatable
187             this._setupDataTable();
188             
189             // register the "enter" event on the search text field
190             var searchText = Dom.get(parent.id + "-search-text");
191             
192             new YAHOO.util.KeyListener(searchText,
193             {
194                keys: YAHOO.util.KeyListener.KEY.ENTER
195             },
196             {
197                fn: function() 
198                {
199                   parent.onSearchClick();
200                },
201                scope: parent,
202                correctScope: true
203             }, "keydown").enable();
204          },
205          
206          onShow: function onShow()
207          {
208             Dom.get(parent.id + "-search-text").focus();
209          },
210          
211          onUpdate: function onUpdate()
212          {
213             // update the text field - as this event could come from bookmark, navigation or a search button click
214             var searchTermElem = Dom.get(parent.id + "-search-text");
215             searchTermElem.value = parent.searchTerm;
216             
217             // check search length again as we may have got here via history navigation
218             if (!this.isSearching && parent.searchTerm !== undefined && parent.searchTerm.length >= parent.options.minSearchTermLength)
219             {
220                this.isSearching = true;
221 
222                var me = this;
223                
224                // Reset the custom error messages
225                me._setDefaultDataTableErrors(parent.widgets.dataTable);
226                
227                // Don't display any message
228                parent.widgets.dataTable.set("MSG_EMPTY", parent._msg("message.searching"));
229                
230                // Empty results table
231                parent.widgets.dataTable.deleteRows(0, parent.widgets.dataTable.getRecordSet().getLength());
232                
233                var successHandler = function ConsoleUsers__ps_successHandler(sRequest, oResponse, oPayload)
234                {
235                   me._enableSearchUI();                  
236                   me._setDefaultDataTableErrors(parent.widgets.dataTable);
237                   parent.widgets.dataTable.onDataReturnInitializeTable.call(parent.widgets.dataTable, sRequest, oResponse, oPayload);
238                };
239                
240                var failureHandler = function ConsoleUsers__ps_failureHandler(sRequest, oResponse)
241                {
242                   me._enableSearchUI();
243                   if (oResponse.status == 401)
244                   {
245                      // Our session has likely timed-out, so refresh to offer the login page
246                      window.location.reload();
247                   }
248                   else
249                   {
250                      try
251                      {
252                         var response = YAHOO.lang.JSON.parse(oResponse.responseText);
253                         parent.widgets.dataTable.set("MSG_ERROR", response.message);
254                         parent.widgets.dataTable.showTableMessage(response.message, YAHOO.widget.DataTable.CLASS_ERROR);
255                         me._setResultsMessage("message.noresults");
256                      }
257                      catch(e)
258                      {
259                         me._setDefaultDataTableErrors(parent.widgets.dataTable);
260                      }
261                   }
262                };
263 
264                // Send the query to the server
265                parent.widgets.dataSource.sendRequest(me._buildSearchParams(parent.searchTerm),
266                {
267                   success: successHandler,
268                   failure: failureHandler,
269                   scope: parent
270                });
271                me._setResultsMessage("message.searchingFor", $html(parent.searchTerm));
272 
273                // Disable search button and display a wait feedback message if the users hasn't been found yet
274                parent.widgets.searchButton.set("disabled", true);
275                YAHOO.lang.later(2000, me, function(){
276                   if (me.isSearching)
277                   {
278                      if (!me.widgets.feedbackMessage)
279                      {
280                       me.widgets.feedbackMessage = Alfresco.util.PopupManager.displayMessage(
281                       {
282                          text: Alfresco.util.message("message.searching", parent.name),
283                          spanClass: "wait",
284                          displayTime: 0
285                       });
286                      }
287                      else if (!me.widgets.feedbackMessage.cfg.getProperty("visible"))
288                      {
289                       me.widgets.feedbackMessage.show();
290                      }
291                   }
292                }, []);
293             }
294          },
295 
296          /**
297           * Enable search button, hide the pending wait message and set the panel as not searching.
298           *
299           * @method _enableSearchUI
300           * @private
301           */
302          _enableSearchUI: function _enableSearchUI()
303          {
304             // Enable search button and close the wait feedback message if present
305             if (this.widgets.feedbackMessage && this.widgets.feedbackMessage.cfg.getProperty("visible"))
306             {
307                this.widgets.feedbackMessage.hide();
308             }
309             parent.widgets.searchButton.set("disabled", false);
310             this.isSearching = false;
311          },
312 
313          /**
314           * Setup the YUI DataTable with custom renderers.
315           *
316           * @method _setupDataTable
317           * @private
318           */
319          _setupDataTable: function _setupDataTable()
320          {
321             /**
322              * DataTable Cell Renderers
323              *
324              * Each cell has a custom renderer defined as a custom function. See YUI documentation for details.
325              * These MUST be inline in order to have access to the parent instance (via the "parent" variable).
326              */
327             
328             /**
329              * User avatar custom datacell formatter
330              *
331              * @method renderCellAvatar
332              */
333             var renderCellAvatar = function renderCellAvatar(elCell, oRecord, oColumn, oData)
334             {
335                Dom.setStyle(elCell, "min-height", "64px");
336                Dom.setStyle(elCell.parentNode, "width", oColumn.width + "px");
337                Dom.setStyle(elCell.parentNode, "border-right", "1px solid #D7D7D7");
338                
339                // apply the avatar image as a background
340                var avatarUrl = Alfresco.constants.URL_RESCONTEXT + "components/images/no-user-photo-64.png";
341                if (oRecord.getData("avatar") !== undefined)
342                {
343                   avatarUrl = Alfresco.constants.PROXY_URI + oRecord.getData("avatar") + "?c=queue&ph=true";
344                }
345                Dom.setStyle(elCell, "background-image", "url('" + avatarUrl + "')");
346                Dom.setStyle(elCell, "background-repeat", "no-repeat");
347                Dom.setStyle(elCell, "background-position", "22px 50%");
348                
349                // overlay the account enabled/disabled indicator image
350                var enabled = (oRecord.getData("enabled") ? 'enabled' : 'disabled');
351                elCell.innerHTML = '<img class="indicator" alt="' + parent._msg("label." + enabled) + '" src="' + Alfresco.constants.URL_RESCONTEXT + 'components/images/account_' + enabled + '.gif" alt="" />';
352             };
353             
354             /**
355              * User full name custom datacell formatter
356              *
357              * @method renderCellFullName
358              */
359             var renderCellFullName = function renderCellFullName(elCell, oRecord, oColumn, oData)
360             {
361                // Create view userlink
362                var firstName = oRecord.getData("firstName"),
363                   lastName = oRecord.getData("lastName"),
364                   name = firstName + ' ' + (lastName ? lastName : ""),
365                   viewUserLink = document.createElement("a");
366                viewUserLink.innerHTML = $html(name);
367 
368                // fire the 'viewUserClick' event when the selected user in the list has changed
369                YAHOO.util.Event.addListener(viewUserLink, "click", function(e)
370                {
371                   YAHOO.Bubbling.fire('viewUserClick',
372                   {
373                      username: oRecord.getData("userName")
374                   });
375                }, null, parent);
376                elCell.appendChild(viewUserLink);
377             };
378             
379             /**
380              * Quota custom datacell formatter
381              *
382              * @method renderCellQuota
383              */
384             var renderCellQuota = function renderCellQuota(elCell, oRecord, oColumn, oData)
385             {
386                var quota = oRecord.getData("quota");
387                var display = (quota !== -1 ? Alfresco.util.formatFileSize(quota) : "");
388                elCell.innerHTML = display;
389             };
390             
391             /**
392              * Usage custom datacell formatter
393              *
394              * @method renderCellUsage
395              */
396             var renderCellUsage = function renderCellQuota(elCell, oRecord, oColumn, oData)
397             {
398                elCell.innerHTML = Alfresco.util.formatFileSize(oRecord.getData("sizeCurrent"));
399             };
400             
401             /**
402              * Generic HTML-safe custom datacell formatter
403              */
404             var renderCellSafeHTML = function renderCellSafeHTML(elCell, oRecord, oColumn, oData)
405             {
406                elCell.innerHTML = $html(oData);
407             };
408             
409             /**
410              * Usage custom datacell sorter
411              */
412             var sortCellUsage = function sortCellUsage(a, b, desc)
413             {
414                var numA = a.getData("sizeCurrent"),
415                    numB = b.getData("sizeCurrent");
416                
417                if (desc)
418                {
419                   return (numA < numB ? 1 : (numA > numB ? -1 : 0));
420                }
421                return (numA < numB ? -1 : (numA > numB ? 1 : 0));
422             };
423             
424             /**
425              * Quota custom datacell sorter
426              */
427             var sortCellQuota = function sortCellQuota(a, b, desc)
428             {
429                var numA = a.getData("quota"),
430                    numB = b.getData("quota");
431                
432                if (desc)
433                {
434                   return (numA < numB ? 1 : (numA > numB ? -1 : 0));
435                }
436                return (numA < numB ? -1 : (numA > numB ? 1 : 0));
437             };
438             
439             // DataTable column defintions
440             var columnDefinitions =
441             [
442                { key: "avatar", label: "", sortable: false, formatter: renderCellAvatar, width: 70 },
443                { key: "fullName", label: parent._msg("label.name"), sortable: true, formatter: renderCellFullName },
444                { key: "userName", label: parent._msg("label.username"), sortable: true, formatter: renderCellSafeHTML },
445                { key: "jobtitle", label: parent._msg("label.jobtitle"), sortable: true, formatter: renderCellSafeHTML },
446                { key: "email", label: parent._msg("label.email"), sortable: true, formatter: renderCellSafeHTML },
447                { key: "usage", label: parent._msg("label.usage"), sortable: true, sortOptions: {sortFunction: sortCellUsage}, formatter: renderCellUsage },
448                { key: "quota", label: parent._msg("label.quota"), sortable: true, sortOptions: {sortFunction: sortCellQuota}, formatter: renderCellQuota }
449             ];
450             
451             // DataTable definition
452             parent.widgets.dataTable = new YAHOO.widget.DataTable(parent.id + "-datatable", columnDefinitions, parent.widgets.dataSource,
453             {
454                initialLoad: false,
455                renderLoopSize: 32,
456                sortedBy:
457                {
458                   key: "userName",
459                   dir: "asc"
460                },
461                MSG_EMPTY: parent._msg("message.empty")
462             });
463          },
464          
465          /**
466           * Resets the YUI DataTable errors to our custom messages
467           * NOTE: Scope could be YAHOO.widget.DataTable, so can't use "this"
468           *
469           * @method _setDefaultDataTableErrors
470           * @param dataTable {object} Instance of the DataTable
471           * @private
472           */
473          _setDefaultDataTableErrors: function _setDefaultDataTableErrors(dataTable)
474          {
475             var msg = Alfresco.util.message;
476             dataTable.set("MSG_EMPTY", parent._msg("message.empty", "Alfresco.ConsoleUsers"));
477             dataTable.set("MSG_ERROR", parent._msg("message.error", "Alfresco.ConsoleUsers"));
478          },
479          
480          /**
481           * Build URI parameters for People List JSON data webscript
482           *
483           * @method _buildSearchParams
484           * @param searchTerm {string} User search term
485           * @private
486           */
487          _buildSearchParams: function _buildSearchParams(searchTerm)
488          {
489             return "?filter=" + encodeURIComponent(searchTerm) + "&maxResults=" + parent.options.maxSearchResults;
490          },
491          
492          /**
493           * Set the message in the Results Bar area
494           * 
495           * @method _setResultsMessage
496           * @param messageId {string} The messageId to display
497           * @private
498           */
499          _setResultsMessage: function _setResultsMessage(messageId, arg1, arg2)
500          {
501             var resultsDiv = Dom.get(parent.id + "-search-bar");
502             resultsDiv.innerHTML = parent._msg(messageId, arg1, arg2);
503          }
504       });
505       new SearchPanelHandler();
506       
507       /* View Panel Handler */
508       ViewPanelHandler = function ViewPanelHandler_constructor()
509       {
510          ViewPanelHandler.superclass.constructor.call(this, "view");
511       };
512       
513       YAHOO.extend(ViewPanelHandler, Alfresco.ConsolePanelHandler,
514       {
515          onLoad: function onLoad()
516          {
517             // Buttons
518             parent.widgets.gobackButton = Alfresco.util.createYUIButton(parent, "goback-button", parent.onGoBackClick);
519             parent.widgets.deleteuserButton = Alfresco.util.createYUIButton(parent, "deleteuser-button", parent.onDeleteUserClick);
520             parent.widgets.edituserButton = Alfresco.util.createYUIButton(parent, "edituser-button", parent.onEditUserClick);
521          },
522          
523          onBeforeShow: function onBeforeShow()
524          {
525             // Hide the main panel area before it is displayed - so we don't show
526             // old data to the user before the Update() method paints the results
527             Dom.get(parent.id + "-view-title").innerHTML = "";
528             Dom.setStyle(parent.id + "-view-main", "visibility", "hidden");
529          },
530          
531          onShow: function onShow()
532          {
533             window.scrollTo(0, 0);
534          },
535          
536          onUpdate: function onUpdate()
537          {
538             var success = function(res)
539             {
540                var fnSetter = function(id, val)
541                {
542                   Dom.get(parent.id + id).innerHTML = val ? $html(val) : "";
543                };
544                
545                var person = YAHOO.lang.JSON.parse(res.serverResponse.responseText);
546                
547                // apply avatar image URL
548                var photos = Dom.getElementsByClassName("view-photoimg", "img");
549                for (var i in photos)
550                {
551                   photos[i].src = person.avatar ?
552                         Alfresco.constants.PROXY_URI + person.avatar + "?c=force" :
553                         Alfresco.constants.URL_RESCONTEXT + "components/images/no-user-photo-64.png";
554                }
555                
556                // About section fields
557                var firstName = person.firstName,
558                   lastName = person.lastName,
559                   fullName = firstName + ' ' + (lastName ? lastName : "");
560                fnSetter("-view-title", fullName);
561                fnSetter("-view-name", fullName);
562                fnSetter("-view-jobtitle", person.jobtitle);
563                fnSetter("-view-organization", person.organization);
564                // biography is a special html field
565                var bio = person.persondescription ? person.persondescription : "";
566                Dom.get(parent.id + "-view-bio").innerHTML = bio.replace(/\n/g, "<br/>");
567                
568                // Contact section fields
569                fnSetter("-view-location", person.location);
570                fnSetter("-view-email", person.email);
571                fnSetter("-view-telephone", person.telephone);
572                fnSetter("-view-mobile", person.mobile);
573                fnSetter("-view-skype", person.skype);
574                fnSetter("-view-instantmsg", person.instantmsg);
575                fnSetter("-view-googleusername", person.googleusername);
576                
577                // Company section fields
578                fnSetter("-view-companyname", person.organization);
579                // build the company address up and set manually - encoding each value
580                var addr = "";
581                addr += person.companyaddress1 ? ($html(person.companyaddress1) + "<br/>") : "";
582                addr += person.companyaddress2 ? ($html(person.companyaddress2) + "<br/>") : "";
583                addr += person.companyaddress3 ? ($html(person.companyaddress3) + "<br/>") : "";
584                addr += person.companypostcode ? ($html(person.companypostcode) + "<br/>") : "";
585                Dom.get(parent.id + "-view-companyaddress").innerHTML = addr;
586                fnSetter("-view-companytelephone", person.companytelephone);
587                fnSetter("-view-companyfax", person.companyfax);
588                fnSetter("-view-companyemail", person.companyemail);
589                
590                // More section fields
591                fnSetter("-view-username", parent.currentUserId);
592                fnSetter("-view-enabled", person.enabled ? parent._msg("label.enabled") : parent._msg("label.disabled"));
593                fnSetter("-view-quota", (person.quota !== -1 ? Alfresco.util.formatFileSize(person.quota) : ""));
594                fnSetter("-view-usage", Alfresco.util.formatFileSize(person.sizeCurrent));
595                var fnGroupToString = function()
596                {
597                   return this.displayName;
598                }
599                for (var i = 0, j = person.groups.length; i < j; person.groups[i++].toString = fnGroupToString) {}
600                fnSetter("-view-groups", person.groups.join(", "));
601                
602                // Make main panel area visible
603                Dom.setStyle(parent.id + "-view-main", "visibility", "visible");
604             };
605             
606             // make an ajax call to get user details
607             Alfresco.util.Ajax.request(
608             {
609                url: Alfresco.constants.PROXY_URI + "api/people/" + encodeURIComponent(parent.currentUserId) + "?groups=true",
610                method: Alfresco.util.Ajax.GET,
611                successCallback:
612                {
613                   fn: success,
614                   scope: parent
615                },
616                failureMessage: parent._msg("message.getuser-failure", $html(parent.currentUserId))   
617             });
618          }
619       });
620       new ViewPanelHandler();
621       
622       /* Create User Panel Handler */
623       CreatePanelHandler = function CreatePanelHandler_constructor()
624       {
625          CreatePanelHandler.superclass.constructor.call(this, "create");
626       };
627       
628       YAHOO.extend(CreatePanelHandler, Alfresco.ConsolePanelHandler,
629       {
630          _visible: false,
631          
632          _groups: [],
633          
634          _form: null,
635          
636          onLoad: function onLoad()
637          {
638             // events we are interested in
639             YAHOO.Bubbling.on("itemSelected", this.onGroupSelected, this);
640             YAHOO.Bubbling.on("removeGroupCreate", this.onRemoveGroupCreate, this);
641             
642             // Buttons
643             parent.widgets.createuserOkButton = Alfresco.util.createYUIButton(parent, "createuser-ok-button", parent.onCreateUserOKClick);
644             parent.widgets.createuserAnotherButton = Alfresco.util.createYUIButton(parent, "createuser-another-button", parent.onCreateUserAnotherClick);
645             parent.widgets.createuserCancelButton = Alfresco.util.createYUIButton(parent, "createuser-cancel-button", parent.onCreateUserCancelClick);
646             
647             // Form definition
648             var form = new Alfresco.forms.Form(parent.id + "-create-form");
649             form.setSubmitElements([parent.widgets.createuserOkButton, parent.widgets.createuserAnotherButton]);
650             form.setShowSubmitStateDynamically(true);
651             
652             // Form field validation
653             form.addValidation(parent.id + "-create-firstname", Alfresco.forms.validation.mandatory, null, "keyup");
654             form.addValidation(parent.id + "-create-email", Alfresco.forms.validation.mandatory, null, "keyup");
655             form.addValidation(parent.id + "-create-email", Alfresco.forms.validation.email, null, "keyup");
656             form.addValidation(parent.id + "-create-username", Alfresco.forms.validation.nodeName, null, "keyup");
657             form.addValidation(parent.id + "-create-username", Alfresco.forms.validation.length,
658             {
659                min: parent.options.minUsernameLength,
660                max: 100,
661                crop: true,
662                includeWhitespace: false
663             }, "keyup");
664             form.addValidation(parent.id + "-create-password", Alfresco.forms.validation.length,
665             {
666                min: parent.options.minPasswordLength,
667                max: 100,
668                crop: true
669             }, "keyup");
670             form.addValidation(parent.id + "-create-verifypassword", Alfresco.forms.validation.length,
671             {
672                min: parent.options.minPasswordLength,
673                max: 100,
674                crop: true
675             }, "keyup");
676             
677             // Initialise the form
678             form.init();
679             this._form = form;
680             
681             // Load in the Groups Finder component from the server
682             Alfresco.util.Ajax.request(
683             {
684                url: Alfresco.constants.URL_SERVICECONTEXT + "components/people-finder/group-finder",
685                dataObj:
686                {
687                   htmlid: parent.id + "-create-groupfinder"
688                },
689                successCallback:
690                {
691                   fn: this.onGroupFinderLoaded,
692                   scope: this
693                },
694                failureMessage: "Could not load Group Finder component",
695                execScripts: true
696             });
697          },
698          
699          onGroupFinderLoaded: function onGroupFinderLoaded(res)
700          {
701             // Inject the component from the XHR request into it's placeholder DIV element
702             var finderDiv = Dom.get(parent.id + "-create-groupfinder");
703             finderDiv.innerHTML = res.serverResponse.responseText;
704             
705             // Find the Group Finder by container ID
706             parent.modules.createGroupFinder = Alfresco.util.ComponentManager.get(parent.id + "-create-groupfinder");
707             
708             // Set the correct options for our use
709             parent.modules.createGroupFinder.setOptions(
710             {
711                viewMode: Alfresco.GroupFinder.VIEW_MODE_COMPACT,
712                singleSelectMode: false,
713                wildcardPrefix: false
714             });
715          },
716          
717          /**
718           * Group selected event handler.
719           * This event can be fired from either Groups picker - so we much ensure
720           * the event is for the current panel by checking panel visibility.
721           * 
722           * @method onGroupSelected
723           * @param e {object} DomEvent
724           * @param args {array} Event parameters (depends on event type)
725           */
726          onGroupSelected: function onGroupSelected(e, args)
727          {
728             if (this._visible)
729             {
730                this.addGroup(args[1]);
731             }
732          },
733          
734          /**
735           * Add a group to the list of selected groups
736           *
737           * @method addGroup
738           * @param group {object} Group object
739           */
740          addGroup: function addGroup(group)
741          {
742             var found = false;
743             for (var i=0, j=this._groups.length; i<j; i++)
744             {
745                if (this._groups[i] != null && this._groups[i].itemName === group.itemName)
746                {
747                   found = true;
748                   break;
749                }
750             }
751             
752             if (!found)
753             {
754                this._groups.push(group);
755                
756                var groupDiv = Dom.get(parent.id + "-create-groups");
757                var idx = (this._groups.length - 1);
758                var groupEl = document.createElement("span");
759                groupEl.setAttribute("id", parent.id + "_group" + idx);
760                groupEl.setAttribute("title", parent._msg("label.removegroup"));
761                Dom.addClass(groupEl, "group-item");
762                groupEl.innerHTML = $html(group.displayName);
763                groupDiv.appendChild(groupEl);
764 
765                Alfresco.util.useAsButton(groupEl, function(e, obj)
766                {
767                   // Remove group from ui
768                   YAHOO.Bubbling.fire('removeGroupCreate', { id: obj.idx });
769 
770                   // Tell group finder to deselect the group
771                   YAHOO.Bubbling.fire('itemDeselected', { eventGroup: parent.modules.createGroupFinder, itemName: obj.group.itemName });
772                }, { idx: idx, group: group });
773             }
774          },
775          
776          getGroups: function getGroups()
777          {
778             var groups = [];
779             for (var i=0, j=this._groups.length; i<j; i++)
780             {
781                if (this._groups[i] != null)
782                {
783                   groups.push(this._groups[i].itemName);
784                }
785             }
786             return groups;
787          },
788          
789          /**
790           * Group removed event handler
791           *
792           * @method onRemoveGroupCreate
793           * @param e {object} DomEvent
794           * @param args {array} Event parameters (depends on event type)
795           */
796          onRemoveGroupCreate: function onRemoveGroupCreate(e, args)
797          {
798             var i = args[1].id;
799             var el = Dom.get(parent.id + "_group" + i);
800             el.parentNode.removeChild(el);
801             this._groups[i] = null;
802          },
803          
804          onBeforeShow: function onBeforeShow()
805          {
806             // Hide the main panel area before it is displayed - so we don't show
807             // old data to the user before the onShow() method paints the results
808             Dom.setStyle(parent.id + "-create-main", "visibility", "hidden");
809             
810             this.clear();
811          },
812          
813          clear: function clear()
814          {
815             var fnClearEl = function(id)
816             {
817                Dom.get(parent.id + id).value = "";
818             };
819             
820             // clear data fields
821             fnClearEl("-create-firstname");
822             fnClearEl("-create-lastname");
823             fnClearEl("-create-email");
824             fnClearEl("-create-username");
825             fnClearEl("-create-password");
826             fnClearEl("-create-verifypassword");
827             fnClearEl("-create-quota");
828             Dom.get(parent.id + "-create-disableaccount").checked = false;
829             
830             // reset quota selection drop-down
831             Dom.get(parent.id + "-create-quotatype").value = "gb";
832             
833             // clear selected groups
834             this._groups = [];            
835             Dom.get(parent.id + "-create-groups").innerHTML = "";
836             if (parent.modules.createGroupFinder)
837             {
838                parent.modules.createGroupFinder.clearResults();
839             }
840             if (this._form !== null)
841             {
842                this._form.init();
843             }
844 
845             // Notify group finder that no groups are selected
846             YAHOO.Bubbling.fire("allItemsDeselected",
847             {
848                eventGroup: parent.modules.createGroupFinder
849             });
850 
851          },
852          
853          onShow: function onShow()
854          {
855             this._visible = true;
856             window.scrollTo(0, 0);
857             
858             // Make main panel area visible
859             Dom.setStyle(parent.id + "-create-main", "visibility", "visible");
860             
861             Dom.get(parent.id + "-create-firstname").focus();
862          },
863          
864          onHide: function onHide()
865          {
866             this._visible = false;
867          }
868       });
869       new CreatePanelHandler();
870       
871       /* Update User Panel Handler */
872       UpdatePanelHandler = function UpdatePanelHandler_constructor()
873       {
874          UpdatePanelHandler.superclass.constructor.call(this, "update");
875       };
876       
877       YAHOO.extend(UpdatePanelHandler, Alfresco.ConsolePanelHandler,
878       {
879          _visible: false,
880          
881          _removedGroups: [],
882          _addedGroups: [],
883          _originalGroups: [],
884          _groups: [],
885          _photoReset: false,
886          _form: null,
887          
888          onLoad: function onLoad()
889          {
890             // events we are interested in
891             YAHOO.Bubbling.on("itemSelected", this.onGroupSelected, this);
892             YAHOO.Bubbling.on("removeGroupUpdate", this.onRemoveGroupUpdate, this);
893             
894             // Buttons
895             parent.widgets.updateuserSaveButton = Alfresco.util.createYUIButton(parent, "updateuser-save-button", parent.onUpdateUserOKClick);
896             parent.widgets.updateuserCancelButton = Alfresco.util.createYUIButton(parent, "updateuser-cancel-button", parent.onUpdateUserCancelClick);
897             parent.widgets.updateuserClearPhotoButton = Alfresco.util.createYUIButton(parent, "updateuser-clearphoto-button", parent.onUpdateUserClearPhotoClick);
898             
899             // Form definition
900             var form = new Alfresco.forms.Form(parent.id + "-update-form");
901             form.setSubmitElements(parent.widgets.updateuserSaveButton);
902             form.setShowSubmitStateDynamically(true);
903             
904             // Form field validation
905             form.addValidation(parent.id + "-update-firstname", Alfresco.forms.validation.mandatory, null, "keyup");
906             form.addValidation(parent.id + "-update-email", Alfresco.forms.validation.mandatory, null, "keyup");
907             form.addValidation(parent.id + "-update-email", Alfresco.forms.validation.email, null, "keyup");
908             
909             // Initialise the form
910             form.init();
911             this._form = form;
912             
913             // Load in the Groups Finder component from the server
914             Alfresco.util.Ajax.request(
915             {
916                url: Alfresco.constants.URL_SERVICECONTEXT + "components/people-finder/group-finder",
917                dataObj:
918                {
919                   htmlid: parent.id + "-update-groupfinder"
920                },
921                successCallback:
922                {
923                   fn: this.onGroupFinderLoaded,
924                   scope: this
925                },
926                failureMessage: "Could not load Group Finder component",
927                execScripts: true
928             });
929          },
930          
931          onGroupFinderLoaded: function onGroupFinderLoaded(res)
932          {
933             // Inject the component from the XHR request into it's placeholder DIV element
934             var finderDiv = Dom.get(parent.id + "-update-groupfinder");
935             finderDiv.innerHTML = res.serverResponse.responseText;
936             
937             // Find the Group Finder by container ID
938             parent.modules.updateGroupFinder = Alfresco.util.ComponentManager.get(parent.id + "-update-groupfinder");
939             
940             // Set the correct options for our use
941             parent.modules.updateGroupFinder.setOptions(
942             {
943                viewMode: Alfresco.GroupFinder.VIEW_MODE_COMPACT,
944                singleSelectMode: false,
945                wildcardPrefix: false
946             });
947          },
948          
949          /**
950           * Group selected event handler.
951           * This event can be fired from either Groups picker - so we much ensure
952           * the event is for the current panel by checking panel visibility.
953           * 
954           * @method onGroupSelected
955           * @param e {object} DomEvent
956           * @param args {array} Event parameters (depends on event type)
957           */
958          onGroupSelected: function onGroupSelected(e, args)
959          {
960             if (this._visible)
961             {
962                this.addGroup(args[1]);
963             }
964          },
965          
966          /**
967           * Add a group to the list of selected groups
968           *
969           * @method addGroup
970           * @param group {object} Group object
971           */
972          addGroup: function addGroup(group)
973          {
974             var found = false,
975                i, j;
976             for (i = 0, j = this._groups.length; i < j; i++)
977             {
978                if (this._groups[i] !== null && this._groups[i].itemName === group.itemName)
979                {
980                   found = true;
981                   break;
982                }
983             }
984             
985             if (!found)
986             {
987                this._groups.push(group);
988                
989                var groupDiv = Dom.get(parent.id + "-update-groups"),
990                   idx = (this._groups.length-1),
991                   groupEl = document.createElement("span");
992                groupEl.setAttribute("id", parent.id + "_group" + idx);
993                groupEl.setAttribute("title", parent._msg("label.removegroup"));
994                Dom.addClass(groupEl, "group-item");
995                groupEl.innerHTML = $html(group.displayName);
996                groupDiv.appendChild(groupEl);
997 
998                Alfresco.util.useAsButton(groupEl, function(e, obj)
999                {
1000                   // Remove group from ui
1001                   YAHOO.Bubbling.fire('removeGroupUpdate', { id: obj.idx });
1002 
1003                   // Tell group finder to deselect the group
1004                   YAHOO.Bubbling.fire('itemDeselected', { eventGroup: parent.modules.updateGroupFinder, itemName: obj.group.itemName });
1005                }, { idx: idx, group: group });
1006 
1007 
1008 
1009                // if this group wasn't one of the original list, then add it to the addition list
1010                found = false;
1011                for (i = 0, j = this._originalGroups.length; i < j; i++)
1012                {
1013                   if (this._originalGroups[i].itemName === group.itemName)
1014                   {
1015                      found = true;
1016                      break;
1017                   }
1018                }
1019                if (!found)
1020                {
1021                   this._addedGroups.push(group.itemName);
1022                }
1023             }
1024          },
1025          
1026          /**
1027           * Group removed event handler
1028           *
1029           * @method onRemoveGroupUpdate
1030           * @param e {object} DomEvent
1031           * @param args {array} Event parameters (depends on event type)
1032           */
1033          onRemoveGroupUpdate: function onRemoveGroupUpdate(e, args)
1034          {
1035             var i = args[1].id;
1036             var el = Dom.get(parent.id + "_group" + i);
1037             el.parentNode.removeChild(el);
1038             var group = this._groups[i];
1039             this._groups[i] = null;
1040             
1041             // if this group was one of the original list, then add it to the removed list
1042             for (var i=0, j=this._originalGroups.length; i<j; i++)
1043             {
1044                if (this._originalGroups[i].itemName === group.itemName)
1045                {
1046                   this._removedGroups.push(group.itemName);
1047                   break;
1048                }
1049             }
1050             // also remove from the added groups list
1051             for (var i=0, j=this._addedGroups.length; i<j; i++)
1052             {
1053                if (this._addedGroups[i] === group.itemName)
1054                {
1055                   this._addedGroups.splice(i, 1);
1056                   break;
1057                }
1058             }
1059          },
1060          
1061          getAddedGroups: function getAddedGroups()
1062          {
1063             return this._addedGroups;
1064          },
1065          
1066          getRemovedGroups: function getRemovedGroups()
1067          {
1068             return this._removedGroups;
1069          },
1070          
1071          resetGroups: function resetGroups()
1072          {
1073             this._groups = [];
1074             this._addedGroups = [];
1075             this._removedGroups = [];
1076             Dom.get(parent.id + "-update-groups").innerHTML = "";
1077          },
1078          
1079          setPhotoReset: function setPhotoReset()
1080          {
1081             this._photoReset = true;
1082          },
1083          
1084          getPhotoReset: function getPhotoReset()
1085          {
1086             return this._photoReset;
1087          },
1088          
1089          onBeforeShow: function onBeforeShow()
1090          {
1091             // Hide the main panel area before it is displayed - so we don't show
1092             // old data to the user before the Update() method paints the results
1093             Dom.get(parent.id + "-update-title").innerHTML = "";
1094             Dom.setStyle(parent.id + "-update-main", "visibility", "hidden");
1095          },
1096          
1097          onShow: function onShow()
1098          {
1099             this._visible = true;
1100             window.scrollTo(0, 0);
1101          },
1102           
1103          onHide: function onHide()
1104          {
1105             this._visible = false;
1106          },
1107          
1108          onUpdate: function onUpdate()
1109          {
1110             var me = this;
1111             var success = function(res)
1112             {
1113                var fnSetter = function(id, val)
1114                {
1115                   Dom.get(parent.id + id).value = val;
1116                };
1117                var fnDisabler = function(id, propId, map)
1118                {
1119                   if (map["{http://www.alfresco.org/model/content/1.0}" + propId])
1120                   {
1121                      Dom.get(parent.id + id).setAttribute("disabled", true);
1122                   }
1123                };
1124                
1125                var person = YAHOO.lang.JSON.parse(res.serverResponse.responseText);
1126                
1127                // apply avatar image URL
1128                var photos = Dom.getElementsByClassName("update-photoimg", "img");
1129                for (var i in photos)
1130                {
1131                   photos[i].src = person.avatar ?
1132                         Alfresco.constants.PROXY_URI + person.avatar + "?c=force" :
1133                         Alfresco.constants.URL_RESCONTEXT + "components/images/no-user-photo-64.png";
1134                }
1135                
1136                // About section fields
1137                var firstName = person.firstName,
1138                   lastName = person.lastName,
1139                   fullName = firstName + ' ' + (lastName ? lastName : "");
1140                Dom.get(parent.id + "-update-title").innerHTML = $html(fullName);
1141                fnSetter("-update-firstname", firstName);
1142                fnDisabler("-update-firstname", "firstName", person.immutability);
1143                fnSetter("-update-lastname", lastName);
1144                fnDisabler("-update-lastname", "lastName", person.immutability);
1145                fnSetter("-update-email", person.email);
1146                fnDisabler("-update-email", "email", person.immutability);
1147                if (!person.capabilities.isMutable)
1148                {
1149                   Dom.get(parent.id + "-update-old-password").setAttribute("disabled", true);
1150                   Dom.get(parent.id + "-update-password").setAttribute("disabled", true);
1151                   Dom.get(parent.id + "-update-verifypassword").setAttribute("disabled", true);
1152                }
1153                fnSetter("-update-old-password", "");
1154                fnSetter("-update-password", "");
1155                fnSetter("-update-verifypassword", "");
1156                
1157                // convert quota to closest value type
1158                var quota = person.quota;
1159                if (quota !== -1)
1160                {
1161                   if (quota < Alfresco.util.BYTES_MB)
1162                   {
1163                      // show in kilobytes
1164                      quota = Math.round(quota / Alfresco.util.BYTES_KB);
1165                      Dom.get(parent.id + "-update-quotatype").value = "kb";
1166                   }
1167                   else if (quota < Alfresco.util.BYTES_GB)
1168                   {
1169                      // show in metabytes
1170                      quota = Math.round(quota / Alfresco.util.BYTES_MB);
1171                      Dom.get(parent.id + "-update-quotatype").value = "mb";
1172                   }
1173                   else
1174                   {
1175                      // show in gigabytes
1176                      quota = Math.round(quota / Alfresco.util.BYTES_GB);
1177                      Dom.get(parent.id + "-update-quotatype").value = "gb";
1178                   }
1179                   fnSetter("-update-quota", quota.toString());
1180                }
1181                else
1182                {
1183                   fnSetter("-update-quota", "");
1184                }
1185                
1186                // account enabled/disabled
1187                Dom.get(parent.id + "-update-disableaccount").checked = (person.enabled == false);
1188                
1189                // add groups the user is already assigned to and maintain a copy of the original group list
1190                me.resetGroups();
1191                YAHOO.Bubbling.fire("allItemsDeselected",
1192                {
1193                   eventGroup: parent.modules.updateGroupFinder
1194                });
1195                me._originalGroups = person.groups;
1196                for (var i=0, j=person.groups.length; i<j; i++)
1197                {
1198                   me.addGroup(
1199                   {
1200                      "itemName": person.groups[i].itemName,
1201                      "displayName": person.groups[i].displayName
1202                   });
1203 
1204                   // Make the group finder aware of which groups the user already has
1205                   YAHOO.Bubbling.fire("itemSelected",
1206                   {
1207                      eventGroup: parent.modules.updateGroupFinder,
1208                      "itemName": person.groups[i].itemName,
1209                      "displayName": person.groups[i].displayName
1210                   });
1211                }
1212                
1213                // Hide or show the old password field - only required if user changing own password
1214                if (parent.currentUserId.toLowerCase() === Alfresco.constants.USERNAME.toLowerCase())
1215                {
1216                   Dom.setStyle(parent.id + "-oldpassword-wrapper", "display", "block");
1217                }
1218                else
1219                {
1220                   Dom.setStyle(parent.id + "-oldpassword-wrapper", "display", "none");
1221                }
1222                
1223                // Make main panel area visible
1224                Dom.setStyle(parent.id + "-update-main", "visibility", "visible");
1225 
1226 
1227 
1228 
1229                me._form.updateSubmitElements();
1230             };
1231             
1232             // make an ajax call to get user details
1233             Alfresco.util.Ajax.request(
1234             {
1235                url: Alfresco.constants.PROXY_URI + "api/people/" + encodeURIComponent(parent.currentUserId) + "?groups=true",
1236                method: Alfresco.util.Ajax.GET,
1237                successCallback:
1238                {
1239                   fn: success,
1240                   scope: parent
1241                },
1242                failureMessage: parent._msg("message.getuser-failure", $html(parent.currentUserId))
1243             });
1244          }
1245       });
1246       new UpdatePanelHandler();
1247       
1248       CSVResultsPanelHandler = function CSVResultsPanelHandler_constructor()
1249       {
1250          CSVResultsPanelHandler.superclass.constructor.call(this, "csvresults");
1251       };
1252       
1253       YAHOO.extend(CSVResultsPanelHandler, Alfresco.ConsolePanelHandler,
1254       {
1255          /**
1256           * PANEL LIFECYCLE CALLBACKS
1257           */
1258          /**
1259           * Called by the ConsolePanelHandler when this panel shall be loaded
1260           *
1261           * @method onLoad
1262           */
1263          onLoad: function onLoad()
1264          {
1265             parent.widgets.csvGobackButton = Alfresco.util.createYUIButton(parent, "csv-goback-button", parent.onGoBackClick);
1266          },
1267          
1268          onShow: function onShow()
1269          {
1270             if (parent.csvResults)
1271             {
1272                var dataSource;
1273                var successful = parent.csvResults.successful;
1274                if (successful &&  successful.length > 0 && parent.csvResults.successful[0].response)
1275                {
1276                   successful = successful[0].response;
1277                   
1278                   // If the response contains the "successful" array containing an element then it does not necessarily
1279                   // mean that the CSV upload succeeded. This simply means that the upload request was successfully processed
1280                   // (i.e. the file was received)
1281                   if (successful.data && successful.data.users)
1282                   {
1283                      parent.fileUpload.hide();
1284                      
1285                      // If the successful response contains a data object with a "users" attribute then we at least know that
1286                      // some users have been processed so can construct a result table using that data...
1287                      dataSource = new YAHOO.util.DataSource(successful.data.users);
1288                      dataSource.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
1289                      dataSource.responseSchema = { fields: [ "username", "uploadStatus" ]};
1290                      
1291                      // Show a pop-up with the summary data...
1292                      if (successful.data.addedUsers == 0)
1293                      {
1294                         // No new users were added...
1295                         Alfresco.util.PopupManager.displayMessage(
1296                         {
1297                            text: parent._msg("message.csvupload.failure")
1298                         });
1299                      }
1300                      else if (successful.data.addedUsers == successful.data.totalUsers)
1301                      {
1302                         // All the users found in the CSV file were added
1303                         Alfresco.util.PopupManager.displayMessage(
1304                         {
1305                            text: parent._msg("message.csvupload.success", successful.data.addedUsers)
1306                         });
1307                      }
1308                      else
1309                      {                     
1310                         // Some of the users could not be added.
1311                         var failedUsers = successful.data.totalUsers - successful.data.addedUsers;
1312                         Alfresco.util.PopupManager.displayMessage(
1313                         {
1314                            text: parent._msg("message.csvupload.partialSuccess", successful.data.addedUsers, failedUsers)
1315                         });
1316                      }
1317                      
1318                      var columnDefs = [{key:"username", label: parent._msg("label.username"), sortable: true, resizeable: true},
1319                                        {key:"uploadStatus", label: parent._msg("label.uploadStatus"), sortable: true, resizeable: true}];
1320                      
1321                      var resultsTable = new YAHOO.widget.DataTable(parent.id + "-csvresults-datatable",
1322                                                                    columnDefs,
1323                                                                    dataSource);
1324                      
1325                      Dom.removeClass(parent.id + "-csvresults-success", "hidden");
1326                      Dom.addClass(parent.id + "-csvresults-failure", "hidden");
1327                   }
1328                   else
1329                   {
1330                      parent.fileUpload.hide();
1331                      
1332                      // The CSV upload failed            
1333                      Alfresco.util.PopupManager.displayMessage(
1334                      {
1335                         text: parent._msg("message.csvupload.error")
1336                      });
1337                      
1338                      Dom.get(parent.id + "-csvresults-error").innerHTML = successful.message;
1339                      
1340                      Dom.addClass(parent.id + "-csvresults-success", "hidden");
1341                      Dom.removeClass(parent.id + "-csvresults-failure", "hidden");
1342                   }
1343                   
1344                   
1345                }
1346                else
1347                {
1348                   // The upload did not work.
1349                }
1350             }
1351          }
1352       });
1353       new CSVResultsPanelHandler();
1354       
1355       return this;
1356    };
1357    
1358    YAHOO.extend(Alfresco.ConsoleUsers, Alfresco.ConsoleTool,
1359    {
1360       /**
1361        * Object container for initialization options
1362        *
1363        * @property options
1364        * @type object
1365        */
1366       options:
1367       {
1368          /**
1369           * Number of characters required for a search.
1370           * 
1371           * @property minSearchTermLength
1372           * @type int
1373           * @default 1
1374           */
1375          minSearchTermLength: 1,
1376          
1377          /**
1378           * Maximum number of items to display in the results list
1379           * 
1380           * @property maxSearchResults
1381           * @type int
1382           * @default 100
1383           */
1384          maxSearchResults: 100,
1385          
1386          /**
1387           * Minimum length of a username
1388           * 
1389           * @property minUsernameLength
1390           * @type int
1391           * @default 2
1392           */
1393          minUsernameLength: 2,
1394          
1395          /**
1396           * Minimum length of a password
1397           * 
1398           * @property minPasswordLength
1399           * @type int
1400           * @default 3
1401           */
1402          minPasswordLength: 3
1403       },
1404       
1405       /**
1406        * Current user id for an action.
1407        * 
1408        * @property currentUserId
1409        * @type string
1410        */
1411       currentUserId: "",
1412       
1413       /**
1414        * Current search term, obtained from form input field.
1415        * 
1416        * @property searchTerm
1417        * @type string
1418        */
1419       searchTerm: undefined,
1420       
1421       /**
1422        * The result of the last CSV upload.
1423        * 
1424        * @property csvResults
1425        * @type object
1426        */
1427       csvResults: undefined,
1428       
1429       
1430       /**
1431        * Fired by YUI when parent element is available for scripting.
1432        * Component initialisation, including instantiation of YUI widgets and event listener binding.
1433        *
1434        * @method onReady
1435        */
1436       onReady: function ConsoleUsers_onReady()
1437       {
1438          // Generate the popup dialog for confirmation of deleting a user
1439          this.popups.deleteDialog = Alfresco.util.createYUIPanel("deleteDialog", 
1440          {
1441             width: "36em",
1442             text: '<div class="yui-u" style="text-align:center"><br/>' + this._msg("panel.delete.msg") + '<br/><br/></div>',
1443             buttons: [
1444             {
1445                text: this._msg("button.delete"),
1446                handler:
1447                {
1448                   fn: this.onDeleteUserOK,
1449                   scope: this
1450                }
1451             },
1452             {
1453                text: this._msg("button.cancel"),
1454                handler:
1455                {
1456                   fn: this.onDeleteUserCancel,
1457                   scope: this
1458                },
1459                isDefault: true
1460             }]
1461          },
1462          {
1463             type: YAHOO.widget.SimpleDialog
1464          });
1465          
1466          this.popups.deleteDialog.setHeader(this._msg("panel.delete.header"));
1467          
1468          // Call super-class onReady() method
1469          Alfresco.ConsoleUsers.superclass.onReady.call(this);
1470       },
1471       
1472       /**
1473        * YUI WIDGET EVENT HANDLERS
1474        * Handlers for standard events fired from YUI widgets, e.g. "click"
1475        */
1476       
1477       /**
1478        * History manager state change event handler (override base class)
1479        *
1480        * @method onStateChanged
1481        * @param e {object} DomEvent
1482        * @param args {array} Event parameters (depends on event type)
1483        */
1484       onStateChanged: function ConsoleUsers_onStateChanged(e, args)
1485       {
1486          var state = this.decodeHistoryState(args[1].state);
1487          
1488          // test if panel has actually changed?
1489          if (state.panel)
1490          {
1491             this.showPanel(state.panel);
1492          }
1493          
1494          if (state.search !== undefined && this.currentPanelId === "search")
1495          {
1496             // keep track of the last search performed
1497             var searchTerm = state.search;
1498             this.searchTerm = searchTerm;
1499             
1500             this.updateCurrentPanel();
1501          }
1502          
1503          if (state.userid &&
1504              (this.currentPanelId === "view" ||
1505               this.currentPanelId === "create" ||
1506               this.currentPanelId === "update"))
1507          {
1508             this.currentUserId = state.userid;
1509             
1510             this.updateCurrentPanel();
1511          }
1512       },
1513       
1514       /**
1515        * Search button click event handler
1516        *
1517        * @method onSearchClick
1518        * @param e {object} DomEvent
1519        * @param args {array} Event parameters (depends on event type)
1520        */
1521       onSearchClick: function ConsoleUsers_onSearchClick(e, args)
1522       {
1523          var searchTermElem = Dom.get(this.id + "-search-text");
1524          var searchTerm = YAHOO.lang.trim(searchTermElem.value);
1525          
1526          // inform the user if the search term entered is too small
1527          if (searchTerm.length < this.options.minSearchTermLength)
1528          {
1529             Alfresco.util.PopupManager.displayMessage(
1530             {
1531                text: this._msg("message.minimum-length", this.options.minSearchTermLength)
1532             });
1533             return;
1534          }
1535          
1536          this.refreshUIState({"search": searchTerm});
1537       },
1538       
1539       /**
1540        * Upload Users button click event handler
1541        *
1542        * @method onUploadUsersClick
1543        * @param e {object} DomEvent
1544        * @param args {array} Event parameters (depends on event type)
1545        */
1546       onUploadUsersClick: function ConsoleUsers_onUploadUsersClick(e, args)
1547       {
1548          // Force the use of the HTML (rather than Flash) uploader because there are issues with the
1549          // Flash uploader in these circumstances when Sharepoint is being used. The Flash uploader
1550          // picks up the wrong JSESSIONID cookie which causes the upload to fail.
1551          if (!this.fileUpload)
1552          {
1553             this.fileUpload = Alfresco.util.ComponentManager.findFirst("Alfresco.HtmlUpload") 
1554          }
1555          
1556          // Show uploader for single file select - override the upload URL to use appropriate upload service
1557          var uploadConfig =
1558          {
1559             uploadURL: "api/people/upload.html",
1560             mode: this.fileUpload.MODE_SINGLE_UPLOAD,
1561             onFileUploadComplete:
1562             {
1563                fn: this.onUsersUploadComplete,
1564                scope: this
1565             }
1566          };
1567          
1568          this.fileUpload.show(uploadConfig);
1569          
1570          // Make sure the "use Flash" tip is hidden just in case Flash is enabled...
1571          var singleUploadTip = Dom.get(this.fileUpload.id + "-singleUploadTip-span");
1572          Dom.addClass(singleUploadTip, "hidden");
1573          Event.preventDefault(e);
1574       },
1575       
1576       /**
1577        * Users Upload complete event handler
1578        *
1579        * @method onUsersUploadComplete
1580        * @param complete {object} Object literal containing details of successful upload
1581        */
1582       onUsersUploadComplete: function ConsoleUsers_onUsersUploadComplete(complete)
1583       {
1584          this.csvResults = complete;
1585          this.refreshUIState({"panel": "csvresults"});
1586       },
1587       
1588       /**
1589        * New User button click event handler
1590        *
1591        * @method onNewUserClick
1592        * @param e {object} DomEvent
1593        * @param args {array} Event parameters (depends on event type)
1594        */
1595       onNewUserClick: function ConsoleUsers_onNewUserClick(e, args)
1596       {
1597          this.refreshUIState({"panel": "create"});
1598       },
1599       
1600       /**
1601        * Edit User button click event handler
1602        *
1603        * @method onEditUserClick
1604        * @param e {object} DomEvent
1605        * @param args {array} Event parameters (depends on event type)
1606        */
1607       onEditUserClick: function ConsoleUsers_onEditUserClick(e, args)
1608       {
1609          this.refreshUIState({"panel": "update"});
1610       },
1611       
1612       /**
1613        * View User event handler
1614        *
1615        * @method onViewUserClick
1616        * @param e {object} DomEvent
1617        * @param args {array} Event parameters (depends on event type)
1618        */
1619       onViewUserClick: function ConsoleUsers_onViewUserClick(e, args)
1620       {
1621          var userid = args[1].username;
1622          this.refreshUIState({"panel": "view", "userid": userid});
1623       },
1624 
1625       /**
1626        * Go back button click event handler
1627        *
1628        * @method onGoBackClick
1629        * @param e {object} DomEvent
1630        * @param args {array} Event parameters (depends on event type)
1631        */
1632       onGoBackClick: function ConsoleUsers_onGoBackClick(e, args)
1633       {
1634          this.refreshUIState({"panel": "search"});
1635       },
1636 
1637       /**
1638        * Delete User button click event handler
1639        *
1640        * @method onDeleteUserClick
1641        * @param e {object} DomEvent
1642        * @param args {array} Event parameters (depends on event type)
1643        */
1644       onDeleteUserClick: function ConsoleUsers_onDeleteUserClick(e, args)
1645       {
1646          this.popups.deleteDialog.show();
1647       },
1648       
1649       /**
1650        * Fired when the admin confirms that they want to delete a User.
1651        *
1652        * @method onDeleteUserOK
1653        * @param e {object} DomEvent
1654        */
1655       onDeleteUserOK: function ConsoleUsers_onDeleteUserOK(e)
1656       {
1657          Alfresco.util.Ajax.request(
1658          {
1659             method: Alfresco.util.Ajax.DELETE,
1660             url: Alfresco.constants.PROXY_URI + "api/people/" + encodeURIComponent(this.currentUserId),
1661             successCallback:
1662             {
1663                fn: this.onDeletedUser,
1664                scope: this
1665             },
1666             failureMessage: this._msg("panel.delete.fail")
1667          });
1668       },
1669       
1670       /**
1671        * Fired on successful deletion of a user.
1672        *
1673        * @method onDeletedUser
1674        * @param e {object} DomEvent
1675        */
1676       onDeletedUser: function ConsoleUsers_onDeletedUser(e)
1677       {
1678          // return to the search screen - we can no longer view the user details
1679          this.popups.deleteDialog.hide();
1680          Alfresco.util.PopupManager.displayMessage(
1681          {
1682             text: this._msg("message.delete-success")
1683          });
1684          this.refreshUIState({"panel": "search"});
1685       },
1686       
1687       /**
1688        * Fired when the admin cancels the operation to delete a User.
1689        *
1690        * @method onDeleteUserCancel
1691        * @param e {object} DomEvent
1692        */
1693       onDeleteUserCancel: function ConsoleUsers_onDeleteUserCancel(e)
1694       {
1695          this.popups.deleteDialog.hide();
1696       },
1697       
1698       /**
1699        * Fired when the Create User OK button is clicked.
1700        *
1701        * @method onCreateUserOKClick
1702        * @param e {object} DomEvent
1703        * @param args {array} Event parameters (depends on event type)
1704        */
1705       onCreateUserOKClick: function ConsoleUsers_onCreateUserOKClick(e, args)
1706       {
1707          var handler = function(res)
1708          {
1709             window.scrollTo(0, 0);
1710             Alfresco.util.PopupManager.displayMessage(
1711             {
1712                text: this._msg("message.create-success")
1713             });
1714             this.refreshUIState({"panel": "search"});
1715          };
1716          this._createUser(handler);
1717       },
1718       
1719       /**
1720        * Fired when the Create User and Create Another button is clicked.
1721        *
1722        * @method onCreateUserAnotherClick
1723        * @param e {object} DomEvent
1724        * @param args {array} Event parameters (depends on event type)
1725        */
1726       onCreateUserAnotherClick: function ConsoleUsers_onCreateUserAnotherClick(e, args)
1727       {
1728          var me = this;
1729          var handler = function(res)
1730          {
1731             window.scrollTo(0, 0);
1732             Alfresco.util.PopupManager.displayMessage(
1733             {
1734                text: me._msg("message.create-success")
1735             });
1736             
1737             // clear fields
1738             this._getCurrentPanel().clear();
1739             Dom.get(me.id + "-create-firstname").focus();
1740          };
1741          this._createUser(handler);
1742       },
1743       
1744       /**
1745        * Fired when the Create User Cancel button is clicked.
1746        *
1747        * @method onCreateUserCancelClick
1748        * @param e {object} DomEvent
1749        * @param args {array} Event parameters (depends on event type)
1750        */
1751       onCreateUserCancelClick: function ConsoleUsers_onCreateUserCancelClick(e, args)
1752       {
1753          this.refreshUIState({"panel": "search"});
1754       },
1755       
1756       /**
1757        * Fired when the Update User OK button is clicked.
1758        *
1759        * @method onUpdateUserOKClick
1760        * @param e {object} DomEvent
1761        * @param args {array} Event parameters (depends on event type)
1762        */
1763       onUpdateUserOKClick: function ConsoleUsers_onUpdateUserOKClick(e, args)
1764       {
1765          var me = this;
1766          var handler = function(res)
1767          {
1768             window.scrollTo(0, 0);
1769             Alfresco.util.PopupManager.displayMessage(
1770             {
1771                text: me._msg("message.update-success")
1772             });
1773             me.refreshUIState({"panel": "view"});
1774          };
1775          this._updateUser(handler);
1776       },
1777       
1778       /**
1779        * Fired when the Update User Cancel button is clicked.
1780        *
1781        * @method onUpdateUserCancelClick
1782        * @param e {object} DomEvent
1783        * @param args {array} Event parameters (depends on event type)
1784        */
1785       onUpdateUserCancelClick: function ConsoleUsers_onUpdateUserCancelClick(e, args)
1786       {
1787          this.refreshUIState({"panel": "view"});
1788       },
1789       
1790       /**
1791        * Fired when the Use Default button is clicked to clear user photo.
1792        *
1793        * @method onUpdateUserClearPhotoClick
1794        * @param e {object} DomEvent
1795        * @param args {array} Event parameters (depends on event type)
1796        */
1797       onUpdateUserClearPhotoClick: function ConsoleUsers_onUpdateUserClearPhotoClick(e, args)
1798       {
1799          Dom.get(this.id + "-update-photoimg").src = Alfresco.constants.URL_RESCONTEXT + "components/images/no-user-photo-64.png";
1800          this._getCurrentPanel().setPhotoReset();
1801       },
1802       
1803       /**
1804        * Encode state object into a packed string for use as url history value.
1805        * Override base class.
1806        * 
1807        * @method encodeHistoryState
1808        * @param obj {object} state object
1809        * @private
1810        */
1811       encodeHistoryState: function ConsoleUsers_encodeHistoryState(obj)
1812       {
1813          // wrap up current state values
1814          var stateObj = {};
1815          if (this.currentPanelId !== "")
1816          {
1817             stateObj.panel = this.currentPanelId;
1818          }
1819          if (this.currentUserId !== "")
1820          {
1821             stateObj.userid = this.currentUserId;
1822          }
1823          if (this.searchTerm !== undefined)
1824          {
1825             stateObj.search = this.searchTerm;
1826          }
1827          
1828          // convert to encoded url history state - overwriting with any supplied values
1829          var state = "";
1830          if (obj.panel || stateObj.panel)
1831          {
1832             state += "panel=" + encodeURIComponent(obj.panel ? obj.panel : stateObj.panel);
1833          }
1834          if (obj.userid || stateObj.userid)
1835          {
1836             if (state.length !== 0)
1837             {
1838                state += "&";
1839             }
1840             state += "userid=" + encodeURIComponent(obj.userid ? obj.userid : stateObj.userid);
1841          }
1842          if (obj.search !== undefined || stateObj.search !== undefined)
1843          {
1844             if (state.length !== 0)
1845             {
1846                state += "&";
1847             }
1848             state += "search=" + encodeURIComponent(obj.search !== undefined ? obj.search : stateObj.search);
1849          }
1850          return state;
1851       },
1852       
1853       /**
1854        * PRIVATE FUNCTIONS
1855        */
1856       
1857       /**
1858        * Create a user - returning true on success, false on any error.
1859        * 
1860        * @method _createUser
1861        * @param handler {function} Handler function to be called on successful creation
1862        * @private
1863        */
1864       _createUser: function ConsoleUsers__createUser(handler)
1865       {
1866          // TODO: respect minimum field length for username/password
1867          
1868          var me = this;
1869          var fnGetter = function(id)
1870          {
1871             return YAHOO.lang.trim(Dom.get(me.id + id).value);
1872          };
1873          
1874          // verify password against second field
1875          var password = fnGetter("-create-password");
1876          var verifypw = fnGetter("-create-verifypassword");
1877          if (password !== verifypw)
1878          {
1879             Alfresco.util.PopupManager.displayMessage(
1880             {
1881                text: this._msg("message.password-validate-failure")
1882             });
1883             return;
1884          }
1885          
1886          // gather up the data for our JSON PUT request
1887          var username = fnGetter("-create-username");
1888          var quota = this._calculateQuota(me.id + "-create");
1889          
1890          // gather the selected groups from the panel
1891          var groups = this._getCurrentPanel().getGroups();
1892          
1893          var personObj =
1894          {
1895             userName: username,
1896             password: password,
1897             firstName: fnGetter("-create-firstname"),
1898             lastName: fnGetter("-create-lastname"),
1899             email: fnGetter("-create-email"),
1900             disableAccount: Dom.get(me.id + "-create-disableaccount").checked,
1901             quota: quota,
1902             groups: groups
1903          };
1904          
1905          Alfresco.util.Ajax.request(
1906          {
1907             url: Alfresco.constants.PROXY_URI + "api/people",
1908             method: Alfresco.util.Ajax.POST,
1909             dataObj: personObj,
1910             requestContentType: Alfresco.util.Ajax.JSON,
1911             successCallback:
1912             {
1913                fn: handler,
1914                scope: this
1915             },
1916             failureCallback:
1917             {
1918                fn: function(res)
1919                {
1920                   var json = Alfresco.util.parseJSON(res.serverResponse.responseText);
1921                   if (json.status.code === 409)
1922                   {
1923                      // username already exists
1924                      Alfresco.util.PopupManager.displayPrompt(
1925                      {
1926                         title: this._msg("message.failure"),
1927                         text: this._msg("message.create-user-exists")
1928                      });
1929                   }
1930                   else
1931                   {
1932                      // generic error
1933                      Alfresco.util.PopupManager.displayPrompt(
1934                      {
1935                         title: this._msg("message.failure"),
1936                         text: this._msg("message.create-failure", json.message)
1937                      });
1938                   }
1939                },
1940                scope: this
1941             }
1942          });
1943       },
1944       
1945       /**
1946        * Update a user - returning true on success, false on any error.
1947        * 
1948        * @method _updateUser
1949        * @param handler {function} Handler function to be called on successful update
1950        * @private
1951        */
1952       _updateUser: function ConsoleUsers__updateUser(handler)
1953       {
1954          var me = this;
1955          
1956          var isCurrentUser = (this.currentUserId.toLowerCase() === Alfresco.constants.USERNAME.toLowerCase());
1957          
1958          var fnGetter = function(id)
1959          {
1960             return Dom.get(me.id + id).value;
1961          };
1962          
1963          var updateSuccess = function(res)
1964          {
1965             var completed = function(res)
1966             {
1967                if (YAHOO.lang.trim(fnGetter("-update-password")).length !== 0)
1968                {
1969                   var passwordObj =
1970                   {
1971                      newpw: YAHOO.lang.trim(fnGetter("-update-password"))
1972                   };
1973                   if (isCurrentUser == true)
1974                   {
1975                      passwordObj.oldpw = YAHOO.lang.trim(fnGetter("-update-old-password"));
1976                   }
1977                   
1978                   // update the password for the user
1979                   Alfresco.util.Ajax.request(
1980                   {
1981                      url: Alfresco.constants.PROXY_URI + "api/person/changepassword/" + encodeURIComponent(me.currentUserId),
1982                      method: Alfresco.util.Ajax.POST,
1983                      dataObj: passwordObj,
1984                      requestContentType: Alfresco.util.Ajax.JSON,
1985                      successCallback:
1986                      {
1987                         fn: handler,
1988                         scope: me
1989                      },
1990                      failureMessage: me._msg("message.password-failure")   
1991                   });
1992                }
1993                else
1994                {
1995                   handler.call();
1996                }
1997             };
1998             
1999             if (this._getCurrentPanel().getPhotoReset())
2000             {
2001                Alfresco.util.Ajax.request(
2002                {
2003                   url: Alfresco.constants.PROXY_URI + "slingshot/profile/resetavatar/" + encodeURIComponent(this.currentUserId),
2004                   method: Alfresco.util.Ajax.PUT,
2005                   requestContentType: Alfresco.util.Ajax.JSON,
2006                   successCallback:
2007                   {
2008                      fn: completed,
2009                      scope: this
2010                   },
2011                   failureCallback:
2012                   {
2013                      fn: function(res)
2014                      {
2015                         // generic error
2016                         Alfresco.util.PopupManager.displayPrompt(
2017                         {
2018                            title: this._msg("message.failure"),
2019                            text: this._msg("message.clear-photo-failure")
2020                         });
2021                         completed.call();
2022                      },
2023                      scope: this
2024                   }
2025                });
2026             }
2027             else
2028             {
2029                completed.call();
2030             }
2031          };
2032          
2033          // verify password against second field
2034          var oldPw = fnGetter("-update-old-password");
2035          var password = fnGetter("-update-password");
2036          var verifypw = fnGetter("-update-verifypassword");
2037          if (YAHOO.lang.trim(password).length !== 0)
2038          {
2039             if (isCurrentUser == true && (YAHOO.lang.trim(oldPw).length === 0))
2040             {
2041                Alfresco.util.PopupManager.displayMessage(
2042                {
2043                   text: this._msg("message.password-validate-oldpw")
2044                });
2045                return;
2046             }
2047             if (YAHOO.lang.trim(password).length < this.options.minPasswordLength)
2048             {
2049                Alfresco.util.PopupManager.displayMessage(
2050                {
2051                   text: this._msg("message.password-validate-length", this.options.minPasswordLength)
2052                });
2053                return;
2054             }
2055             if (password !== verifypw)
2056             {
2057                Alfresco.util.PopupManager.displayMessage(
2058                {
2059                   text: this._msg("message.password-validate-failure")
2060                });
2061                return;
2062             }
2063          }
2064          
2065          // gather up the data for our JSON PUT request
2066          var quota = this._calculateQuota(me.id + "-update");
2067          
2068          // gather the groups for addition and groups for removal from the panel
2069          var addGroups = this._getCurrentPanel().getAddedGroups();
2070          var removeGroups = this._getCurrentPanel().getRemovedGroups();
2071          
2072          var personObj =
2073          {
2074             firstName: fnGetter("-update-firstname"),
2075             lastName: fnGetter("-update-lastname"),
2076             email: fnGetter("-update-email"),
2077             disableAccount: Dom.get(me.id + "-update-disableaccount").checked,
2078             quota: quota,
2079             addGroups: addGroups,
2080             removeGroups: removeGroups
2081          };
2082          
2083          Alfresco.util.Ajax.request(
2084          {
2085             url: Alfresco.constants.PROXY_URI + "api/people/" + encodeURIComponent(this.currentUserId),
2086             method: Alfresco.util.Ajax.PUT,
2087             dataObj: personObj,
2088             requestContentType: Alfresco.util.Ajax.JSON,
2089             successCallback:
2090             {
2091                fn: updateSuccess,
2092                scope: this
2093             },
2094             failureCallback:
2095             {
2096                fn: function(res)
2097                {
2098                   var json = Alfresco.util.parseJSON(res.serverResponse.responseText);
2099                   Alfresco.util.PopupManager.displayPrompt(
2100                   {
2101                      title: this._msg("message.failure"),
2102                      text: this._msg("message.update-failure", json.message)
2103                   });
2104                },
2105                scope: this
2106             }
2107          });
2108       },
2109       
2110       /**
2111        * Return the quota value as input by the user - converted to bytes.
2112        * 
2113        * @method _calculateQuota
2114        * @param idPrefix {string} ID prefix of the quota UI elements
2115        * @return the quota value as input by the user - converted to bytes
2116        * @private
2117        */
2118       _calculateQuota: function ConsoleUsers__calculateQuota(idPrefix)
2119       {
2120          var quota = -1;
2121          var quotaValue = Dom.get(idPrefix + "-quota").value;
2122          if (quotaValue.length !== 0)
2123          {
2124             // convert from giga/mega/kilo bytes
2125             try
2126             {
2127                quota = parseInt(quotaValue);
2128                if (quota >= 0)
2129                {
2130                   var quotaType = Dom.get(idPrefix + "-quotatype").value;
2131                   if (quotaType === "gb")
2132                   {
2133                      quota *= Alfresco.util.BYTES_GB;
2134                   }
2135                   else if (quotaType === "mb")
2136                   {
2137                      quota *= Alfresco.util.BYTES_MB;
2138                   }
2139                   else if (quotaType === "kb")
2140                   {
2141                      quota *= Alfresco.util.BYTES_KB;
2142                   }
2143                }
2144                else
2145                {
2146                   quota = -1;
2147                }
2148             }
2149             catch (e)
2150             {
2151                // ignore if we cannot parse quota field
2152             }
2153          }
2154          return quota;
2155       },
2156       
2157       /**
2158        * Gets a custom message
2159        *
2160        * @method _msg
2161        * @param messageId {string} The messageId to retrieve
2162        * @return {string} The custom message
2163        * @private
2164        */
2165       _msg: function ConsoleUsers__msg(messageId)
2166       {
2167          return Alfresco.util.message.call(this, messageId, "Alfresco.ConsoleUsers", Array.prototype.slice.call(arguments).slice(1));
2168       }
2169    });
2170 })();