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  * ConsoleGroups tool component.
 22  *
 23  * @namespace Alfresco
 24  * @class Alfresco.ConsoleGroups
 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     * ConsoleGroups constructor.
 42     *
 43     * @param {String} htmlId The HTML id of the parent element
 44     * @return {Alfresco.ConsoleGroups} The new ConsoleGroups instance
 45     * @constructor
 46     */
 47    Alfresco.ConsoleGroups = function(htmlId)
 48    {
 49       this.name = "Alfresco.ConsoleGroups";
 50       Alfresco.ConsoleGroups.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", "columnbrowser"], this.onComponentsLoaded, this);
 57 
 58       /* Decoupled event listeners */
 59       YAHOO.Bubbling.on("newGroup", this.onNewGroup, this);
 60       YAHOO.Bubbling.on("updateGroup", this.onUpdateGroup, this);
 61 
 62       /* Define panel handlers */
 63       var parent = this;
 64       this.panelHandlers = {};
 65 
 66       // NOTE: the panel registered first is considered the "default" view and is displayed first
 67 
 68       /* Search Panel Handler */
 69       SearchPanelHandler = function SearchPanelHandler_constructor()
 70       {
 71          SearchPanelHandler.superclass.constructor.call(this, "search");
 72       };
 73 
 74       YAHOO.extend(SearchPanelHandler, Alfresco.ConsolePanelHandler,
 75       {
 76          /**
 77           * INSTANCE VARIABLES
 78           */
 79 
 80          /**
 81           * Keeps track if this panel is visble or not
 82           *
 83           * @property _visible
 84           * @type Boolean
 85           */
 86          _visible: false,
 87 
 88          /**
 89           * Keeps track if this panel is searching or not
 90           *
 91           * @property isSearching
 92           * @type Boolean
 93           */
 94          isSearching: false,
 95 
 96          /**
 97           * When the Add User dialog or the Add Group dialog is shown this variable keeps track
 98           * of which group the selected user or group should be added to.
 99           *
100           * @property _selectedParentGroupShortName
101           * @type String
102           */
103          _selectedParentGroupShortName: null,
104 
105          /**
106           * PANEL LIFECYCLE CALLBACKS
107           */
108 
109          /**
110           * Called by the ConsolePanelHandler when this panel shall be loaded
111           *
112           * @method onLoad
113           */
114          onLoad: function ConsoleGroups_SearchPanelHandler_onLoad()
115          {
116             var me = this;
117 
118             // Search Button
119             var searchButton = new YAHOO.widget.Button(parent.id + "-search-button", {});
120             searchButton.on("click", this.onSearchClick, searchButton, this);
121             this.widgets.searchButton = searchButton;
122 
123             // ColumnBrowser
124             this.widgets.columnbrowser = new YAHOO.extension.ColumnBrowser(parent.id + "-columnbrowser",
125             {
126                numVisible: 3,
127                rootUrl: Alfresco.constants.PROXY_URI + "api/rootgroups?sortBy=displayName",
128                pagination:
129                {
130                   rowsPerPage: parent.options.maxPageSize,
131                   rowsPerPageParam: 'maxItems',
132                   recordOffsetParam: 'skipCount',
133                   firstPageLinkLabel : parent._msg('tinyPagination.firstPageLinkLabel'),
134                   lastPageLinkLabel : parent._msg('tinyPagination.lastPageLinkLabel'),
135                   previousPageLinkLabel : parent._msg('tinyPagination.previousPageLinkLabel'),
136                   nextPageLinkLabel : parent._msg('tinyPagination.nextPageLinkLabel'),
137                   pageReportTemplate : parent._msg('tinyPagination.pageReportTemplate'),
138                   template: parent._msg('tinyPagination.template')
139                },
140                columnInfoBuilder:
141                {
142                   fn: this.onBuildColumnInfo,
143                   scope: this
144                }
145             });
146 
147             // ColumnBrowser Breadcrumb
148             this.widgets.breadcrumb = new YAHOO.extension.ColumnBrowserBreadCrumb(parent.id + "-breadcrumb",
149             {
150                columnBrowser: this.widgets.columnbrowser,
151                root: parent._msg("label.breadcrumb.root")
152             });
153 
154             // Browse button
155             var browse = new YAHOO.widget.Button(parent.id + "-browse-button", {});
156             browse.on("click", this.onBrowseClick, browse, this);
157 
158             // Show All checkbox
159             var elShowAll = Dom.get(parent.id + "-show-all");
160             Event.addListener(elShowAll, "change", function()
161                {
162                   var state = {
163                      "showAll": elShowAll.checked
164                   };
165                   parent.refreshUIState(state);
166                }, null, this);
167 
168             // DataTable and DataSource setup
169             this.widgets.dataSource = new YAHOO.util.DataSource(Alfresco.constants.PROXY_URI + "api/groups?",
170             {
171                responseType: YAHOO.util.DataSource.TYPE_JSON,
172                responseSchema:
173                {
174                   resultsList: "data",
175                   metaFields:
176                   {
177                      recordOffset: "startIndex",
178                      totalRecords: "totalRecords"
179                   }
180                }
181             });
182 
183             // Work to be performed after data has been queried but before display by the DataTable
184             this.widgets.dataSource.doBeforeParseData = function ConsoleGroups_SearchPanel_doBeforeParseData(oRequest, oFullResponse)
185             {
186                var updatedResponse = oFullResponse;
187 
188                if (oFullResponse)
189                {
190                   var items = oFullResponse.data;
191 
192                   // initial sort by username field
193                   items.sort(function(a, b)
194                   {
195                      var name1 = a.shortName ? a.shortName.toLowerCase() : "",
196                         name2 = b.shortName ? b.shortName.toLowerCase() : "";
197                      return (name1 > name2) ? 1 : (name1 < name2) ? -1 : 0;
198                   });
199 
200                   // we need to wrap the array inside a JSON object so the DataTable gets the object it expects
201                   updatedResponse =
202                   {
203                      "data": items
204                   };
205                }
206 
207                // update Results Bar message with number of results found
208                if (items.length == 0)
209                {
210                   me._setResultsMessage("message.noresults");
211                }
212                else if (items.length < parent.options.maxSearchResults)
213                {
214                   me._setResultsMessage("message.results", $html(parent.query), items.length);
215                }
216                else
217                {
218                   me._setResultsMessage("message.maxresults", parent.options.maxSearchResults);
219                }
220 
221                return updatedResponse;
222             };
223 
224             // Setup the main datatable
225             this._setupDataTable();
226 
227             // register the "enter" event on the search text field
228             var searchText = Dom.get(parent.id + "-search-text");
229             new YAHOO.util.KeyListener(searchText,
230             {
231                keys: YAHOO.util.KeyListener.KEY.ENTER
232             },
233             {
234                fn: function()
235                {
236                   this.onSearchClick();
237                },
238                scope: this,
239                correctScope: true
240             }, "keydown").enable();
241 
242             // Load in the People Finder component from the server
243             Alfresco.util.Ajax.request(
244             {
245                url: Alfresco.constants.URL_SERVICECONTEXT + "components/people-finder/people-finder",
246                dataObj:
247                {
248                   htmlid: parent.id + "-search-peoplefinder"
249                },
250                successCallback:
251                {
252                   fn: this.onPeopleFinderLoaded,
253                   scope: this
254                },
255                failureMessage: "Could not load People Finder component",
256                execScripts: true
257             });
258 
259             // Load in the Group Finder component from the server
260             Alfresco.util.Ajax.request(
261             {
262                url: Alfresco.constants.URL_SERVICECONTEXT + "components/people-finder/group-finder",
263                dataObj:
264                {
265                   htmlid: parent.id + "-search-groupfinder"
266                },
267                successCallback:
268                {
269                   fn: this.onGroupFinderLoaded,
270                   scope: this
271                },
272                failureMessage: "Could not load Group Finder component",
273                execScripts: true
274             });
275 
276             // Create delete group panel
277             this.widgets.deleteGroupPanel = new Alfresco.util.createYUIPanel(parent.id + "-deletegroupdialog",
278             {
279                visible: false
280             });
281 
282             // Add event listeners to buttons
283             this.widgets.deleteGroupCancelButton = new YAHOO.widget.Button(parent.id + "-cancel-button", {});
284             this.widgets.deleteGroupCancelButton.on("click", function()
285             {
286                this.widgets.deleteGroupPanel.hide();
287             }, null, this);
288 
289             this.widgets.deleteGroupOkButton = Alfresco.util.createYUIButton(parent, "remove-button", null);
290          },
291 
292          /**
293           * Called by the ConsolePanelHandler when this panel is shown
294           *
295           * @method onShow
296           */
297          onShow: function ConsoleGroups_SearchPanelHandler_onShow()
298          {
299             this._visible = true;
300 
301             // Set focus to the search input field
302             Dom.get(parent.id + "-search-text").focus();
303             
304             // Show All checkbox state from history
305             var elShowAll = Dom.get(parent.id + "-show-all");
306             elShowAll.checked = parent.showAll;
307          },
308 
309          /**
310           * Called by the ConsolePanelHandler when this panel shall update its appearance
311           *
312           * @method onUpdate
313           */
314          onUpdate: function ConsoleGroups_SearchPanelHandler_onUpdate()
315          {
316             if (parent.refresh == undefined || parent.query !== undefined)
317             {
318                /**
319                 * The search panel shall only be displayed if a query exists OR when the page is initially loaded.
320                 * The parent.refresh == undefined indicates that the page was reloaded when in browse view state.
321                 */
322 
323                // Lets display the search list since the state indicates a query has been used
324                Dom.addClass(parent.id + "-browse-panel", "hidden");
325                Dom.removeClass(parent.id + "-search-panel", "hidden");
326                
327                if (parent.query !== undefined)
328                {
329                   // update the text field - as this event could come from bookmark, navigation or a search button click
330                   var queryElem = Dom.get(parent.id + "-search-text");
331                   queryElem.value = parent.query;
332 
333                   // Redo the search
334                   this.doSearch();
335                }
336             }
337             else
338             {
339                // No query in the state then display the column browser
340                Dom.addClass(parent.id + "-search-panel", "hidden");
341                Dom.removeClass(parent.id + "-browse-panel", "hidden");
342 
343                var paths = this.widgets.columnbrowser.get("urlPath");
344                if (!paths || paths.length == 0)
345                {
346                   // Load the root groups
347                   this.widgets.columnbrowser.load();
348                }
349                else if (parent.refresh)
350                {
351                   // Refresh the column browser
352                   this.widgets.columnbrowser.load(paths, true);
353                }
354             }
355          },
356 
357          /**
358           * Called by the ConsolePanelHandler when this panel is hidden
359           *
360           * @method onHide
361           */
362          onHide: function ConsoleGroups_SearchPanelHandler_onHide()
363          {
364             this._visible = false;
365          },
366 
367          /**
368           * BUTTON EVENT HANDLERS
369           */
370 
371          /**
372           * Called when the user clicks the search button
373           *
374           * @method onSearchClick
375           */
376          onSearchClick: function ConsoleGroups_SearchPanelHandler_onSearchClick()
377          {
378             var query = YAHOO.lang.trim(Dom.get(parent.id + "-search-text").value);
379 
380             // inform the user if the search term entered is too small
381             if (query.replace(/\*/g, "").length < parent.options.minSearchTermLength)
382             {
383                Alfresco.util.PopupManager.displayMessage(
384                {
385                   text: parent._msg("message.minimum-length", parent.options.minSearchTermLength)
386                });
387                return;
388             }
389 
390             parent.refreshUIState({"query": query});
391          },
392 
393          /**
394           * Called when the user clicks the browse button
395           *
396           * @method onBrowseClick
397           */
398          onBrowseClick: function ConsoleGroups_SearchPanelHandler_onBrowseClick()
399          {
400             parent.refreshUIState({"query": undefined, "refresh": "false"});
401          },
402 
403          /**
404           * Called when the user clicks the delete button in the cofirm dialog.
405           * Deletes the group from the repository or simply removes is from a parent group.
406           *
407           * @param e The click event
408           * @param obj information about the group and its parent group
409           * shall be removed from parent group. If now present group will be deleted.
410           */
411          onConfirmedDeleteGroupClick: function ConsoleGroups_SearchPanelHandler_onConfirmedDeleteGroupClick(e, obj)
412          {
413             // Hide the confirm dialog
414             this.widgets.deleteGroupPanel.hide();
415 
416             if (obj.multiParentMode && Dom.get(parent.id + "-remove").checked)
417             {
418                // Just remove the group from the parent group
419                this._removeGroup(obj.fullName, obj.parentShortName, obj.displayName);
420             }
421             else
422             {
423                // Delete the group form the repository
424                this._deleteGroup(obj.shortName, obj.displayName);
425             }
426          },
427 
428          /**
429           * Group selected event handler.
430           * This event is fired from Group picker - so we much ensure
431           * the event is for the current panel by checking panel visibility.
432           *
433           * @method onGroupSelected
434           * @param e DomEvent
435           * @param args Event parameters (depends on event type)
436           */
437          onGroupSelected: function ConsoleGroups_SearchPanelHandler_onGroupSelected(e, args)
438          {
439             // This is a "global" event so we ensure the event is for the current panel by checking panel visibility.
440             if (this._visible)
441             {
442                var name = args[1].displayName;
443                this.widgets.addGroupPanel.hide();
444                this._addToGroup(
445                      args[1].itemName,
446                      this._selectedParentGroupShortName,
447                      parent._msg("message.addgroup-success", name),
448                      parent._msg("message.addgroup-failure", name));
449             }
450          },
451 
452          /**
453           * Called when the user has selected a person from the add user dialog.
454           *
455           * @method onPersonSelected
456           * @param e DomEvent
457           * @param args Event parameters (depends on event type)
458           */
459          onPersonSelected: function ConsoleGroups_SearchPanelHandler_onPersonSelected(e, args)
460          {
461             // This is a "global" event so we ensure the event is for the current panel by checking panel visibility.
462             if (this._visible)
463             {
464                var name = args[1].firstName + " " + args[1].lastName;
465                this.widgets.addUserPanel.hide();
466                this._addToGroup(
467                      args[1].userName,
468                      this._selectedParentGroupShortName,
469                      parent._msg("message.adduser-success", name),
470                      parent._msg("message.adduser-failure", name));
471             }
472          },
473 
474          /**
475           * Called when the user clicks the new group icon in the column browser header
476           *
477           * @method onNewGroupClick
478           * @param columnInfo
479           */
480          onNewGroupClick: function ConsoleGroups_SearchPanelHandler__onNewGroupClick(columnInfo)
481          {
482             // Send avenet so the create panel will be displayed
483             YAHOO.Bubbling.fire('newGroup',
484             {
485                group: columnInfo.parent ? columnInfo.parent.shortName : undefined,
486                groupDisplayName: columnInfo.parent ? columnInfo.parent.label : parent._msg("label.theroot")
487             });
488          },
489 
490          /**
491           * Called when the user clicks the add gorup icon in the column browser header
492           *
493           * @method onAddGroupClick
494           * @param columnInfo
495           */
496          onAddGroupClick: function ConsoleGroups_SearchPanelHandler__onAddGroupClick(columnInfo)
497          {
498             this._selectedParentGroupShortName = columnInfo.parent.shortName;
499             this.modules.searchGroupFinder.clearResults();
500             this.widgets.addGroupPanel.show();
501          },
502 
503          /**
504           * Called when the user clicks the add user icon in the column browser header
505           *
506           * @method onAddUserClick
507           * @param columnInfo
508           */
509          onAddUserClick: function ConsoleGroups_SearchPanelHandler_onAddUserClick(columnInfo)
510          {
511             this._selectedParentGroupShortName = columnInfo.parent.shortName;
512             this.modules.searchPeopleFinder.clearResults();
513             this.widgets.addUserPanel.show();
514          },
515 
516          /**
517           * Called when the user clicks a groups delete icon in the column browser
518           *
519           * @method onDeleteClick
520           * @param ctx An object describing the group and its parent group
521           *        ctx.columnInfo describes the clicked groups column (and its parent group)
522           *        ctx.itemInfo describes the clicked group
523           */
524          onDeleteClick: function ConsoleGroups_SearchPanelHandler_onDeleteClick(ctx)
525          {
526             this._confirmDeleteGroup(
527                   ctx.itemInfo.shortName,
528                   ctx.itemInfo.fullName,
529                   ctx.itemInfo.label,
530                   ctx.columnInfo.parent ? ctx.columnInfo.parent.shortName : null,
531                   ctx.columnInfo.parent ? ctx.columnInfo.parent.label : parent._msg("label.theroot"));
532          },
533 
534          /**
535           * Called when the user clicks a users delete icon in the column browser
536           *
537           * @method onUserRemoveClick
538           * @param ctx An object describing the group and its parent group
539           *        ctx.columnInfo describes the clicked groups column (and its parent group)
540           *        ctx.itemInfo describes the clicked group
541           */
542          onUserRemoveClick: function ConsoleGroups_SearchPanelHandler_onUserRemoveClick(ctx)
543          {
544             this._confirmRemoveUser(ctx.columnInfo.parent.shortName, ctx.itemInfo.shortName, ctx.itemInfo.label);
545          },
546 
547          /**
548           * Called when the user clicks a groups update icon in the column browser
549           *
550           * @method onUpdateClick
551           * @param ctx An object describing the group and its parent group
552           *        ctx.columnInfo describes the clicked groups column (and its parent group)
553           *        ctx.itemInfo describes the clicked group
554           */
555          onUpdateClick: function ConsoleGroups_SearchPanelHandler_onUpdateClick(ctx)
556          {
557             YAHOO.Bubbling.fire('updateGroup', {group: ctx.itemInfo.shortName, groupDisplayName: ctx.itemInfo.label});
558          },
559 
560          /**
561           * MODULE TEMPLATE LOAD HANDLERS
562           */
563 
564          /**
565           * Called when the people finder template has been loaded.
566           * Creates a dialog and inserts the people finder for choosing users to add.
567           *
568           * @method onPeopleFinderLoaded
569           * @param response The server response
570           */
571          onPeopleFinderLoaded: function ConsoleGroups_SearchPanelHandler_onPeopleFinderLoaded(response)
572          {
573             // Inject the component from the XHR request into it's placeholder DIV element
574             var finderDiv = Dom.get(parent.id + "-search-peoplefinder");
575             finderDiv.innerHTML = response.serverResponse.responseText;
576 
577             // Create the Add User dialog
578             this.widgets.addUserPanel = Alfresco.util.createYUIPanel(parent.id + "-peoplepicker");
579 
580             // Find the People Finder by container ID
581             this.modules.searchPeopleFinder = Alfresco.util.ComponentManager.get(parent.id + "-search-peoplefinder");
582 
583             // Set the correct options for our use
584             this.modules.searchPeopleFinder.setOptions(
585             {
586                singleSelectMode: true
587             });
588 
589             // Make sure we listen for events when the user selects a person
590             YAHOO.Bubbling.on("personSelected", this.onPersonSelected, this);
591          },
592 
593          /**
594           * Called when the group finder template has been loaded.
595           * Creates a dialog and inserts the group finder for choosing groups to add.
596           *
597           * @method onGroupFinderLoaded
598           * @param response The server response
599           */
600          onGroupFinderLoaded: function ConsoleGroups_SearchPanelHandler_onGroupFinderLoaded(response)
601          {
602             // Inject the component from the XHR request into it's placeholder DIV element
603             var finderDiv = Dom.get(parent.id + "-search-groupfinder");
604             finderDiv.innerHTML = response.serverResponse.responseText;
605 
606             // Create the Add Group dialog
607             this.widgets.addGroupPanel = Alfresco.util.createYUIPanel(parent.id + "-grouppicker")
608 
609             // Find the Group Finder by container ID
610             this.modules.searchGroupFinder = Alfresco.util.ComponentManager.get(parent.id + "-search-groupfinder");
611 
612             // Set the correct options for our use
613             this.modules.searchGroupFinder.setOptions(
614             {
615                singleSelectMode: true
616             });
617 
618             // Make sure we listen for events when the user selects a group
619             YAHOO.Bubbling.on("itemSelected", this.onGroupSelected, this);
620 
621          },
622 
623          /**
624           * COLUMN BROWSER CALLBACKS
625           */
626 
627          /**
628           * Called by the Column Browser to let this component transform the custom server reponse to a
629           * columnInfo object that the Column Browser understands
630           *
631           * @method onBuildColumnInfo
632           * @param serverResponse Response from the server containing column data OR null if a leaf was clicked
633           * @param itemInfo The parent item that was clicked to get the column data OR null if it is the root column
634           */
635          onBuildColumnInfo: function ConsoleGroups_SearchPanelHandler_onBuildColumnInfo(serverResponse, itemInfo)
636          {
637             // Create columnInfo and its header
638             var headerButtons = [];
639             if (!itemInfo || itemInfo.cssClass == 'groups-item-group')
640             {
641                headerButtons.push({
642                   title: (itemInfo ? parent._msg("button.newsubgroup") : parent._msg("button.newgroup")),
643                   cssClass: "groups-newgroup-button",
644                   click: {
645                      fn: this.onNewGroupClick,
646                      scope: this
647                   }
648                });
649             }
650             if (itemInfo && itemInfo.cssClass == 'groups-item-group')
651             {
652                // Only add the following button for NON root columns 
653                headerButtons.push({
654                   title: parent._msg("button.addgroup"),
655                   cssClass: "groups-addgroup-button",
656                   click: {
657                      fn: this.onAddGroupClick,
658                      scope: this
659                   }
660                });
661                headerButtons.push({
662                   title: parent._msg("button.adduser"),
663                   cssClass: "groups-adduser-button",
664                   click: {
665                      fn: this.onAddUserClick,
666                      scope: this
667                   }
668                });
669             }
670 
671             // Create column descriptor
672             var column = {
673                parent: itemInfo,
674                header: {
675                   buttons: headerButtons
676                },
677                body: {
678                   items: []
679                }
680             };
681 
682             // Get data from request
683             var obj = {};
684             if (serverResponse)
685             {
686                // Parse response if there was one
687                obj = YAHOO.lang.JSON.parse(serverResponse.responseText);
688 
689                // Translate group paging attributes to columnbrowser pagination attributes 
690                if (obj.paging)
691                {
692                   column.pagination = {
693                      totalRecords : obj.paging.totalItems,
694                      recordOffset: obj.paging.skipCount
695                   };
696                }
697             }
698 
699             // Create item buttons for users and groups
700             var groupButtons = [
701                {
702                   title: parent._msg("button.updategroup"),
703                   cssClass: "groups-update-button",
704                   click: {
705                      fn: this.onUpdateClick,
706                      scope: this
707                   }
708                },
709                {
710                   title: parent._msg("button.deletegroup"),
711                   cssClass: "groups-delete-button",
712                   click: {
713                      fn: this.onDeleteClick,
714                      scope: this
715                   }
716                }
717             ];
718             var usersButtons = [
719                {
720                   title: parent._msg("button.removeuser"),
721                   cssClass: "users-remove-button",
722                   click: {
723                      fn: this.onUserRemoveClick,
724                      scope: this
725                   }
726                }
727             ];
728 
729             // Transform server respons to itemInfos and add them to the columnInfo's body
730             for (var i = 0; obj.data && i < obj.data.length; i++)
731             {
732                var o = obj.data[i];
733                var label = o.displayName;
734                if (o.displayName !== o.shortName)
735                {
736                   label += " (" + o.shortName + ")";
737                }
738                var item = {
739                   shortName: o.shortName,
740                   fullName: o.fullName,
741                   url: o.authorityType == 'GROUP' ? Alfresco.constants.PROXY_URI + o.url + "/children?sortBy=displayName" : null,
742                   hasNext: o.groupCount > 0 || o.userCount > 0,
743                   label: label,
744                   next : null,
745                   cssClass: o.authorityType == 'GROUP' ? "groups-item-group" : "groups-item-user",
746                   buttons: o.authorityType == 'GROUP' ? groupButtons : usersButtons
747                };
748                column.body.items.push(item);
749             }
750 
751             return column;
752          },
753 
754          /**
755           * PUBLIC METHODS
756           */
757 
758          /**
759           * Invoke search based on the "state", use the state-query parameter that is stored in the parent object
760           * each time a state is set.
761           *
762           * @method doSearch
763           */
764          doSearch: function ConsoleGroups_SearchPanelHandler_doSearch()
765          {
766             // check search length again as we may have got here via history navigation
767             if (!this.isSearching && parent.query !== undefined && parent.query.length >= parent.options.minSearchTermLength)
768             {
769                this.isSearching = true;
770                
771                var me = this;
772 
773                // Reset the custom error messages
774                me._setDefaultDataTableErrors(me.widgets.dataTable);
775 
776                // Don't display any message
777                me.widgets.dataTable.set("MSG_EMPTY", parent._msg("message.searching"));
778 
779                // Empty results table
780                me.widgets.dataTable.deleteRows(0, me.widgets.dataTable.getRecordSet().getLength());
781 
782                var successHandler = function ConsoleGroups__ps_successHandler(sRequest, oResponse, oPayload)
783                {
784                   me._enableSearchUI();
785                   me._setDefaultDataTableErrors(me.widgets.dataTable);
786                   me.widgets.dataTable.onDataReturnInitializeTable.call(me.widgets.dataTable, sRequest, oResponse, oPayload);
787                };
788 
789                var failureHandler = function ConsoleGroups__ps_failureHandler(sRequest, oResponse)
790                {
791                   me._enableSearchUI();
792                   if (oResponse.status == 401)
793                   {
794                      // Our session has likely timed-out, so refresh to offer the login page
795                      window.location.reload();
796                   }
797                   else
798                   {
799                      try
800                      {
801                         me.widgets.dataTable.set("MSG_ERROR", parent._msg("message.noresults.short"));
802                         me.widgets.dataTable.showTableMessage(parent._msg("message.noresults.short"), YAHOO.widget.DataTable.CLASS_ERROR);
803                         me._setResultsMessage("message.noresults");
804                      }
805                      catch(e)
806                      {
807                         me._setDefaultDataTableErrors(me.widgets.dataTable);
808                      }
809                   }
810                };
811 
812                // Send the query to the server and disable search button
813                me.widgets.dataSource.sendRequest(me._buildSearchParams(parent.query),
814                {
815                   success: successHandler,
816                   failure: failureHandler,
817                   scope: parent
818                });
819                me._setResultsMessage("message.searchingFor", $html(parent.query));
820 
821                // Disable search button and display a wait feedback message if the groups hasn't been found yet
822                me.widgets.searchButton.set("disabled", true);
823                YAHOO.lang.later(2000, me, function(){
824                   if (me.isSearching)
825                   {
826                      if (!me.widgets.feedbackMessage)
827                      {
828                         me.widgets.feedbackMessage = Alfresco.util.PopupManager.displayMessage(
829                         {
830                            text: Alfresco.util.message("message.searching", parent.name),
831                            spanClass: "wait",
832                            displayTime: 0
833                         });
834                      }
835                      else if (!me.widgets.feedbackMessage.cfg.getProperty("visible"))
836                      {
837                         me.widgets.feedbackMessage.show();
838                      }
839                   }
840                }, []);
841             }
842          },
843 
844          /**
845           * Enable search button, hide the pending wait message and set the panel as not searching.
846           *
847           * @method _enableSearchUI
848           * @private
849           */
850          _enableSearchUI: function ConsoleGroups_SearchPanelHandler__enableSearchUI()
851          {
852             // Enable search button and close the wait feedback message if present
853             if (this.widgets.feedbackMessage && this.widgets.feedbackMessage.cfg.getProperty("visible"))
854             {
855                this.widgets.feedbackMessage.hide();
856             }
857             this.widgets.searchButton.set("disabled", false);
858             this.isSearching = false;
859          },
860 
861          /**
862           * PRIVATE METHODS
863           */
864 
865          /**
866           * Asks the users if he is sure he wants to delete the group
867           *
868           * @param shortName shortName The id of the group to delete
869           * @param fullName The fullName of the group to delete (needed only if removing group from parent group)
870           * @param displayName The displayName of the group to delete
871           * @param parentShortName The shortName of the parent group to remove group from (needed only if removing group from parent group)
872           * @param parentDisplayName The displayName of the parent group to remove group from (needed only if removing group from parent group)
873           */
874          _confirmDeleteGroup: function ConsoleGroups_SearchPanelHandler_confirmDeleteGroup(shortName, fullName, displayName, parentShortName, parentDisplayName)
875          {
876             var me = this;
877             parent.getParentGroups(shortName,
878             {
879                fn: function(groups)
880                {
881                   // Remove previous listeners so we don't make duplicate calls and add a new one later
882                   this.widgets.deleteGroupOkButton.removeListener("click", this.onConfirmedDeleteGroupClick);
883                   var callbackObj =
884                   {
885                      shortName: shortName,
886                      fullName: fullName,
887                      displayName: displayName,
888                      parentShortName: parentShortName,
889                      parentDisplayName: parentDisplayName
890                   };
891 
892                   // Make sure the dialog is displayed correctly
893                   if (!groups || groups.length == 0 || groups.length == 1)
894                   {
895                      // Group is root group or has only 1 parent
896                      Dom.addClass(parent.id + "-multiparent", "hidden");
897                      Dom.removeClass(parent.id + "-singleparent", "hidden");
898                      Dom.get(parent.id + "-singleparent-message").innerHTML = parent._msg("panel.deletegroup.singleparentmessage", $html(displayName));
899                      this.widgets.deleteGroupOkButton.on("click", this.onConfirmedDeleteGroupClick, callbackObj, this);
900                   }
901                   else
902                   {
903                      // Group has multiple parents
904                      Dom.addClass(parent.id + "-singleparent", "hidden");
905                      Dom.removeClass(parent.id + "-multiparent", "hidden");
906                      Dom.get(parent.id + "-multiparent-message").innerHTML = parent._msg("panel.deletegroup.multiparentmessage", $html(displayName));
907                      Dom.get(parent.id + "-remove-message").innerHTML = parent._msg("panel.deletegroup.removemessage", $html(displayName), $html(parentDisplayName));
908                      Dom.get(parent.id + "-delete-message").innerHTML = parent._msg("panel.deletegroup.deletemessage", $html(displayName));
909                      Dom.get(parent.id + "-searchdelete-message").innerHTML = parent._msg("panel.deletegroup.searchdeletemessage", $html(displayName));
910 
911                      // Lets display the groups parents to the user, but only the first 10
912                      var parentStr = "", displayLimit = 10;
913                      for (var i = 0; i < groups.length && i < displayLimit; i++)
914                      {
915                         parentStr += groups[i].displayName + (i < groups.length - 1 ? ", " : "");
916                      }
917                      if (i >= displayLimit)
918                      {
919                         parentStr += parent._msg("label.moregroups", groups.length - displayLimit);
920                      }
921                      Dom.get(parent.id + "-parents").innerHTML = $html(parentStr);
922 
923                      if (parentShortName)
924                      {
925                         // Display both the option to remove from parent group and delete the group
926                         Dom.get(parent.id + "-remove").checked = true;
927                         Dom.removeClass(parent.id + "-removerow", "hidden");
928                         Dom.removeClass(parent.id + "-deleterow", "hidden");
929                         Dom.addClass(parent.id + "-searchdeleterow", "hidden");
930                      }
931                      else
932                      {
933                         /**
934                          * The group was clicked in a context where none of the parents was displayed,
935                          * in other words in the search list. There fore we can't display the option of just
936                          * removing the group.
937                          */
938                         Dom.get(parent.id + "-delete").checked = true;
939                         Dom.addClass(parent.id + "-removerow", "hidden");
940                         Dom.addClass(parent.id + "-deleterow", "hidden");
941                         Dom.removeClass(parent.id + "-searchdeleterow", "hidden");
942                      }
943 
944                      // Make sure the callback knows what mode the dialog was displayed in
945                      callbackObj.multiParentMode = true;
946                      this.widgets.deleteGroupOkButton.on("click", this.onConfirmedDeleteGroupClick, callbackObj, this);
947                   }
948                   // Show the dialog
949                   this.widgets.deleteGroupPanel.show();
950                },
951                scope: this
952             }, "message.delete-failure");
953          },
954 
955          /**
956           * Asks the users if he is sure he wants to remove the user from the group.
957           *
958           * @param groupId The id of the group to remove the user from
959           * @param userId The id of the user to remove
960           * @param userDisplayName The displayName of the user
961           */
962          _confirmRemoveUser: function ConsoleGroups_SearchPanelHandler__confirmRemoveUser(groupId, userId, userDisplayName)
963          {
964             var me = this;
965             Alfresco.util.PopupManager.displayPrompt(
966             {
967                title: parent._msg("message.confirm.removeuser.title"),
968                text: parent._msg("message.confirm.removeuser", userDisplayName),
969                buttons: [
970                   {
971                      text: parent._msg("button.yes"),
972                      handler: function ConsoleGroups__removeUser_confirmYes()
973                      {
974                         this.destroy();
975                         me._removeUser.call(me, groupId, userId, userDisplayName);
976                      }
977                   },
978                   {
979                      text: parent._msg("button.no"),
980                      handler: function ConsoleGroups__removeUser_confirmNo()
981                      {
982                         this.destroy();
983                      },
984                      isDefault: true
985                   }]
986             });
987          },
988 
989          /**
990           * Deletes the group from the repository.
991           *
992           * @param shortName The shortName of the group
993           * @param displayName The displayName  of the group
994           * shall be removed from parent group. If now present group will be deleted.
995           */
996          _deleteGroup: function ConsoleGroups_SearchPanelHandler__deleteGroup(shortName, displayName)
997          {
998             var url = Alfresco.constants.PROXY_URI + "api/groups/" + encodeURIComponent(shortName);
999             this._doDeleteCall(url, displayName);
1000          },
1001 
1002          /**
1003           * Removes the group from a parent group
1004           *
1005           * @param fullName The full authority name of the group
1006           * @param parentShortName the shortname of the parent group
1007           * @param displayName The displayName  of the group
1008           */
1009          _removeGroup: function ConsoleGroups_SearchPanelHandler__removeGroup(fullName, parentShortName, displayName)
1010          {
1011             if (parentShortName == null)
1012             {
1013                // todo implement when webscript api supports it
1014                // This isn't supported by the webscript api yet
1015                Alfresco.util.PopupManager.displayPrompt(
1016                {
1017                   title: parent._msg("message.failure"),
1018                   text: parent._msg("message.noRemoveGroupFromRootSupport")
1019                });
1020                return;
1021             }
1022             var url = Alfresco.constants.PROXY_URI + "api/groups/" + encodeURIComponent(parentShortName) + "/children/" + encodeURIComponent(fullName);
1023             this._doDeleteCall(url, displayName);
1024          },
1025 
1026          /**
1027           * Deletes or removes a group depending on the url
1028           *
1029           * @param url Url to use to remove or delete group
1030           */
1031          _doDeleteCall: function ConsoleGroups_SearchPanelHandler__deleteCall(url, displayName)
1032          {
1033             var groupDisplayName = displayName;
1034             Alfresco.util.Ajax.request( 
1035             {
1036                method: Alfresco.util.Ajax.DELETE,
1037                url: url,
1038                successCallback:
1039                {
1040                   fn: function(o)
1041                   {
1042                      // Refresh column browser
1043                      var paths = this.widgets.columnbrowser.get("urlPath");
1044                      this.widgets.columnbrowser.load(paths, true);
1045 
1046                      // Refresh search table
1047                      this.doSearch();
1048 
1049                      // Display success message
1050                      Alfresco.util.PopupManager.displayMessage(
1051                      {
1052                         text: parent._msg("message.delete-success", groupDisplayName)
1053                      });
1054                   },
1055                   scope: this
1056                },
1057                failureMessage: parent._msg("message.delete-failure", groupDisplayName)
1058             });
1059          },
1060 
1061          /**
1062           * Remove the user from the group
1063           *
1064           * @param groupId The id of the group
1065           * @param userId The id of the user
1066           * @param userDisplayName The displayName of the user
1067           */
1068          _removeUser: function ConsoleGroups_SearchPanelHandler__removeUser(groupId, userId, userDisplayName)
1069          {
1070             var name = userDisplayName;
1071             Alfresco.util.Ajax.request( 
1072             {
1073                method: Alfresco.util.Ajax.DELETE,
1074                url: Alfresco.constants.PROXY_URI + "api/groups/" + encodeURIComponent(groupId) + "/children/" + encodeURIComponent(userId),
1075                successCallback:
1076                {
1077                   fn: function(o)
1078                   {
1079                      // Refresh column browser
1080                      var paths = this.widgets.columnbrowser.get("urlPath");
1081                      this.widgets.columnbrowser.load(paths, true);
1082 
1083                      // Display success message
1084                      Alfresco.util.PopupManager.displayMessage(
1085                      {
1086                         text: parent._msg("message.removeuser-success", name)
1087                      });
1088                   },
1089                   scope: this
1090                },
1091                failureMessage: parent._msg("message.removeuser-failure", name)
1092             });
1093          },
1094 
1095          /**
1096           * Adds a user or group to a parent group.
1097           *
1098           * @param objectId The id to a user (userName) or a group (fullName)
1099           * @param parentGroupShortName The shortName of the parent group that the object shall be added under
1100           * @param successMessage Message to display if the request is successful
1101           * @param failureMessage Message to display if the request fails
1102           */
1103          _addToGroup: function ConsoleGroups_SearchPanelHandler__addToGroup(objectId, parentGroupShortName, successMessage, failureMessage)
1104          {
1105             Alfresco.util.Ajax.jsonPost(
1106             {
1107                url: Alfresco.constants.PROXY_URI + "api/groups/" + encodeURIComponent(parentGroupShortName) + "/children/" + encodeURIComponent(objectId),
1108                successCallback:
1109                {
1110                   fn: function(o)
1111                   {
1112                      // Refresh column browser
1113                      var paths = this.widgets.columnbrowser.get("urlPath");
1114                      this.widgets.columnbrowser.load(paths, true);
1115 
1116                      // Display success message
1117                      Alfresco.util.PopupManager.displayMessage(
1118                      {
1119                         text: successMessage
1120                      });
1121                   },
1122                   scope: this
1123                },
1124                failureMessage: failureMessage
1125             });
1126          },
1127 
1128          /**
1129           * Setup the YUI DataTable with custom renderers.
1130           *
1131           * @method _setupDataTable
1132           * @private
1133           */
1134          _setupDataTable: function ConsoleGroups_SearchPanelHandler__setupDataTable()
1135          {
1136             var me = this;
1137 
1138             /**
1139              * DataTable Cell Renderers
1140              *
1141              * Each cell has a custom renderer defined as a custom function. See YUI documentation for details.
1142              * These MUST be inline in order to have access to the parent instance (via the "parent" variable).
1143              */
1144 
1145             /**
1146              * Generic HTML-safe custom datacell formatter
1147              */
1148             var renderCellSafeHTML = function renderCellSafeHTML(elCell, oRecord, oColumn, oData)
1149             {
1150                elCell.innerHTML = $html(oData);
1151             };
1152 
1153             /**
1154              * Group actions custom datacell formatter
1155              *
1156              * @method renderActions
1157              */
1158             var renderActions = function renderActions(elCell, oRecord, oColumn, oData)
1159             {
1160                // fire the 'updateGroupClick' event when the group has been clicked
1161                var updateLink = document.createElement("a");
1162                //updateLink.setAttribute("href", "#");
1163                Dom.addClass(updateLink, "update");
1164                updateLink.innerHTML = " ";
1165                YAHOO.util.Event.addListener(updateLink, "click", function(e)
1166                {
1167                   YAHOO.Bubbling.fire('updateGroup',
1168                   {
1169                      group: oRecord.getData("shortName"),
1170                      groupDisplayName: oRecord.getData("displayName"),
1171                      query: this.query
1172                   });
1173                }, null, parent);
1174                elCell.appendChild(updateLink);
1175 
1176                //
1177                var deleteLink = document.createElement("a");
1178                //deleteLink.setAttribute("href", "#");
1179                Dom.addClass(deleteLink, "delete");
1180                deleteLink.innerHTML = " ";
1181                YAHOO.util.Event.addListener(deleteLink, "click", function(e)
1182                {
1183                   me._confirmDeleteGroup(
1184                         oRecord.getData("shortName"),
1185                         null,
1186                         oRecord.getData("displayName"),
1187                         null,
1188                         null);
1189                });
1190                elCell.appendChild(deleteLink);
1191             };
1192 
1193             // DataTable column defintions
1194             var columnDefinitions =
1195             [
1196                { key: "shortName", label: parent._msg("label.shortname"), sortable: true, formatter: renderCellSafeHTML },
1197                { key: "displayName", label: parent._msg("label.displayname"), sortable: true, formatter: renderCellSafeHTML },
1198                { key: "actions",     label: parent._msg("label.actions"), sortable: false, formatter: renderActions }
1199             ];
1200 
1201             // DataTable definition
1202             this.widgets.dataTable = new YAHOO.widget.DataTable(parent.id + "-datatable", columnDefinitions, this.widgets.dataSource,
1203             {
1204                initialLoad: false,
1205                renderLoopSize: 32,
1206                sortedBy:
1207                {
1208                   key: "displayName",
1209                   dir: "asc"
1210                },
1211                MSG_EMPTY: parent._msg("message.empty")
1212             });
1213          },
1214 
1215          /**
1216           * Resets the YUI DataTable errors to our custom messages
1217           * NOTE: Scope could be YAHOO.widget.DataTable, so can't use "this"
1218           *
1219           * @method _setDefaultDataTableErrors
1220           * @param dataTable Instance of the DataTable
1221           * @private
1222           */
1223          _setDefaultDataTableErrors: function ConsoleGroups_SearchPanelHandler__setDefaultDataTableErrors(dataTable)
1224          {
1225             dataTable.set("MSG_EMPTY", parent._msg("message.empty", "Alfresco.ConsoleGroups"));
1226             dataTable.set("MSG_ERROR", parent._msg("message.error", "Alfresco.ConsoleGroups"));
1227          },
1228 
1229          /**
1230           * Build URI parameters for People List JSON data webscript
1231           *
1232           * @method _buildSearchParams
1233           * @param query User search term
1234           * @private
1235           */
1236          _buildSearchParams: function ConsoleGroups_SearchPanelHandler__buildSearchParams(query)
1237          {
1238             var query = "shortNameFilter=" + encodeURIComponent(query);
1239             var showAll = Dom.get(parent.id + "-show-all").checked;
1240             return showAll ? query : query + "&zone=APP.DEFAULT";
1241          },
1242 
1243          /**
1244           * Set the message in the Results Bar area
1245           *
1246           * @method _setResultsMessage
1247           * @param messageId The messageId to display
1248           * @private
1249           */
1250          _setResultsMessage: function ConsoleGroups_SearchPanelHandler__setResultsMessage(messageId, arg1, arg2)
1251          {
1252             var resultsDiv = Dom.get(parent.id + "-search-bar-text");
1253             resultsDiv.innerHTML = parent._msg(messageId, arg1, arg2);
1254          }
1255       });
1256       this.panelHandlers.searchPanelHandler = new SearchPanelHandler();
1257 
1258       /* Create Group Panel Handler */
1259       CreatePanelHandler = function CreatePanelHandler_constructor()
1260       {
1261          CreatePanelHandler.superclass.constructor.call(this, "create");
1262       };
1263 
1264       YAHOO.extend(CreatePanelHandler, Alfresco.ConsolePanelHandler,
1265       {
1266          /**
1267           * INSTANCE VARIABLES
1268           */
1269 
1270          /**
1271           * Keeps track if this panel is visble or not
1272           *
1273           * @property _visible
1274           * @type Boolean
1275           */
1276          _visible: false,
1277 
1278          /**
1279           * Keeps track if this panel shall request the view panel to refresh after a cancel click
1280           *
1281           * @property _refresh
1282           * @type Boolean
1283           */
1284          _refresh: false,
1285 
1286          /**
1287           * PANEL LIFECYCLE CALLBACKS
1288           */
1289 
1290          /**
1291           * Called by the ConsolePanelHandler when this panel shall be loaded
1292           *
1293           * @method onLoad
1294           */
1295          onLoad: function ConsoleGroups_CreatePanelHandler_onLoad()
1296          {
1297             // Buttons
1298             this.widgets.creategroupOkButton = new YAHOO.widget.Button(parent.id + "-creategroup-ok-button",
1299             {
1300                type: "button"
1301             });
1302             this.widgets.creategroupOkButton.on("click", this.onCreateGroupOKClick, null, this);
1303             this.widgets.creategroupOkButton.set("disabled", true);
1304 
1305             this.widgets.creategroupAnotherButton = new YAHOO.widget.Button(parent.id + "-creategroup-another-button",
1306             {
1307                type: "button"
1308             });
1309             this.widgets.creategroupAnotherButton.on("click", this.onCreateGroupAnotherClick, null, this);
1310             this.widgets.creategroupAnotherButton.set("disabled", true);
1311 
1312             this.widgets.creategroupCancelButton = new YAHOO.widget.Button(parent.id + "-creategroup-cancel-button",
1313             {
1314                type: "button"
1315             });
1316             this.widgets.creategroupCancelButton.on("click", this.onCreateGroupCancelClick, null, this);
1317 
1318             // Form definition
1319             var form = new Alfresco.forms.Form(parent.id + "-create-form");
1320             form.setSubmitElements([this.widgets.creategroupOkButton, this.widgets.creategroupAnotherButton]);
1321             form.setShowSubmitStateDynamically(true);
1322 
1323             // Form field validation
1324             form.addValidation(parent.id + "-create-shortname", Alfresco.forms.validation.mandatory, null, "keyup");
1325             form.addValidation(parent.id + "-create-shortname", Alfresco.forms.validation.nodeName, null, "keyup");
1326             form.addValidation(parent.id + "-create-shortname", Alfresco.forms.validation.length,
1327             {
1328                min: 3,
1329                max: 100,
1330                crop: true,
1331                includeWhitespace: false
1332             }, "keyup");
1333             form.addValidation(parent.id + "-create-displayname", Alfresco.forms.validation.mandatory, null, "keyup");
1334             form.addValidation(parent.id + "-create-displayname", Alfresco.forms.validation.length,
1335             {
1336                min: 3,
1337                max: 255,
1338                crop: true,
1339                includeWhitespace: false
1340             }, "keyup");
1341 
1342             // Initialize form
1343             form.init();
1344             this.forms.createForm = form;
1345 
1346          },
1347 
1348          /**
1349           * Called by the ConsolePanelHandler when this panel shall be loaded
1350           *
1351           * @method onBeforeShow
1352           */
1353          onBeforeShow: function ConsoleGroups_CreatePanelHandler_onBeforeShow()
1354          {
1355             // Hide the main panel area before it is displayed - so we don't show
1356             // old data to the user before the onShow() method paints the results
1357             Dom.setStyle(parent.id + "-create-main", "visibility", "hidden");
1358             this.clear();
1359          },
1360 
1361          /**
1362           * Clears the form fields, makes sure buttons are in correct state and sets focus
1363           *
1364           * @method clear
1365           */
1366          clear: function clear()
1367          {
1368             Dom.get(parent.id + "-create-shortname").value = "";
1369             Dom.get(parent.id + "-create-displayname").value = "";
1370             if (this.forms.createForm !== null)
1371             {
1372                this.forms.createForm.init();
1373             }
1374          },
1375 
1376          /**
1377           * Called by the ConsolePanelHandler when this panel is shown
1378           *
1379           * @method onShow
1380           */
1381          onShow: function ConsoleGroups_CreatePanelHandler_onShow()
1382          {
1383             this._visible = true;
1384             this._refresh = false;
1385             window.scrollTo(0, 0);
1386 
1387             // Make main panel area visible
1388             Dom.setStyle(parent.id + "-create-main", "visibility", "visible");
1389 
1390             Dom.get(parent.id + "-create-shortname").focus();
1391          },
1392 
1393          /**
1394           * Called by the ConsolePanelHandler when this panel is hidden
1395           *
1396           * @method onHide
1397           */
1398          onHide: function ConsoleGroups_CreatePanelHandler_onHide()
1399          {
1400             this._visible = false;
1401          },
1402 
1403          /**
1404           * BUTTON EVENT HANDLERS
1405           */
1406 
1407          /**
1408           * Fired when the Create Group OK button is clicked.
1409           *
1410           * @method onCreateGroupOKClick
1411           * @param e DomEvent
1412           * @param args Event parameters (depends on event type)
1413           */
1414          onCreateGroupOKClick: function ConsoleGroups_CreatePanelHandler_onCreateGroupOKClick(e, args)
1415          {
1416             var successHandler = function(response)
1417             {
1418                window.scrollTo(0, 0);
1419                Alfresco.util.PopupManager.displayMessage(
1420                {
1421                   text: parent._msg("message.create-success")
1422                });
1423                parent.refreshUIState(
1424                {
1425                   "panel": "search",
1426                   "refresh": "true"
1427                });
1428             };
1429             this._createGroup(successHandler);
1430          },
1431 
1432          /**
1433           * Fired when the Create Group Cancel button is clicked.
1434           *
1435           * @method onCreateGroupCancelClick
1436           * @param e {object} DomEvent
1437           * @param args {array} Event parameters (depends on event type)
1438           */
1439          onCreateGroupCancelClick: function ConsoleGroups_CreatePanelHandler_onCreateGroupCancelClick(e, args)
1440          {
1441             parent.refreshUIState(
1442             {
1443                "panel": "search",
1444                "refresh": this._refresh ? "true" : "false"
1445             });
1446          },
1447 
1448          /**
1449           * Fired when the Create Another Group button is clicked.
1450           *
1451           * @method onCreateGroupAnotherClick
1452           * @param e DomEvent
1453           * @param args Event parameters (depends on event type)
1454           */
1455          onCreateGroupAnotherClick: function ConsoleGroups_CreatePanelHandler_onCreateGroupAnotherClick(e, args)
1456          {
1457             var successHandler = function(response)
1458             {
1459                // Scroll to top and notify user
1460                window.scrollTo(0, 0);
1461                Alfresco.util.PopupManager.displayMessage(
1462                {
1463                   text: parent._msg("message.create-success")
1464                });
1465 
1466                // Make sure we refresh view panel if cancel is clicked
1467                this._refresh = true;
1468 
1469                // Clear old values so new ones can be entered
1470                this.clear();
1471                Dom.get(parent.id + "-create-shortname").focus();
1472             };
1473             this._createGroup(successHandler);
1474          },
1475 
1476          /**
1477           * PRIVATE METHODS
1478           */
1479 
1480          /**
1481           * Create a group, but first check if it already exists
1482           *
1483           * @method _createGroup
1484           * @param handler Handler function to be called on successful creation
1485           * @private
1486           */
1487          _createGroup: function ConsoleGroups_CreatePanelHandler__createGroup(successHandler)
1488          {
1489             var me = this;
1490             var shortName = YAHOO.lang.trim(Dom.get(parent.id + "-create-shortname").value);
1491             parent.getParentGroups(shortName,
1492             {
1493                fn: function(groups)
1494                {
1495                   if (groups)
1496                   {
1497                      // The group alredy existed, now let's see if the identifer already is placed under this group
1498                      var alreadyThere = false;
1499                      var parentStr = "";
1500                      for (var i = 0; i < groups.length; i++)
1501                      {
1502                         parentStr += groups[i].displayName + (i < groups.length - 1 ? ", " : "");
1503                      }
1504                      parentStr = parentStr.length > 0 ? parentStr : parent._msg("label.theroot");
1505                      Alfresco.util.PopupManager.displayPrompt(
1506                      {
1507                         text: parent._msg("message.confirm.add", shortName, parentStr, parent.group ? parent.group : parent._msg("label.theroot")),
1508                         buttons: [
1509                            {
1510                               text: parent._msg("button.ok"),
1511                               handler: function ConsoleGroups__createGroup_confirmOk()
1512                               {
1513                                  // Hide Prompt
1514                                  this.destroy();
1515                                  if (parent.group)
1516                                  {
1517                                     me._createGroupAfterExistCheck.call(me, successHandler);
1518                                  }
1519                                  else
1520                                  {
1521                                     // todo implement when webscript api supports it
1522                                     // This isn't supported by the webscript api yet
1523                                     Alfresco.util.PopupManager.displayPrompt(
1524                                     {
1525                                        title: parent._msg("message.failure"),
1526                                        text: parent._msg("message.noAddGroupFromRootSupport")
1527                                     });
1528                                     return;
1529 
1530                                  }
1531                               }
1532                            },
1533                            {
1534                               text: parent._msg("button.cancel"),
1535                               handler: function ConsoleGroups__createGroup_confirmCancel()
1536                               {
1537                                  // Hide prompt
1538                                  this.destroy();
1539                               },
1540                               isDefault: true
1541                            }]
1542                      });
1543                   }
1544                   else
1545                   {
1546                      // Group didn't exist go ahead and create it
1547                      me._createGroupAfterExistCheck.call(me, successHandler);
1548                   }
1549                },
1550                scope: this
1551             }, "message.create-failure");
1552          },
1553 
1554          /**
1555           * Create the group
1556           *
1557           * @method _createGroupAfterExistCheck
1558           * @param handler {function} Handler function to be called on successful creation
1559           * @private
1560           */
1561          _createGroupAfterExistCheck: function ConsoleGroups_CreatePanelHandler__createGroupAfterExistCheck(successHandler)
1562          {
1563             // gather up the data for our JSON PUT request
1564             var shortName = YAHOO.lang.trim(Dom.get(parent.id + "-create-shortname").value);
1565             var displayName = YAHOO.lang.trim(Dom.get(parent.id + "-create-displayname").value);
1566             displayName = displayName == "" ? undefined : displayName;
1567             var groupObj = {};
1568 
1569             var url = Alfresco.constants.PROXY_URI + "api/";
1570             var sh = successHandler;
1571             if (parent.group && parent.group.length > 0)
1572             {
1573                url += "groups/" + encodeURIComponent(parent.group) + "/children/GROUP_" + encodeURIComponent(shortName);
1574                sh = function(response)
1575                {
1576                   if (displayName && shortName != displayName)
1577                   {
1578                      /**
1579                       * When a group is created by adding it to a parent group its not possible to
1580                       * set the displayName in the same call, then another call must be made to
1581                       * update the display name.
1582                       */
1583                      groupObj.displayName = displayName;
1584                      parent.panelHandlers.updatePanelHandler.updateGroupRequest(shortName, groupObj,
1585                      {
1586                         fn: successHandler,
1587                         scope: this
1588                      });
1589                   }
1590                   else
1591                   {
1592                      successHandler.call(this, response);
1593                   }
1594                };
1595             }
1596             else
1597             {
1598                url += "rootgroups/" + encodeURIComponent(shortName);
1599                if (displayName)
1600                {
1601                   groupObj.displayName = displayName;
1602                }
1603             }
1604 
1605             Alfresco.util.Ajax.jsonPost(
1606             {
1607                url: url,
1608                dataObj: groupObj,
1609                successCallback:
1610                {
1611                   fn: sh,
1612                   scope: this
1613                },
1614                failureCallback:
1615                {
1616                   fn: function(o)
1617                   {
1618                      var obj = YAHOO.lang.JSON.parse(o.serverResponse.responseText);
1619                      Alfresco.util.PopupManager.displayPrompt(
1620                      {
1621                         title: parent._msg("message.failure"),
1622                         text: parent._msg("message.create-failure", obj.message)
1623                      });
1624                   },
1625                   scope: this
1626                }
1627             });
1628          }
1629 
1630       });
1631       this.panelHandlers.createPanelHandler = new CreatePanelHandler();
1632 
1633 
1634       /* Update Group Panel Handler */
1635       UpdatePanelHandler = function UpdatePanelHandler_constructor()
1636       {
1637          UpdatePanelHandler.superclass.constructor.call(this, "update");
1638       };
1639 
1640       YAHOO.extend(UpdatePanelHandler, Alfresco.ConsolePanelHandler,
1641       {
1642          /**
1643           * INSTANCE VARIABLES
1644           */
1645 
1646          /**
1647           * Keeps track if this panel is visble or not
1648           *
1649           * @property _visible
1650           * @type Boolean
1651           */
1652          _visible: false,
1653 
1654          /**
1655           * PANEL LIFECYCLE CALLBACKS
1656           */
1657 
1658          /**
1659           * Called by the ConsolePanelHandler when this panel shall be loaded
1660           *
1661           * @method onLoad
1662           */
1663          onLoad: function ConsoleGroups_UpdatePanelHandler_onLoad()
1664          {
1665             // Buttons
1666             this.widgets.updategroupSaveButton = new YAHOO.widget.Button(parent.id + "-updategroup-save-button",
1667             {
1668                type: "button"
1669             });
1670             this.widgets.updategroupSaveButton.on("click", this.onUpdateGroupOKClick, null, this);
1671             this.widgets.updategroupCancelButton = new YAHOO.widget.Button(parent.id + "-updategroup-cancel-button",
1672             {
1673                type: "button"
1674             });
1675             this.widgets.updategroupCancelButton.on("click", this.onUpdateGroupCancelClick, null, this);
1676 
1677             // Form definition
1678             var form = new Alfresco.forms.Form(parent.id + "-update-form");
1679             form.setSubmitElements(this.widgets.updategroupSaveButton);
1680             form.setShowSubmitStateDynamically(true);
1681 
1682             // Form field validation
1683             form.addValidation(parent.id + "-update-displayname", Alfresco.forms.validation.mandatory, null, "keyup");
1684             form.addValidation(parent.id + "-update-displayname", Alfresco.forms.validation.length,
1685             {
1686                min: 3,
1687                max: 255,
1688                crop: true,
1689                includeWhitespace: false
1690             }, "keyup");
1691 
1692             // Initialise the form
1693             form.init();
1694             this.forms.updateForm = form;
1695          },
1696 
1697          /**
1698           * Called by the ConsolePanelHandler just before this panel is about to be shown
1699           *
1700           * @method onBeforeShow
1701           */
1702          onBeforeShow: function ConsoleGroups_UpdatePanelHandler_onBeforeShow()
1703          {
1704             // Hide the main panel area before it is displayed - so we don't show
1705             // old data to the user before the Update() method paints the results
1706             Dom.setStyle(parent.id + "-update-main", "visibility", "hidden");
1707          },
1708 
1709          /**
1710           * Called by the ConsolePanelHandler when this panel is shown
1711           *
1712           * @method onShow
1713           */
1714          onShow: function ConsoleGroups_UpdatePanelHandler_onShow()
1715          {
1716             this._visible = true;
1717          },
1718 
1719          /**
1720           * Called by the ConsolePanelHandler when this panel is hidden
1721           *
1722           * @method onHide
1723           */
1724          onHide: function ConsoleGroups_UpdatePanelHandler_onHide()
1725          {
1726             this._visible = false;
1727          },
1728 
1729          /**
1730           * Called by the ConsolePanelHandler when this panel shall update its appearance
1731           *
1732           * @method onUpdate
1733           */
1734          onUpdate: function ConsoleGroups_UpdatePanelHandler_onUpdate()
1735          {
1736             var success = function(o)
1737             {
1738                // Properties section fields
1739                var group = o.json.data;
1740                Dom.get(parent.id + "-update-title").innerHTML = $html(group.displayName);
1741                Dom.get(parent.id + "-update-shortname").innerHTML = $html(group.shortName);
1742                Dom.get(parent.id + "-update-displayname").value = group.displayName;
1743 
1744                // Make sure buttons are in the correct state
1745                if (this.forms.updateForm)
1746                {
1747                   this.forms.updateForm.init();
1748                }
1749 
1750                // Make main panel area visible and focus
1751                window.scrollTo(0, 0);
1752                Dom.setStyle(parent.id + "-update-main", "visibility", "visible");
1753                Dom.get(parent.id + "-update-displayname").focus();
1754             };
1755 
1756             // make an ajax call to get group details
1757             Alfresco.util.Ajax.jsonGet(
1758             {
1759                url: Alfresco.constants.PROXY_URI + "api/groups/" + encodeURIComponent(parent.group),
1760                successCallback:
1761                {
1762                   fn: success,
1763                   scope: this
1764                },
1765                failureMessage: parent._msg("message.getgroup-failure", $html(parent.group))
1766             });
1767          },
1768 
1769          /**
1770           * BUTTON EVENT HANDLERS
1771           */
1772 
1773          /**
1774           * Fired when the Update User OK button is clicked.
1775           *
1776           * @method onUpdateGroupOKClick
1777           * @param e DomEvent
1778           * @param args Event parameters (depends on event type)
1779           */
1780          onUpdateGroupOKClick: function ConsoleGroups_UpdatePanelHandler_onUpdateGroupOKClick(e, args)
1781          {
1782             var handler = function(res)
1783             {
1784                window.scrollTo(0, 0);
1785                Alfresco.util.PopupManager.displayMessage(
1786                {
1787                   text: parent._msg("message.update-success")
1788                });
1789 
1790                var state = {"panel": "search", "refresh": "true"};
1791                if (parent.query)
1792                {
1793                   // If this panel was triggered from the search list, send back the query so the list will be displayed
1794                   state.query = parent.query;
1795                }
1796                parent.refreshUIState(state);
1797             };
1798             this._updateGroup(handler);
1799          },
1800 
1801          /**
1802           * Fired when the Update Group Cancel button is clicked.
1803           *
1804           * @method onUpdateGroupCancelClick
1805           * @param e {object} DomEvent
1806           * @param args {array} Event parameters (depends on event type)
1807           */
1808          onUpdateGroupCancelClick: function ConsoleGroups_UpdatePanelHandler_onUpdateGroupCancelClick(e, args)
1809          {
1810             var state = {"panel": "search", "refresh": "false"};
1811             if (parent.query)
1812             {
1813                // If this panel was triggered from the search list, send back the query so the list will be displayed
1814                state.query = parent.query;
1815             }
1816             parent.refreshUIState(state);
1817          },
1818 
1819          /**
1820           * PUBLIC METHODS
1821           */
1822 
1823          /**
1824           * Update a group - returning true on success, false on any error.
1825           *
1826           * @method updateGroupRequest
1827           * @param successCallback {function} Success callback to be called on successful update
1828           * @private
1829           */
1830          updateGroupRequest: function ConsoleGroups_UpdatePanelHandler_updateGroupRequest(shortName, groupObj, successCallback)
1831          {
1832             Alfresco.util.Ajax.jsonPut(
1833             {
1834                url: Alfresco.constants.PROXY_URI + "api/groups/" + encodeURIComponent(shortName),
1835                dataObj: groupObj,
1836                successCallback: successCallback,
1837                failureCallback:
1838                {
1839                   fn: function(o)
1840                   {
1841                      Alfresco.util.PopupManager.displayPrompt(
1842                      {
1843                         title: parent._msg("message.failure"),
1844                         text: parent._msg("message.update-failure", o.json.message)
1845                      });
1846                   },
1847                   scope: this
1848                }
1849             });
1850          },
1851 
1852          /**
1853           * PRIVATE METHODS
1854           */
1855 
1856          /**
1857           * Update a group - returning true on success, false on any error.
1858           *
1859           * @method _updateGroup
1860           * @param successHandler Handler function to be called on successful update
1861           * @private
1862           */
1863          _updateGroup: function ConsoleGroups_UpdatePanelHandler__updateGroup(successHandler)
1864          {
1865             this.updateGroupRequest(parent.group,
1866             {
1867                displayName: YAHOO.lang.trim(Dom.get(parent.id + "-update-displayname").value)
1868             },
1869             {
1870                fn: successHandler,
1871                scope: this
1872             });
1873          }
1874 
1875       });
1876       this.panelHandlers.updatePanelHandler = new UpdatePanelHandler();
1877 
1878       return this;
1879    };
1880 
1881    YAHOO.extend(Alfresco.ConsoleGroups, Alfresco.ConsoleTool,
1882    {
1883 
1884       /* STATES */
1885 
1886       /**
1887        * The query to use in a search in the panel
1888        *
1889        * @property query
1890        * @type string
1891        * @default null
1892        */
1893       query: null,
1894 
1895       /**
1896        * Decides if browse panels data needs to be refreshed
1897        *
1898        * @property refresh
1899        * @type boolean
1900        * @default false
1901        */
1902       refresh: false,
1903 
1904       /**
1905        * The current group
1906        *
1907        * @property group
1908        * @type string
1909        * @default null
1910        */
1911       group: null,
1912 
1913       /**
1914        * The display name for the current group
1915        *
1916        * @property groupDisplayName
1917        * @type string
1918        * @default null
1919        */
1920       groupDisplayName: null,
1921 
1922       /**
1923        * Object container for initialization options
1924        *
1925        * @property options
1926        * @type object
1927        */
1928       options:
1929       {
1930          /**
1931           * Number of characters required for a search.
1932           *
1933           * @property minSearchTermLength
1934           * @type int
1935           * @default 1
1936           */
1937          minSearchTermLength: 1,
1938 
1939          /**
1940           * Maximum number of items to display in the results list
1941           *
1942           * @property maxSearchResults
1943           * @type int
1944           * @default 100
1945           */
1946          maxSearchResults: 100,
1947 
1948          /**
1949           * Maximum number of groups & users to rdisplay at the same time in each column
1950           *
1951           * @property maxPageSize
1952           * @type int
1953           * @default 50
1954           */
1955          maxPageSize: 50
1956       },
1957 
1958       /**
1959        * Fired by YUI when parent element is available for scripting.
1960        * Component initialisation, including instantiation of YUI widgets and event listener binding.
1961        *
1962        * @method onReady
1963        */
1964       onReady: function ConsoleGroups_onReady()
1965       {
1966          Alfresco.ConsoleGroups.superclass.onReady.call(this);
1967       },
1968 
1969       /**
1970        * YUI WIDGET EVENT HANDLERS
1971        * Handlers for standard events fired from YUI widgets, e.g. "click"
1972        */
1973 
1974       /**
1975        * History manager state change event handler (override base class)
1976        *
1977        * @method onStateChanged
1978        * @param e {object} DomEvent
1979        * @param args {array} Event parameters (depends on event type)
1980        */
1981       onStateChanged: function ConsoleGroups_onStateChanged(e, args)
1982       {
1983          // Clear old states
1984          this.query = undefined;
1985          this.refresh = undefined;
1986          this.group = undefined;
1987          this.groupDisplayName = undefined;
1988 
1989          var state = this.decodeHistoryState(args[1].state);
1990          if (state.query !== undefined)
1991          {
1992             this.query = state.query;
1993          }
1994          if (state.refresh)
1995          {
1996             this.refresh = state.refresh == "true" ? true : false;
1997          }
1998          if (state.group)
1999          {
2000             this.group = state.group;
2001          }
2002          if (state.groupDisplayName)
2003          {
2004             this.groupDisplayName = state.groupDisplayName;
2005          }
2006          if (state.showAll)
2007          {
2008             this.showAll = state.showAll == "true" ? true : false;
2009             // reset group browser url
2010             var rootUrl = Alfresco.constants.PROXY_URI + "api/rootgroups" +
2011                (this.showAll ? "" : "?zone=APP.DEFAULT");
2012             this.panelHandlers.searchPanelHandler.widgets.columnbrowser.load([rootUrl] , true);
2013          }
2014          
2015          // test if panel has actually changed?
2016          if (state.panel)
2017          {
2018             this.showPanel(state.panel);
2019          }
2020 
2021          if (this.currentPanelId === "search")
2022          {
2023             this.updateCurrentPanel();
2024          }
2025          else if (this.currentPanelId === "create" ||
2026                   (state.group && (this.currentPanelId === "view" || this.currentPanelId === "update")))
2027          {
2028             this.updateCurrentPanel();
2029          }
2030       },
2031 
2032       /**
2033        * New Group event handler
2034        *
2035        * @method onNewGroup
2036        * @param e {object} DomEvent
2037        * @param args {array} Event parameters (depends on event type)
2038        */
2039       onNewGroup: function ConsoleGroups_onNewGroup(e, args)
2040       {
2041          var parentGroup = args[1].group;
2042          this.refreshUIState(
2043          {
2044             "panel": "create",
2045             "group": parentGroup
2046          });
2047       },
2048 
2049       /**
2050        * Update Group event handler
2051        *
2052        * @method onUpdateGroup
2053        * @param e {object} DomEvent
2054        * @param args {array} Event parameters (depends on event type)
2055        */
2056       onUpdateGroup: function ConsoleGroups_onUpdateGroup(e, args)
2057       {
2058          var group = args[1].group;
2059          var query = args[1].query;
2060          var state = {
2061             "panel": "update",
2062             "group": group
2063          };
2064          // Remember query if cancel is clicked
2065          if (query)
2066          {
2067             state.query = query;
2068          }
2069          this.refreshUIState(state);
2070       },
2071 
2072       /**
2073        * Encode state object into a packed string for use as url history value.
2074        * Override base class.
2075        *
2076        * @method encodeHistoryState
2077        * @param obj {object} state object
2078        * @private
2079        */
2080       encodeHistoryState: function ConsoleGroups_encodeHistoryState(obj)
2081       {
2082          // wrap up current state values
2083          var stateObj = {};
2084          if (this.currentPanelId !== "")
2085          {
2086             stateObj.panel = this.currentPanelId;
2087          }
2088 
2089          // convert to encoded url history state - overwriting with any supplied values
2090          var state = "";
2091          if (obj.panel || stateObj.panel)
2092          {
2093             state += "panel=" + encodeURIComponent(obj.panel ? obj.panel : stateObj.panel);
2094          }
2095          if (obj.group)
2096          {
2097             state += state.length > 0 ? "&" : "";
2098             state += "group=" + encodeURIComponent(obj.group);
2099          }
2100          if (obj.query !== undefined)
2101          {
2102             state += state.length > 0 ? "&" : "";
2103             state += "query=" + encodeURIComponent(obj.query);
2104          }
2105          if (obj.refresh)
2106          {
2107             state += state.length > 0 ? "&" : "";
2108             state += "refresh=" + encodeURIComponent(obj.refresh);
2109          }
2110          if (obj.showAll !== undefined)
2111          {
2112             state += state.length > 0 ? "&" : "";
2113             state += "showAll=" + encodeURIComponent(obj.showAll);
2114          }
2115          return state;
2116       },
2117 
2118       /**
2119        * Helper method for getting the parent groups for group with identifier shortName
2120        *
2121        * @method _getParentGroups
2122        * @param shortName the group identifier
2123        * @param successCallback Callback object Called with the groups as the argument or null if group doesn't exist
2124        * @param failureMessage Displayed if an error (other than 404) occurs
2125        */
2126       getParentGroups: function ConsoleGroups_getParentGroups(shortName, successCallback, failureMessage)
2127       {
2128          Alfresco.util.Ajax.jsonGet(
2129          {
2130             url:  Alfresco.constants.PROXY_URI + "api/groups/" + encodeURIComponent(shortName) + "/parents?level=ALL&maxSize=10",
2131             successCallback:
2132             {
2133                fn: function(o)
2134                {
2135                   var groups = o.json.data ? o.json.data : [];
2136 
2137                   // Since we do
2138                   successCallback.fn.call(successCallback.scope ? successCallback.scope : this, groups);
2139                },
2140                scope: this
2141             },
2142             failureCallback:
2143             {
2144                fn: function(o)
2145                {
2146                   if (o.serverResponse.status == 404)
2147                   {
2148                      // group didn't exist just continue
2149                      successCallback.fn.call(successCallback.scope ? successCallback.scope : this, null);
2150                   }
2151                   else
2152                   {
2153                      // Notify the user that an error occured
2154                      Alfresco.util.PopupManager.displayPrompt(
2155                      {
2156                         title: this._msg("message.failure"),
2157                         text: this._msg(failureMessage, o.json.message)
2158                      });
2159                   }
2160                },
2161                scope: this
2162             }
2163          });
2164       },
2165 
2166       /**
2167        * Gets a custom message
2168        *
2169        * @method _msg
2170        * @param messageId {string} The messageId to retrieve
2171        * @return {string} The custom message
2172        * @private
2173        */
2174       _msg: function ConsoleGroups__msg(messageId)
2175       {
2176          return Alfresco.util.message.call(this, messageId, "Alfresco.ConsoleGroups", Array.prototype.slice.call(arguments).slice(1));
2177       }
2178    });
2179 })();