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  * DocumentList Toolbar component.
 22  * 
 23  * @namespace Alfresco
 24  * @class Alfresco.DocListToolbar
 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       $combine = Alfresco.util.combinePaths,
 40       $siteURL = Alfresco.util.siteURL;
 41 
 42    /**
 43     * Preferences
 44     */
 45    var PREFERENCES_ROOT = "org.alfresco.share.documentList",
 46       PREF_HIDE_NAVBAR = PREFERENCES_ROOT + ".hideNavBar";
 47    
 48    /**
 49     * DocListToolbar constructor.
 50     * 
 51     * @param {String} htmlId The HTML id of the parent element
 52     * @return {Alfresco.DocListToolbar} The new DocListToolbar instance
 53     * @constructor
 54     */
 55    Alfresco.DocListToolbar = function(htmlId)
 56    {
 57       Alfresco.DocListToolbar.superclass.constructor.call(this, "Alfresco.DocListToolbar", htmlId, ["button", "menu", "container"]);
 58       
 59       // Initialise prototype properties
 60       this.selectedFiles = [];
 61       this.currentFilter = {};
 62       this.dynamicControls = [];
 63       this.doclistMetadata = {};
 64       this.actionsView = "browse";
 65 
 66       // Decoupled event listeners
 67       YAHOO.Bubbling.on("filterChanged", this.onFilterChanged, this);
 68       YAHOO.Bubbling.on("deactivateAllControls", this.onDeactivateAllControls, this);
 69       YAHOO.Bubbling.on("deactivateDynamicControls", this.onDeactivateDynamicControls, this);
 70       YAHOO.Bubbling.on("selectedFilesChanged", this.onSelectedFilesChanged, this);
 71       YAHOO.Bubbling.on("userAccess", this.onUserAccess, this);
 72       YAHOO.Bubbling.on("doclistMetadata", this.onDoclistMetadata, this);
 73       YAHOO.Bubbling.on("showFileUploadDialog", this.onFileUpload, this);
 74       YAHOO.Bubbling.on("dropTargetOwnerRequest", this.onDropTargetOwnerRequest, this);
 75       YAHOO.Bubbling.on("documentDragOver", this.onDocumentDragOver, this);
 76       YAHOO.Bubbling.on("documentDragOut", this.onDocumentDragOut, this);
 77       YAHOO.Bubbling.on("registerAction", this.onRegisterAction, this);
 78 
 79       return this;
 80    };
 81 
 82    /**
 83     * Extend from Alfresco.component.Base
 84     */
 85    YAHOO.extend(Alfresco.DocListToolbar, Alfresco.component.Base);
 86    
 87    /**
 88     * Augment prototype with Actions module
 89     */
 90    YAHOO.lang.augmentProto(Alfresco.DocListToolbar, Alfresco.doclib.Actions);
 91    
 92    /**
 93     * Augment prototype with main class implementation, ensuring overwrite is enabled
 94     */
 95    YAHOO.lang.augmentObject(Alfresco.DocListToolbar.prototype,
 96    {
 97       /**
 98        * Object container for initialization options
 99        *
100        * @property options
101        * @type object
102        */
103       options:
104       {
105          /**
106           * Current siteId.
107           * 
108           * @property siteId
109           * @type string
110           */
111          siteId: null,
112 
113          /**
114           * ContainerId representing root container
115           *
116           * @property containerId
117           * @type string
118           * @default "documentLibrary"
119           */
120          containerId: "documentLibrary",
121 
122          /**
123           * Number of multi-file uploads before grouping the Activity Post
124           *
125           * @property groupActivitiesAt
126           * @type int
127           * @default 5
128           */
129          groupActivitiesAt: 5,
130          
131          /**
132           * Flag indicating whether navigation bar is visible or not.
133           * 
134           * @property hideNavBar
135           * @type boolean
136           */
137          hideNavBar: false,
138          
139          /**
140           * Google Docs enabled/disabled flag.
141           * 
142           * @property googleDocsEnabled
143           * @type boolean
144           * @default false
145           */
146          googleDocsEnabled: false,
147 
148          /**
149           * Whether the Repo Browser is in use or not
150           *
151           * @property repositoryBrowsing
152           * @type boolean
153           */
154          repositoryBrowsing: true,
155 
156          /**
157           * Decides it the title shall be displayed next to the name if it contains a value that is different from the name
158           *
159           * @property useTitle
160           * @type boolean
161           * @default true
162           */
163          useTitle: true
164       },
165       
166       /**
167        * Current path being browsed.
168        * 
169        * @property currentPath
170        * @type string
171        */
172       currentPath: "",
173 
174       /**
175        * Current filter to choose toolbar view and populate description.
176        * 
177        * @property currentFilter
178        * @type string
179        */
180       currentFilter: null,
181 
182       /**
183        * FileUpload module instance.
184        * 
185        * @property fileUpload
186        * @type Alfresco.FileUpload
187        */
188       fileUpload: null,
189 
190       /**
191        * Array of selected states for visible files.
192        * 
193        * @property selectedFiles
194        * @type array
195        */
196       selectedFiles: null,
197 
198       /**
199        * Folder Details Url for last breadcrumb
200        * 
201        * @property folderDetailsUrl
202        * @type string
203        */
204       folderDetailsUrl: null,
205 
206       /**
207        * Dynamic controls that take part in the deactivateDynamicControls event
208        * 
209        * @property dynamicControls
210        * @type array
211        */
212       dynamicControls: null,
213 
214       /**
215        * Metadata returned by doclist data webscript
216        *
217        * @property doclistMetadata
218        * @type object
219        * @default null
220        */
221       doclistMetadata: null,
222 
223       /**
224        * Fired by YUI when parent element is available for scripting.
225        * Component initialisation, including instantiation of YUI widgets and event listener binding.
226        *
227        * @method onReady
228        */
229       onReady: function DLTB_onReady()
230       {
231          // New Content menu button
232          this.widgets.createContent = Alfresco.util.createYUIButton(this, "createContent-button", this.onCreateContent,
233          {
234             type: "menu", 
235             menu: "createContent-menu",
236             lazyloadmenu: false,
237             disabled: true,
238             value: "CreateChildren"
239          });
240          // Make sure we load sub menu lazily with data on each click
241          var templateNodesMenus = this.widgets.createContent.getMenu().getSubmenus(),
242             templateNodesMenu = templateNodesMenus.length > 0 ? templateNodesMenus[0] : null;
243          if (templateNodesMenu)
244          {
245             templateNodesMenu.subscribe("beforeShow", this.onCreateByTemplateNodeBeforeShow, this, true);
246             templateNodesMenu.subscribe("click", this.onCreateByTemplateNodeClick, this, true);
247          }
248          this.dynamicControls.push(this.widgets.createContent);
249 
250          // New Folder button: user needs "create" access
251          this.widgets.newFolder = Alfresco.util.createYUIButton(this, "newFolder-button", this.onNewFolder,
252          {
253             disabled: true,
254             value: "CreateChildren"
255          });
256          this.dynamicControls.push(this.widgets.newFolder);
257          
258          // File Upload button: user needs  "CreateChildren" access
259          this.widgets.fileUpload = Alfresco.util.createYUIButton(this, "fileUpload-button", this.onFileUpload,
260          {
261             disabled: true,
262             value: "CreateChildren"
263          });
264          this.dynamicControls.push(this.widgets.fileUpload);
265 
266          // Selected Items menu button
267          this.widgets.selectedItems = Alfresco.util.createYUIButton(this, "selectedItems-button", this.onSelectedItems,
268          {
269             type: "menu", 
270             menu: "selectedItems-menu",
271             lazyloadmenu: false,
272             disabled: true
273          });
274          this.dynamicControls.push(this.widgets.selectedItems);
275 
276          // Hide/Show NavBar button
277          this.widgets.hideNavBar = Alfresco.util.createYUIButton(this, "hideNavBar-button", this.onHideNavBar,
278          {
279             type: "checkbox",
280             checked: this.options.hideNavBar
281          });
282          if (this.widgets.hideNavBar !== null)
283          {
284             this.widgets.hideNavBar.set("title", this.msg(this.options.hideNavBar ? "button.navbar.show" : "button.navbar.hide"));
285             Dom.setStyle(this.id + "-navBar", "display", this.options.hideNavBar ? "none" : "block");
286             this.dynamicControls.push(this.widgets.hideNavBar);
287          }
288 
289          // RSS Feed link button
290          this.widgets.rssFeed = Alfresco.util.createYUIButton(this, "rssFeed-button", null, 
291          {
292             type: "link"
293          });
294          this.dynamicControls.push(this.widgets.rssFeed);
295 
296          // Folder Up Navigation button
297          this.widgets.folderUp =  Alfresco.util.createYUIButton(this, "folderUp-button", this.onFolderUp,
298          {
299             disabled: true,
300             title: this.msg("button.up")
301          });
302          this.dynamicControls.push(this.widgets.folderUp);
303 
304          // DocLib Actions module
305          this.modules.actions = new Alfresco.module.DoclibActions();
306          
307          // Reference to Document List component
308          this.modules.docList = Alfresco.util.ComponentManager.findFirst("Alfresco.DocumentList");
309 
310          // Preferences service
311          this.services.preferences = new Alfresco.service.Preferences();
312 
313          // Finally show the component body here to prevent UI artifacts on YUI button decoration
314          Dom.setStyle(this.id + "-body", "visibility", "visible");
315       },
316       
317 
318       /**
319        * YUI WIDGET EVENT HANDLERS
320        * Handlers for standard events fired from YUI widgets, e.g. "click"
321        */
322 
323       /**
324        * Create Content menu click handler for create content menu items (not create by template node menu items)
325        *
326        * @method onCreateContent
327        * @param sType {string} Event type, e.g. "click"
328        * @param aArgs {array} Arguments array, [0] = DomEvent, [1] = EventTarget
329        * @param p_obj {object} Object passed back from subscribe method
330        */
331       onCreateContent: function DLTB_onCreateContent(sType, aArgs, p_obj)
332       {
333          var eventTarget = aArgs[1],
334             anchor = eventTarget.element.getElementsByTagName("a")[0];
335 
336          // Make sure a create content menu item was clicked (not a template node)
337          if (eventTarget.parent === this.widgets.createContent.getMenu() && anchor && anchor.nodeName == "A")
338          {
339             anchor.href = YAHOO.lang.substitute(anchor.href,
340             {
341                nodeRef: this.doclistMetadata.parent.nodeRef
342             });
343 
344             // Portlet fix: parameter might be encoded
345             if (anchor.href.indexOf("%7BnodeRef%7D") !== -1)
346             {
347                anchor.href = anchor.href.replace("%7BnodeRef%7D", encodeURIComponent(this.doclistMetadata.parent.nodeRef));
348             }
349          }
350       },
351 
352       /**
353        * Create Content Template Node menu beforeShow handler
354        *
355        * @method onCreateByTemplateNodeBeforeShow
356        */
357       onCreateByTemplateNodeBeforeShow: function DLTB_onCreateByTemplateNodeBeforeShow()
358       {
359          // Display loading message
360          var templateNodesMenu = this.widgets.createContent.getMenu().getSubmenus()[0];
361          if (templateNodesMenu.getItems().length == 0)
362          {
363             templateNodesMenu.clearContent();
364             templateNodesMenu.addItem(this.msg("label.loading"));
365             templateNodesMenu.render();
366 
367             // Load template nodes
368             Alfresco.util.Ajax.jsonGet(
369             {
370                url: Alfresco.constants.PROXY_URI + "slingshot/doclib/node-templates",
371                successCallback:
372                {
373                   fn: function(response, menu)
374                   {
375                      var nodes = response.json.data,
376                         menuItems = [],
377                         name;
378                      for (var i = 0, il = nodes.length; i < il; i++)
379                      {
380                         node = nodes[i];
381                         name = $html(node.name);
382                         if (node.title && node.title !== node.name && this.options.useTitle)
383                         {
384                            name += '<span class="title">(' + $html(node.title) + ')</span>';
385                         }
386                         menuItems.push(
387                         {
388                            text: '<span title="' + $html(node.description) + '">' + name +'</span>',
389                            value: node
390                         });
391                      }
392                      if (menuItems.length == 0)
393                      {
394                         menuItems.push(this.msg("label.empty"));
395                      }
396                      templateNodesMenu.clearContent();
397                      templateNodesMenu.addItems(menuItems);
398                      templateNodesMenu.render();
399                   },
400                   scope: this
401                }
402             });
403          }
404       },
405 
406       /**
407        * Create Content Template Node sub menu click handler
408        *
409        * @method onCreateContentTemplateNode
410        * @param sType {string} Event type, e.g. "click"
411        * @param aArgs {array} Arguments array, [0] = DomEvent, [1] = EventTarget
412        * @param p_obj {object} Object passed back from subscribe method
413        */
414       onCreateByTemplateNodeClick: function DLTB_onCreateContentTemplateNode(sType, aArgs, p_obj)
415       {
416          // Create content based on a template
417          var node = aArgs[1].value,
418             destination = this.doclistMetadata.parent.nodeRef;
419 
420          // If node is undefined the loading or empty menu items were clicked
421          if (node)
422          {
423             Alfresco.util.Ajax.jsonPost(
424             {
425                url: Alfresco.constants.PROXY_URI + "slingshot/doclib/node-templates",
426                dataObj:
427                {
428                   sourceNodeRef: node.nodeRef,
429                   parentNodeRef: destination
430                },
431                successCallback:
432                {
433                   fn: function (response)
434                   {
435                      // Make sure we get other components to update themselves to show the new content
436                      YAHOO.Bubbling.fire("nodeCreated",
437                      {
438                         name: node.name,
439                         parentNodeRef: destination,
440                         highlightFile: response.json.name
441                      });
442                   }
443                },
444                successMessage: this.msg("message.create-content-by-template-node.success", node.name),
445                failureMessage: this.msg("message.create-content-by-template-node.failure", node.name)
446             });
447          }
448       },
449 
450       /**
451        * New Folder button click handler
452        *
453        * @method onNewFolder
454        * @param e {object} DomEvent
455        * @param p_obj {object} Object passed back from addListener method
456        */
457       onNewFolder: function DLTB_onNewFolder(e, p_obj)
458       {
459          var destination = this.doclistMetadata.parent.nodeRef;
460 
461          // Intercept before dialog show
462          var doBeforeDialogShow = function DLTB_onNewFolder_doBeforeDialogShow(p_form, p_dialog)
463          {
464             Dom.get(p_dialog.id + "-dialogTitle").innerHTML = this.msg("label.new-folder.title");
465             Dom.get(p_dialog.id + "-dialogHeader").innerHTML = this.msg("label.new-folder.header");
466          };
467          
468          var templateUrl = YAHOO.lang.substitute(Alfresco.constants.URL_SERVICECONTEXT + "components/form?itemKind={itemKind}&itemId={itemId}&destination={destination}&mode={mode}&submitType={submitType}&formId={formId}&showCancelButton=true",
469          {
470             itemKind: "type",
471             itemId: "cm:folder",
472             destination: destination,
473             mode: "create",
474             submitType: "json",
475             formId: "doclib-common"
476          });
477 
478          // Using Forms Service, so always create new instance
479          var createFolder = new Alfresco.module.SimpleDialog(this.id + "-createFolder");
480 
481          createFolder.setOptions(
482          {
483             width: "33em",
484             templateUrl: templateUrl,
485             actionUrl: null,
486             destroyOnHide: true,
487             doBeforeDialogShow:
488             {
489                fn: doBeforeDialogShow,
490                scope: this
491             },
492             onSuccess:
493             {
494                fn: function DLTB_onNewFolder_success(response)
495                {
496                   var folderName = response.config.dataObj["prop_cm_name"];
497                   YAHOO.Bubbling.fire("folderCreated",
498                   {
499                      name: folderName,
500                      parentNodeRef: destination
501                   });
502                   Alfresco.util.PopupManager.displayMessage(
503                   {
504                      text: this.msg("message.new-folder.success", folderName)
505                   });
506                },
507                scope: this
508             },
509             onFailure:
510             {
511                fn: function DLTB_onNewFolder_failure(response)
512                {
513                   if (response)
514                   {
515                      var folderName = response.config.dataObj["prop_cm_name"];
516                      Alfresco.util.PopupManager.displayMessage(
517                      {
518                         text: this.msg("message.new-folder.failure", folderName)
519                      });
520                   }
521                   else
522                   {
523                      Alfresco.util.PopupManager.displayMessage(
524                      {
525                         text: this.msg("message.failure")
526                      });
527                   }
528                },
529                scope: this
530             }
531          }).show();
532       },
533       
534       /**
535        * File Upload button click handler
536        *
537        * @method onFileUpload
538        * @param e {object} DomEvent
539        * @param p_obj {object|array} Object passed back from addListener method or args from Bubbling event
540        */
541       onFileUpload: function DLTB_onFileUpload(e, p_obj)
542       {
543          if (this.fileUpload === null)
544          {
545             this.fileUpload = Alfresco.getFileUploadInstance();
546          }
547          
548          // Show uploader for multiple files
549          var multiUploadConfig =
550          {
551             siteId: this.options.siteId,
552             containerId: this.options.containerId,
553             uploadDirectory: this.currentPath,
554             filter: [],
555             mode: this.fileUpload.MODE_MULTI_UPLOAD,
556             thumbnails: "doclib",
557             onFileUploadComplete:
558             {
559                fn: this.onFileUploadComplete,
560                scope: this
561             }
562          };
563          this.fileUpload.show(multiUploadConfig);
564 
565          if (YAHOO.lang.isArray(p_obj) && p_obj[1].tooltip)
566          {
567             var balloon = Alfresco.util.createBalloon(this.fileUpload.uploader.id + "-dialog",
568             {
569                html: p_obj[1].tooltip,
570                width: "30em"
571             });
572             balloon.show();
573 
574             this.fileUpload.uploader.widgets.panel.hideEvent.subscribe(function()
575             {
576                balloon.hide()
577             });
578          }
579       },
580       
581       /**
582        * Calls the file Upload button click handler but creates an additional tooltip
583        * with information on Drag-and-Drop as an alternative method for uploading content
584        *
585        * @method onFileUploadWithTooltip
586        * @param e {object} DomEvent
587        * @param p_obj {object} Object passed back from addListener method
588        */
589       onFileUploadWithTooltip: function DLTB_onFileUploadWithTooltip(e, p_obj)
590       {
591          this.onFileUpload(e, p_obj);
592       },
593       
594       /**
595        * File Upload complete event handler
596        *
597        * @method onFileUploadComplete
598        * @param complete {object} Object literal containing details of successful and failed uploads
599        */
600       onFileUploadComplete: function DLTB_onFileUploadComplete(complete)
601       {
602          var success = complete.successful.length, activityData, file;
603          if (success > 0)
604          {
605             if (success < (this.options.groupActivitiesAt || 5))
606             {
607                // Below cutoff for grouping Activities into one
608                for (var i = 0; i < success; i++)
609                {
610                   file = complete.successful[i];
611                   activityData =
612                   {
613                      fileName: file.fileName,
614                      nodeRef: file.nodeRef
615                   };
616                   this.modules.actions.postActivity(this.options.siteId, "file-added", "document-details", activityData);
617                }
618             }
619             else
620             {
621                // grouped into one message
622                activityData =
623                {
624                   fileCount: success,
625                   path: this.currentPath,
626                   parentNodeRef : this.doclistMetadata.parent.nodeRef
627                };
628                this.modules.actions.postActivity(this.options.siteId, "files-added", "documentlibrary", activityData);
629             }
630          }
631       },
632 
633       /**
634        * Selected Items button click handler
635        *
636        * @method onSelectedItems
637        * @param sType {string} Event type, e.g. "click"
638        * @param aArgs {array} Arguments array, [0] = DomEvent, [1] = EventTarget
639        * @param p_obj {object} Object passed back from subscribe method
640        */
641       onSelectedItems: function DLTB_onSelectedItems(sType, aArgs, p_obj)
642       {
643          var domEvent = aArgs[0],
644             eventTarget = aArgs[1];
645 
646          // Check mandatory docList module is present
647          if (this.modules.docList)
648          {
649             // Get the function related to the clicked item
650             var fn = Alfresco.util.findEventClass(eventTarget);
651             if (fn && (typeof this[fn] == "function"))
652             {
653                this[fn].call(this, this.modules.docList.getSelectedFiles());
654             }
655          }
656 
657          Event.preventDefault(domEvent);
658       },
659       
660       /**
661        * Delete Multiple Records.
662        *
663        * @method onActionDelete
664        * @param records {object} Object literal representing one or more file(s) or folder(s) to be actioned
665        */
666       onActionDelete: function DLTB_onActionDelete(records)
667       {
668          var me = this,
669             fileNames = [];
670          
671          for (var i = 0, j = records.length; i < j; i++)
672          {
673             fileNames.push("<span class=\"" + (records[i].jsNode.isContainer ? "folder" : "document") + "\">" + $html(records[i].displayName) + "</span>");
674          }
675          
676          var confirmTitle = this.msg("title.multiple-delete.confirm"),
677             confirmMsg = this.msg("message.multiple-delete.confirm", records.length);
678 
679          confirmMsg += "<div class=\"toolbar-file-list\">" + fileNames.join("") + "</div>";
680 
681          Alfresco.util.PopupManager.displayPrompt(
682          {
683             title: confirmTitle,
684             text: confirmMsg,
685             noEscape: true,
686             modal: true,
687             buttons: [
688             {
689                text: this.msg("button.delete"),
690                handler: function DLTB_onActionDelete_delete()
691                {
692                   this.destroy();
693                   me._onActionDeleteConfirm.call(me, records);
694                }
695             },
696             {
697                text: this.msg("button.cancel"),
698                handler: function DLTB_onActionDelete_cancel()
699                {
700                   this.destroy();
701                },
702                isDefault: true
703             }]
704          });
705       },
706 
707       /**
708        * Delete Multiple Records confirmed.
709        *
710        * @method _onActionDeleteConfirm
711        * @param records {array} Array containing records to be deleted
712        * @private
713        */
714       _onActionDeleteConfirm: function DLTB__onActionDeleteConfirm(records)
715       {
716          var multipleRecords = [], i, ii;
717          for (i = 0, ii = records.length; i < ii; i++)
718          {
719             multipleRecords.push(records[i].jsNode.nodeRef.nodeRef);
720          }
721          
722          // Success callback function
723          var fnSuccess = function DLTB__oADC_success(data, records)
724          {
725             var result;
726             var successCount = 0;
727 
728             // Did the operation succeed?
729             if (!data.json.overallSuccess)
730             {
731                Alfresco.util.PopupManager.displayMessage(
732                {
733                   text: this.msg("message.multiple-delete.failure")
734                });
735                return;
736             }
737 
738             YAHOO.Bubbling.fire("filesDeleted");
739 
740             for (i = 0, ii = data.json.totalResults; i < ii; i++)
741             {
742                result = data.json.results[i];
743                
744                if (result.success)
745                {
746                   successCount++;
747                   
748                   YAHOO.Bubbling.fire(result.type == "folder" ? "folderDeleted" : "fileDeleted",
749                   {
750                      multiple: true,
751                      nodeRef: result.nodeRef
752                   });
753                }
754             }
755 
756             // Activities, in Site mode only
757             if (Alfresco.util.isValueSet(this.options.siteId))
758             {
759                var activityData;
760                if (successCount > 0)
761                {
762                   if (successCount < this.options.groupActivitiesAt)
763                   {
764                      // Below cutoff for grouping Activities into one
765                      for (i = 0; i < successCount; i++)
766                      {
767                         activityData =
768                         {
769                            fileName: data.json.results[i].id,
770                            nodeRef: data.json.results[i].nodeRef,
771                            path: this.currentPath
772                         };
773                         this.modules.actions.postActivity(this.options.siteId, "file-deleted", "documentlibrary", activityData);
774                      }
775                   }
776                   else
777                   {
778                      // grouped into one message
779                      activityData =
780                      {
781                         fileCount: successCount,
782                         path: this.currentPath,
783                         parentNodeRef : this.doclistMetadata.parent.nodeRef
784                      };
785                      this.modules.actions.postActivity(this.options.siteId, "files-deleted", "documentlibrary", activityData);
786                   }
787                }
788             }
789 
790             Alfresco.util.PopupManager.displayMessage(
791             {
792                text: this.msg("message.multiple-delete.success", successCount)
793             });
794          };
795          
796          // Construct the data object for the genericAction call
797          this.modules.actions.genericAction(
798          {
799             success:
800             {
801                callback:
802                {
803                   fn: fnSuccess,
804                   scope: this,
805                   obj: records
806                }
807             },
808             failure:
809             {
810                message: this.msg("message.multiple-delete.failure")
811             },
812             webscript:
813             {
814                method: Alfresco.util.Ajax.DELETE,
815                name: "files"
816             },
817             wait:
818             {
819                message: this.msg("message.multiple-delete.please-wait")
820             },
821             config:
822             {
823                requestContentType: Alfresco.util.Ajax.JSON,
824                dataObj:
825                {
826                   nodeRefs: multipleRecords
827                }
828             }
829          });
830       },
831 
832       /**
833        * Deselect currectly selected records.
834        *
835        * @method onActionDeselectAll
836        */
837       onActionDeselectAll: function DLTB_onActionDeselectAll()
838       {
839          if (this.modules.docList)
840          {
841             this.modules.docList.selectFiles("selectNone");
842          }
843       },
844 
845       /**
846        * Show/Hide navigation bar button click handler
847        *
848        * @method onHideNavBar
849        * @param e {object} DomEvent
850        * @param p_obj {object} Object passed back from addListener method
851        */
852       onHideNavBar: function DLTB_onHideNavBar(e, p_obj)
853       {
854          this.options.hideNavBar = this.widgets.hideNavBar.get("checked");
855          this.widgets.hideNavBar.set("title", this.msg(this.options.hideNavBar ? "button.navbar.show" : "button.navbar.hide"));
856          Dom.setStyle(this.id + "-navBar", "display", this.options.hideNavBar ? "none" : "block");
857          this.services.preferences.set(PREF_HIDE_NAVBAR, this.options.hideNavBar);
858          if (e)
859          {
860             Event.preventDefault(e);
861          }
862       },
863 
864       /**
865        * Folder Up Navigate button click handler
866        *
867        * @method onFolderUp
868        * @param e {object} DomEvent
869        * @param p_obj {object} Object passed back from addListener method
870        */
871       onFolderUp: function DLTB_onFolderUp(e, p_obj)
872       {
873          var newPath = this.currentPath.substring(0, this.currentPath.lastIndexOf("/")),
874             filter = this.currentFilter;
875          
876          filter.filterData = newPath;
877 
878          YAHOO.Bubbling.fire("changeFilter", filter);
879          Event.preventDefault(e);
880       },
881 
882 
883       /**
884        * BUBBLING LIBRARY EVENT HANDLERS FOR PAGE EVENTS
885        * Disconnected event handlers for inter-component event notification
886        */
887 
888       /**
889        * Filter Changed event handler
890        *
891        * @method onFilterChanged
892        * @param layer {object} Event fired
893        * @param args {array} Event parameters (depends on event type)
894        */
895       onFilterChanged: function DLTB_onFilterChanged(layer, args)
896       {
897          var obj = args[1];
898          if (obj && (typeof obj.filterId !== "undefined"))
899          {
900             obj.filterOwner = obj.filterOwner || Alfresco.util.FilterManager.getOwner(obj.filterId);
901 
902             if (obj.filterOwner)
903             {
904                if (this.currentFilter.filterOwner != obj.filterOwner || this.currentFilter.filterId != obj.filterId)
905                {
906                   var filterOwner = obj.filterOwner.split(".")[1],
907                      ownerIdClass = filterOwner + "_" + obj.filterId;
908 
909                   // Obtain array of DIVs we might want to hide
910                   var divs = YAHOO.util.Selector.query('div.hideable', Dom.get(this.id)), div;
911                   for (var i = 0, j = divs.length; i < j; i++)
912                   {
913                      div = divs[i];
914                      if (Dom.hasClass(div, filterOwner) || Dom.hasClass(div, ownerIdClass))
915                      {
916                         Dom.removeClass(div, "toolbar-hidden");
917                      }
918                      else
919                      {
920                         Dom.addClass(div, "toolbar-hidden");
921                      }
922                   }
923                }
924             }
925             
926             Alfresco.logger.debug("DLTB_onFilterChanged", "Old Filter", this.currentFilter);
927             this.currentFilter = Alfresco.util.cleanBubblingObject(obj);
928             Alfresco.logger.debug("DLTB_onFilterChanged", "New Filter", this.currentFilter);
929             
930             if (this.currentFilter.filterId == "path" || this.currentFilter.filterId == "category")
931             {
932                this.currentPath = $combine("/", this.currentFilter.filterData);
933                this._generateBreadcrumb();
934 
935                // Enable/disable the Folder Up button
936                var paths = this.currentPath.split("/");
937                // Check for root path special case
938                if (this.currentPath === "/")
939                {
940                   paths = ["/"];
941                }
942                this.widgets.folderUp.set("disabled", paths.length < 2);
943             }
944             else
945             {
946                this._generateDescription();
947             }
948             this._generateRSSFeedUrl();
949          }
950       },
951 
952       /**
953        * Deactivate All Controls event handler
954        *
955        * @method onDeactivateAllControls
956        * @param layer {object} Event fired
957        * @param args {array} Event parameters (depends on event type)
958        */
959       onDeactivateAllControls: function DLTB_onDeactivateAllControls(layer, args)
960       {
961          var index, fnDisable = Alfresco.util.disableYUIButton;
962          for (index in this.widgets)
963          {
964             if (this.widgets.hasOwnProperty(index))
965             {
966                fnDisable(this.widgets[index]);
967             }
968          }
969       },
970 
971       /**
972        * Deactivate Dynamic Controls event handler.
973        * Only deactivates those controls whose enabled state is evaluated on each update.
974        *
975        * @method onDeactivateDynamicControls
976        * @param layer {object} Event fired
977        * @param args {array} Event parameters (depends on event type)
978        */
979       onDeactivateDynamicControls: function DLTB_onDeactivateDynamicControls(layer, args)
980       {
981          var index, fnDisable = Alfresco.util.disableYUIButton;
982          for (index in this.dynamicControls)
983          {
984             if (this.dynamicControls.hasOwnProperty(index))
985             {
986                fnDisable(this.dynamicControls[index]);
987             }
988          }
989       },
990 
991       /**
992        * User Access event handler
993        *
994        * @method onUserAccess
995        * @param layer {object} Event fired
996        * @param args {array} Event parameters (depends on event type)
997        */
998       onUserAccess: function DLTB_onUserAccess(layer, args)
999       {
1000          var fnSetWidgetAccess = function DLTB_onUserAccess_fnSetWidgetAccess(p_widget, p_userAccess)
1001          {
1002             var perms, widgetPermissions, orPermissions, orMatch, isMenuItem = false, fnEnable, fnDisable;
1003             if (p_widget instanceof YAHOO.widget.MenuItem && p_widget.element.firstChild)
1004             {
1005                isMenuItem = true;
1006                // MenuItems have to store permission values in the <a> tag's "rel" attribute
1007                perms = p_widget.element.firstChild.rel;
1008                fnEnable = Alfresco.util.bind(p_widget.cfg.setProperty, p_widget.cfg, "className", "");
1009                fnDisable = Alfresco.util.bind(p_widget.cfg.setProperty, p_widget.cfg, "className", "hidden");
1010             }
1011             else
1012             {
1013                // Buttons store the permission value in the "value" config variable
1014                perms = p_widget.get("value");
1015                fnEnable = Alfresco.util.bind(p_widget.set, p_widget, "disabled", false);
1016                fnDisable = Alfresco.util.bind(p_widget.set, p_widget, "disabled", true);
1017             }
1018             // Default to enabled: disabled via missing permission
1019             fnEnable();
1020             if (typeof perms == "string" && perms !== "")
1021             {
1022                // Comma-separation indicates "AND"
1023                widgetPermissions = perms.split(",");
1024                for (var i = 0, ii = widgetPermissions.length; i < ii; i++)
1025                {
1026                   // Pipe-separation is a special case and indicates an "OR" match. The matched permission is stored in "activePermission" on the widget.
1027                   if (widgetPermissions[i].indexOf("|") !== -1)
1028                   {
1029                      orMatch = false;
1030                      orPermissions = widgetPermissions[i].split("|");
1031                      for (var j = 0, jj = orPermissions.length; j < jj; j++)
1032                      {
1033                         if (p_userAccess[orPermissions[j]])
1034                         {
1035                            orMatch = true;
1036                            if (!isMenuItem)
1037                            {
1038                               p_widget.set("activePermission", orPermissions[j], true);
1039                            }
1040                            break;
1041                         }
1042                      }
1043                      if (!orMatch)
1044                      {
1045                         fnDisable();
1046                         break;
1047                      }
1048                   }
1049                   else if (!p_userAccess[widgetPermissions[i]])
1050                   {
1051                      fnDisable();
1052                      break;
1053                   }
1054                }
1055             }
1056          };
1057          
1058          var obj = args[1];
1059          if (obj && obj.userAccess)
1060          {
1061             // Fake permission if Google Docs is enabled via config
1062             if (this.options.googleDocsEnabled)
1063             {
1064                obj.userAccess["create-google-doc"] = true;
1065             }
1066             
1067             var widget, index, menuItems;
1068             for (index in this.widgets)
1069             {
1070                if (this.widgets.hasOwnProperty(index))
1071                {
1072                   widget = this.widgets[index];
1073                   // Skip if this action specifies "no-access-check"
1074                   if (widget.get("srcelement").className != "no-access-check")
1075                   {
1076                      fnSetWidgetAccess(widget, obj.userAccess);
1077                      if (widget.getMenu() !== null)
1078                      {
1079                         menuItems = widget.getMenu().getItems();
1080                         for (var j = 0, jj = menuItems.length; j < jj; j++)
1081                         {
1082                            fnSetWidgetAccess(menuItems[j], obj.userAccess);
1083                         }
1084                      }
1085                   }
1086                }
1087             }
1088          }
1089       },
1090 
1091       /**
1092        * Selected Files Changed event handler.
1093        * Determines whether to enable or disable the multi-file action drop-down
1094        *
1095        * @method onSelectedFilesChanged
1096        * @param layer {object} Event fired
1097        * @param args {array} Event parameters (depends on event type)
1098        */
1099       onSelectedFilesChanged: function DLTB_onSelectedFilesChanged(layer, args)
1100       {
1101          if (this.modules.docList)
1102          {
1103             var files = this.modules.docList.getSelectedFiles(), fileTypes = [], file,
1104                fileType, userAccess = {}, fileAccess, index,
1105                menuItems = this.widgets.selectedItems.getMenu().getItems(), menuItem,
1106                actionPermissions, typeGroups, typesSupported, disabled,
1107                i, ii, j, jj;
1108             
1109             var fnFileType = function fnFileType(file)
1110             {
1111                return (file.isContainer ? "folder" : "document");
1112             };
1113 
1114             // Check each file for user permissions
1115             for (i = 0, ii = files.length; i < ii; i++)
1116             {
1117                file = files[i];
1118                
1119                // Required user access level - logical AND of each file's permissions
1120                fileAccess = file.node.permissions.user;
1121                for (index in fileAccess)
1122                {
1123                   if (fileAccess.hasOwnProperty(index))
1124                   {
1125                      userAccess[index] = (userAccess[index] === undefined ? fileAccess[index] : userAccess[index] && fileAccess[index]);
1126                   }
1127                }
1128                
1129                // Make a note of all selected file types Using a hybrid array/object so we can use both array.length and "x in object"
1130                fileType = fnFileType(file);
1131                if (!(fileType in fileTypes))
1132                {
1133                   fileTypes[fileType] = true;
1134                   fileTypes.push(fileType);
1135                }
1136             }
1137 
1138             // Now go through the menu items, setting the disabled flag appropriately
1139             for (index in menuItems)
1140             {
1141                if (menuItems.hasOwnProperty(index))
1142                {
1143                   // Defaulting to enabled
1144                   menuItem = menuItems[index];
1145                   disabled = false;
1146 
1147                   if (menuItem.element.firstChild)
1148                   {
1149                      // Check permissions required - stored in "rel" attribute in the DOM
1150                      if (menuItem.element.firstChild.rel && menuItem.element.firstChild.rel !== "")
1151                      {
1152                         // Comma-separated indicates and "AND" match
1153                         actionPermissions = menuItem.element.firstChild.rel.split(",");
1154                         for (i = 0, ii = actionPermissions.length; i < ii; i++)
1155                         {
1156                            // Disable if the user doesn't have ALL the permissions
1157                            if (!userAccess[actionPermissions[i]])
1158                            {
1159                               disabled = true;
1160                               break;
1161                            }
1162                         }
1163                      }
1164 
1165                      if (!disabled)
1166                      {
1167                         // Check filetypes supported
1168                         if (menuItem.element.firstChild.type && menuItem.element.firstChild.type !== "")
1169                         {
1170                            // Pipe-separation indicates grouping of allowed file types
1171                            typeGroups = menuItem.element.firstChild.type.split("|");
1172                            
1173                            for (i = 0; i < typeGroups.length; i++) // Do not optimize - bounds updated within loop
1174                            {
1175                               typesSupported = Alfresco.util.arrayToObject(typeGroups[i].split(","));
1176 
1177                               for (j = 0, jj = fileTypes.length; j < jj; j++)
1178                               {
1179                                  if (!(fileTypes[j] in typesSupported))
1180                                  {
1181                                     typeGroups.splice(i, 1);
1182                                     --i;
1183                                     break;
1184                                  }
1185                               }
1186                            }
1187                            disabled = (typeGroups.length === 0);
1188                         }
1189                      }
1190                      menuItem.cfg.setProperty("disabled", disabled);
1191                   }
1192                }
1193             }
1194             this.widgets.selectedItems.set("disabled", (files.length === 0));
1195          }
1196       },
1197 
1198       /**
1199        * Document List Metadata event handler
1200        * NOTE: This is a temporary fix to enable access to the View Details action from the breadcrumb.
1201        *       A more complete solution is to present the full list of parent folder actions.
1202        *
1203        * @method onDoclistMetadata
1204        * @param layer {object} Event fired
1205        * @param args {array} Event parameters (depends on event type)
1206        */
1207       onDoclistMetadata: function DLTB_onDoclistMetadata(layer, args)
1208       {
1209          var obj = args[1];
1210          this.folderDetailsUrl = null;
1211          if (obj && obj.metadata)
1212          {
1213             this.doclistMetadata = Alfresco.util.deepCopy(obj.metadata);
1214             if (obj.metadata.parent && obj.metadata.parent.nodeRef)
1215             {
1216                this.folderDetailsUrl = $siteURL("folder-details?nodeRef=" + obj.metadata.parent.nodeRef);
1217             }
1218          }
1219       },
1220       
1221       /**
1222        * Handles "dropTargetOwnerRequest" by determining whether or not the target belongs to the breacrumb
1223        * trail, and if it does determines it's path and uses it with the container nodeRef on the callback 
1224        * function.
1225        * 
1226        * @method onDropTargetOwnerRequest
1227        * @property layer The name of the event
1228        * @property args The event payload
1229        */
1230       onDropTargetOwnerRequest: function DLTB_onDropTargetOwnerRequest(layer, args)
1231       {
1232          if (args && args[1] && args[1].elementId)
1233          {
1234             var crumb = Dom.get(args[1].elementId);
1235             var trail = Dom.get(this.id + "-breadcrumb");
1236             if (Dom.isAncestor(trail, crumb))
1237             {
1238                // The current element is part of the breadcrumb trail. 
1239                // Calculate the path by working out its index within the breadcrumb trail
1240                // and then apply that to the path (remembering to compensate for the SPAN
1241                // elements that just contain the ">" separators !
1242                var targetPath = "";
1243                var paths = this.currentPath.split("/");
1244                for (var i = 0, j = trail.children.length; i < j; i++)
1245                {
1246                   if (i % 2 == 0)
1247                   {
1248                      // Only use the current index if it's even (odd indexes indicate
1249                      // the SPAN containing the ">" separator character...
1250                      targetPath = targetPath + "/" + paths[i/2];
1251                   }
1252                   
1253                   // If we've reached the target element then break out of the loop...
1254                   if (crumb == trail.children[i])
1255                   {
1256                      break;
1257                   }
1258                }
1259                
1260                // Use the callback method with a nodeRef built from the the container nodeRef
1261                // concatonated with the constructed path...
1262                var nodeRef = this.doclistMetadata.container + targetPath;
1263                args[1].callback.call(args[1].scope, nodeRef, targetPath);
1264             }
1265          }
1266       },
1267    
1268       /**
1269        * Handles applying the styling and node creation required when a document is dragged
1270        * over a tree node.
1271        * 
1272        * @method onDocumentDragOver
1273        * @property layer The name of the event
1274        * @property args The event payload
1275        */
1276       onDocumentDragOver: function DLTB_onDocumentDragOver(layer, args)
1277       {
1278          if (args && args[1] && args[1].elementId)
1279          {
1280             var crumb = Dom.get(args[1].elementId);
1281             var trail = Dom.get(this.id + "-breadcrumb");
1282             if (Dom.isAncestor(trail, crumb))
1283             {
1284                Dom.addClass(crumb, "documentDragOverHighlight");
1285                var firstCrumbChild = Dom.getFirstChild(crumb);
1286                if (firstCrumbChild != null && firstCrumbChild.tagName != "SPAN")
1287                {
1288                   var arrow = document.createElement("span");
1289                   Dom.addClass(arrow, "documentDragOverArrow");
1290                   Dom.insertBefore(arrow, firstCrumbChild);
1291                }
1292             }
1293          }
1294       },
1295       
1296       /**
1297        * Handles applying the styling and node deletion required when a document is dragged
1298        * out of a tree node.
1299        *
1300        * @method onDocumentDragOut
1301        * @property layer The name of the event
1302        * @property args The event payload
1303        */
1304       onDocumentDragOut: function DLTB_onDocumentDragOut(layer, args)
1305       {
1306          if (args && args[1] && args[1].elementId)
1307          {
1308             var crumb = Dom.get(args[1].elementId);
1309             var trail = Dom.get(this.id + "-breadcrumb");
1310             if (Dom.isAncestor(trail, crumb))
1311             {
1312                Dom.removeClass(crumb, "documentDragOverHighlight");
1313                var firstCrumbChild = Dom.getFirstChild(crumb);
1314                if (firstCrumbChild != null && firstCrumbChild.tagName == "SPAN")
1315                {
1316                   crumb.removeChild(firstCrumbChild);
1317                }
1318             }
1319          }
1320       },
1321       
1322       /**
1323        * PRIVATE FUNCTIONS
1324        */
1325 
1326       /**
1327        * Generates the HTML mark-up for the breadcrumb from the currentPath
1328        *
1329        * @method _generateBreadcrumb
1330        * @private
1331        */
1332       _generateBreadcrumb: function DLTB__generateBreadcrumb()
1333       {
1334          var divBC = Dom.get(this.id + "-breadcrumb");
1335          if (divBC === null)
1336          {
1337             return;
1338          }
1339          divBC.innerHTML = "";
1340          
1341          var paths = this.currentPath.split("/");
1342          // Check for root path special case
1343          if (this.currentPath === "/")
1344          {
1345             paths = ["/"];
1346          }
1347          // Clone the array and re-use the root node name from the DocListTree
1348          var me = this,
1349             displayPaths = paths.concat();
1350          
1351          displayPaths[0] = Alfresco.util.message("node.root", this.currentFilter.filterOwner);
1352 
1353          var fnCrumbIconClick = function DLTB__fnCrumbIconClick(e, path)
1354          {
1355             Dom.addClass(e.target.parentNode, "highlighted");
1356             Event.stopEvent(e);
1357          };
1358 
1359          var fnBreadcrumbClick = function DLTB__fnBreadcrumbClick(e, path)
1360          {
1361             var filter = me.currentFilter;
1362             filter.filterData = path;
1363             
1364             YAHOO.Bubbling.fire("changeFilter", filter);
1365             Event.stopEvent(e);
1366          };
1367          
1368          var eBreadcrumb = new Element(divBC),
1369             newPath,
1370             eCrumb,
1371             eIcon,
1372             eFolder;
1373          
1374          for (var i = 0, j = paths.length; i < j; ++i)
1375          {
1376             newPath = paths.slice(0, i+1).join("/");
1377             eCrumb = new Element(document.createElement("div"));
1378             eCrumb.addClass("crumb");
1379             eCrumb.addClass("documentDroppable"); // This class allows documents to be dropped onto the element
1380             eCrumb.addClass("documentDroppableHighlights"); // This class allows drag over/out events to be processed
1381             
1382             // First crumb doesn't get an icon
1383             if (i > 0)
1384             {
1385                eIcon = new Element(document.createElement("a"),
1386                {
1387                   href: "#",
1388                   innerHTML: " "
1389                });
1390                eIcon.on("click", fnBreadcrumbClick, newPath);
1391                eIcon.addClass("icon");
1392                eIcon.addClass("filter-" + $html(this.currentFilter.filterId));
1393                eCrumb.appendChild(eIcon);
1394             }
1395 
1396             // Last crumb is rendered as a link if folderDetailsUrl is available (via doclistMetadata)
1397             if (j - i < 2)
1398             {
1399                eFolder = new Element(document.createElement("span"),
1400                {
1401                   innerHTML: (this.folderDetailsUrl) ? '<a href="' + this.folderDetailsUrl + '">' + $html(displayPaths[i]) + '</a>' : $html(displayPaths[i])
1402                });
1403                eFolder.addClass("label");
1404                eCrumb.appendChild(eFolder);
1405                eBreadcrumb.appendChild(eCrumb);
1406             }
1407             else
1408             {
1409                eFolder = new Element(document.createElement("a"),
1410                {
1411                   href: "",
1412                   innerHTML: $html(displayPaths[i])
1413                });
1414                eFolder.addClass("folder");
1415                eFolder.on("click", fnBreadcrumbClick, newPath);
1416                eCrumb.appendChild(eFolder);
1417                eBreadcrumb.appendChild(eCrumb);
1418                eBreadcrumb.appendChild(new Element(document.createElement("div"),
1419                {
1420                   innerHTML: ">",
1421                   className: "separator"
1422                }));
1423             }
1424          }
1425          
1426          var rootEl = Dom.get(this.id + "-breadcrumb");
1427          var dndTargets = Dom.getElementsByClassName("crumb", "div", rootEl);
1428          for (var i = 0, j = dndTargets.length; i < j; i++)
1429          {
1430             new YAHOO.util.DDTarget(dndTargets[i]);
1431          }
1432       },
1433 
1434       /**
1435        * Generates the HTML mark-up for the description from the currentFilter
1436        *
1437        * @method _generateDescription
1438        * @private
1439        */
1440       _generateDescription: function DLTB__generateDescription()
1441       {
1442          var divDesc, eDivDesc, eDescMsg, eDescMore, filterDisplay;
1443          
1444          divDesc = Dom.get(this.id + "-description");
1445          if (divDesc === null)
1446          {
1447             return;
1448          }
1449          
1450          while (divDesc.hasChildNodes())
1451          {
1452             divDesc.removeChild(divDesc.lastChild);
1453          }
1454          
1455          // If filterDisplay is provided, then use that instead (e.g. for cases where filterData is a nodeRef)
1456          filterDisplay = typeof this.currentFilter.filterDisplay !== "undefined" ? this.currentFilter.filterDisplay : (this.currentFilter.filterData || "");
1457          
1458          eDescMsg = new Element(document.createElement("div"),
1459          {
1460             innerHTML: this.msg("description." + this.currentFilter.filterId, filterDisplay)
1461          });
1462          eDescMsg.addClass("message");
1463 
1464          // If filterData is populated and a ".more.filterData" i18n message exists, then use that
1465          var i18n = "description." + this.currentFilter.filterId + ".more",
1466             i18nAlt = i18n + ".filterDisplay";
1467          
1468          if (filterDisplay !== "" && this.msg(i18nAlt) !== i18nAlt)
1469          {
1470             i18n = i18nAlt;
1471          }
1472 
1473          eDescMore = new Element(document.createElement("span"),
1474          {
1475             innerHTML: this.msg(i18n, $html(filterDisplay))
1476          });
1477          eDescMore.addClass("more");
1478 
1479          eDescMsg.appendChild(eDescMore);
1480          eDivDesc = new Element(divDesc);
1481          eDivDesc.appendChild(eDescMsg);
1482       },
1483       
1484       /**
1485        * Generates the HTML mark-up for the RSS feed link
1486        *
1487        * @method _generateRSSFeedUrl
1488        * @private
1489        */
1490       _generateRSSFeedUrl: function DLTB__generateRSSFeedUrl()
1491       {
1492          if (this.widgets.rssFeed && this.modules.docList)
1493          {
1494             var params = YAHOO.lang.substitute("{type}/site/{site}/{container}{path}",
1495             {
1496                type: this.modules.docList.options.showFolders ? "all" : "documents",
1497                site: encodeURIComponent(this.options.siteId),
1498                container: encodeURIComponent(this.options.containerId),
1499                path: Alfresco.util.encodeURIPath(this.currentPath)
1500             });
1501 
1502             params += "?filter=" + encodeURIComponent(this.currentFilter.filterId);
1503             if (this.currentFilter.filterData)
1504             {
1505                params += "&filterData=" + encodeURIComponent(this.currentFilter.filterData);             
1506             }
1507             params += "&format=rss";
1508             
1509             this.widgets.rssFeed.set("href", Alfresco.constants.URL_FEEDSERVICECONTEXT + "components/documentlibrary/feed/" + params);
1510             Alfresco.util.enableYUIButton(this.widgets.rssFeed);
1511          }
1512       }
1513    }, true);
1514 })();