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  * Dashboard MySites component.
 22  *
 23  * @namespace Alfresco.dashlet
 24  * @class Alfresco.dashlet.MySites
 25  */
 26 (function()
 27 {
 28    /**
 29     * YUI Library aliases
 30     */
 31    var Dom = YAHOO.util.Dom,
 32       Event = YAHOO.util.Event,
 33       Selector = YAHOO.util.Selector;
 34 
 35    /**
 36     * Alfresco Slingshot aliases
 37     */
 38    var $html = Alfresco.util.encodeHTML,
 39       $links = Alfresco.util.activateLinks;
 40 
 41    /**
 42     * Use the getDomId function to get some unique names for global event handling
 43     */
 44    var FAV_EVENTCLASS = Alfresco.util.generateDomId(null, "fav-site"),
 45       IMAP_EVENTCLASS = Alfresco.util.generateDomId(null, "imap-site"),
 46       LIKE_EVENTCLASS = Alfresco.util.generateDomId(null, "like-site"),
 47       DELETE_EVENTCLASS = Alfresco.util.generateDomId(null, "del-site");
 48 
 49    /**
 50     * Preferences
 51     */
 52    var PREFERENCES_SITES = "org.alfresco.share.sites",
 53       PREFERENCES_SITES_DASHLET_FILTER = PREFERENCES_SITES + ".dashlet.filter";
 54 
 55    /**
 56     * Dashboard MySites constructor.
 57     *
 58     * @param {String} htmlId The HTML id of the parent element
 59     * @return {Alfresco.dashlet.MySites} The new component instance
 60     * @constructor
 61     */
 62    Alfresco.dashlet.MySites = function MySites_constructor(htmlId)
 63    {
 64       Alfresco.dashlet.MySites.superclass.constructor.call(this, "Alfresco.dashlet.MySites", htmlId, ["datasource", "datatable", "animation"]);
 65 
 66       // Initialise prototype properties
 67       this.sites = [];
 68       this.createSite = null;
 69 
 70       // Services
 71       this.services.preferences = new Alfresco.service.Preferences();
 72       this.services.likes = new Alfresco.service.Ratings(Alfresco.service.Ratings.LIKES);
 73 
 74       // Listen for events from other components
 75       YAHOO.Bubbling.on("siteDeleted", this.onSiteDeleted, this);
 76 
 77       return this;
 78    };
 79 
 80    YAHOO.extend(Alfresco.dashlet.MySites, Alfresco.component.Base,
 81    {
 82       /**
 83        * Site data
 84        *
 85        * @property sites
 86        * @type array
 87        */
 88       sites: null,
 89 
 90       /**
 91        * CreateSite module instance.
 92        *
 93        * @property createSite
 94        * @type Alfresco.module.CreateSite
 95        */
 96       createSite: null,
 97 
 98       /**
 99        * Object container for initialization options
100        *
101        * @property options
102        * @type object
103        */
104       options:
105       {
106          /**
107           * List of valid filters
108           *
109           * @property validFilters
110           * @type object
111           */
112          validFilters:
113          {
114             "all": true,
115             "favSites": true
116          },
117           
118          /**
119           * Flag if IMAP server is enabled
120           *
121           * @property imapEnabled
122           * @type boolean
123           * @default false
124           */
125          imapEnabled: false,
126          
127          /**
128           * Result list size maximum
129           *
130           * @property listSize
131           * @type integer
132           * @default 100
133           */
134          listSize: 100
135       },
136 
137       /**
138        * Fired by YUI when parent element is available for scripting
139        * @method onReady
140        */
141       onReady: function MySites_onReady()
142       {
143          var me = this;
144 
145          // Create Dropdown filter
146          this.widgets.type = Alfresco.util.createYUIButton(this, "type", this.onTypeFilterChanged,
147          {
148             type: "menu",
149             menu: "type-menu",
150             lazyloadmenu: false
151          });
152 
153          // Listen on clicks for the create site link
154          Event.addListener(this.id + "-createSite-button", "click", this.onCreateSite, this, true);
155 
156          // DataSource definition
157          this.widgets.dataSource = new YAHOO.util.DataSource(this.sites,
158          {
159             responseType: YAHOO.util.DataSource.TYPE_JSARRAY
160          });
161 
162          // DataTable column defintions
163          var columnDefinitions =
164          [
165             { key: "icon", label: "Icon", sortable: false, formatter: this.bind(this.renderCellIcon), width: 52 },
166             { key: "detail", label: "Description", sortable: false, formatter: this.bind(this.renderCellDetail) },
167             { key: "actions", label: "Actions", sortable: false, formatter: this.bind(this.renderCellActions), width: 24 }
168          ];
169 
170          // DataTable definition
171          this.widgets.dataTable = new YAHOO.widget.DataTable(this.id + "-sites", columnDefinitions, this.widgets.dataSource,
172          {
173             MSG_EMPTY: this.msg("message.datatable.loading")
174          });
175 
176          // Override abstract function within DataTable to set custom empty message
177          this.widgets.dataTable.doBeforeLoadData = function MySites_doBeforeLoadData(sRequest, oResponse, oPayload)
178          {
179             if ((oResponse.results.length === 0) || (oResponse.results.length === 1 && oResponse.results[0].shortName === "swsdp"))
180             {
181                oResponse.results.unshift(
182                {
183                   isInfo: true,
184                   title: me.msg("empty.title"),
185                   description: me.msg("empty.description") + (oResponse.results.length === 1 ? "<p>" + me.msg("empty.description.sample-site") + "</p>" : "")
186                });
187             }
188             return true;
189          };
190 
191          // Add animation to row delete
192          this.widgets.dataTable._deleteTrEl = function MySites__deleteTrEl(row)
193          {
194             var scope = this,
195                trEl = this.getTrEl(row);
196 
197             var changeColor = new YAHOO.util.ColorAnim(trEl,
198             {
199                opacity:
200                {
201                   to: 0
202                }
203             }, 0.25);
204             changeColor.onComplete.subscribe(function()
205             {
206                YAHOO.widget.DataTable.prototype._deleteTrEl.call(scope, row);
207             });
208             changeColor.animate();
209          };
210 
211          /**
212           * Hook favourite site events
213           */
214          var registerEventHandler = function MySites_onReady_registerEventHandler(cssClass, fnHandler)
215          {
216             var fnEventHandler = function MySites_onReady_fnEventHandler(layer, args)
217             {
218                var owner = YAHOO.Bubbling.getOwnerByTagName(args[1].anchor, "div");
219                if (owner !== null)
220                {
221                   fnHandler.call(me, args[1].target.offsetParent, owner);
222                }
223 
224                return true;
225             };
226             YAHOO.Bubbling.addDefaultAction(cssClass, fnEventHandler);
227          };
228 
229          registerEventHandler(FAV_EVENTCLASS, this.onFavouriteSite);
230          registerEventHandler(IMAP_EVENTCLASS, this.onImapFavouriteSite);
231          registerEventHandler(LIKE_EVENTCLASS, this.onLikes);
232          registerEventHandler(DELETE_EVENTCLASS, this.onDeleteSite);
233 
234          // Enable row highlighting
235          this.widgets.dataTable.subscribe("rowMouseoverEvent", this.widgets.dataTable.onEventHighlightRow);
236          this.widgets.dataTable.subscribe("rowMouseoutEvent", this.widgets.dataTable.onEventUnhighlightRow);
237 
238          // Load sites & preferences
239          this.loadSites();
240       },
241 
242       /**
243        * Date drop-down changed event handler
244        *
245        * @method onTypeFilterChanged
246        * @param p_sType {string} The event
247        * @param p_aArgs {array}
248        */
249       onTypeFilterChanged: function MySites_onTypeFilterChanged(p_sType, p_aArgs)
250       {
251          var menuItem = p_aArgs[1];
252          if (menuItem)
253          {
254             this.widgets.type.set("label", menuItem.cfg.getProperty("text"));
255             this.widgets.type.value = menuItem.value;
256 
257             // Save preferences and load sites afterwards
258             this.services.preferences.set(PREFERENCES_SITES_DASHLET_FILTER, menuItem.value,
259             {
260                successCallback:
261                {
262                   fn: this.loadSites,
263                   scope: this
264                }
265             });
266          }
267       },
268 
269       /**
270        * Load sites list
271        *
272        * @method loadSites
273        */
274       loadSites: function MySites_loadSites()
275       {
276          // Load sites
277          Alfresco.util.Ajax.request(
278          {
279             url: Alfresco.constants.PROXY_URI + "api/people/" + encodeURIComponent(Alfresco.constants.USERNAME) + "/sites?roles=user&size=" + this.options.listSize,
280             successCallback:
281             {
282                fn: this.onSitesLoaded,
283                scope: this
284             }
285          });
286       },
287 
288       /**
289        * Retrieve user preferences after sites data has loaded
290        *
291        * @method onSitesLoaded
292        * @param p_response {object} Response from "api/people/{userId}/sites" query
293        */
294       onSitesLoaded: function MySites_onSitesLoaded(p_response)
295       {
296          // Load preferences (after which the appropriate sites will be displayed)
297          this.services.preferences.request(PREFERENCES_SITES,
298          {
299             successCallback:
300             {
301                fn: this.onPreferencesLoaded,
302                scope: this,
303                obj: p_response.json
304             }
305          });
306       },
307 
308       /**
309        * Process response from sites and preferences queries
310        *
311        * @method onPreferencesLoaded
312        * @param p_response {object} Response from "api/people/{userId}/preferences" query
313        * @param p_items {object} Response from "api/people/{userId}/sites" query
314        */
315       onPreferencesLoaded: function MySites_onPreferencesLoaded(p_response, p_items)
316       {
317          var favSites = {},
318             imapfavSites = {},
319             siteManagers, i, j, k, l,
320             ii = 0;
321 
322          // Save preferences
323          if (p_response.json.org)
324          {
325             favSites = p_response.json.org.alfresco.share.sites.favourites;
326             if (typeof(favSites) === "undefined")
327             {
328                favSites = {};
329             } 
330             imapfavSites = p_response.json.org.alfresco.share.sites.imapFavourites;
331          }
332 
333          // Select the preferred filter in the ui
334          var filter = Alfresco.util.findValueByDotNotation(p_response.json, PREFERENCES_SITES_DASHLET_FILTER, "all");
335          filter = this.options.validFilters.hasOwnProperty(filter) ? filter : "all";
336          this.widgets.type.set("label", this.msg("filter." + filter));
337          this.widgets.type.value = filter;
338 
339          // Display the toolbar now that we have selected the filter
340          Dom.removeClass(Selector.query(".toolbar div", this.id, true), "hidden");
341 
342          for (i = 0, j = p_items.length; i < j; i++)
343          {
344             p_items[i].isSiteManager = p_items[i].siteRole === "SiteManager";
345             p_items[i].isFavourite = typeof(favSites[p_items[i].shortName]) == "undefined" ? false : favSites[p_items[i].shortName];
346             if (imapfavSites)
347             {
348                p_items[i].isIMAPFavourite = typeof(imapfavSites[p_items[i].shortName]) == "undefined" ? false : imapfavSites[p_items[i].shortName];
349             }
350          }
351 
352          this.sites = [];
353          for (i = 0, j = p_items.length; i < j; i++)
354          {
355             var site = YAHOO.lang.merge({}, p_items[i]);
356 
357             if (this.filterAccept(this.widgets.type.value, site))
358             {
359                this.sites[ii] = site;
360                ii++;
361             }
362          }
363 
364          this.sites.sort(function(a, b)
365          {
366             var name1 = a.title ? a.title.toLowerCase() : a.shortName.toLowerCase(),
367                 name2 = b.title ? b.title.toLowerCase() : b.shortName.toLowerCase();
368             return (name1 > name2) ? 1 : (name1 < name2) ? -1 : 0;
369          });
370 
371          var successHandler = function MySites_onSitesUpdate_success(sRequest, oResponse, oPayload)
372          {
373             oResponse.results=this.sites;
374             this.widgets.dataTable.set("MSG_EMPTY", "");
375             this.widgets.dataTable.onDataReturnInitializeTable.call(this.widgets.dataTable, sRequest, oResponse, oPayload);
376          };
377 
378          this.widgets.dataSource.sendRequest(this.sites,
379          {
380             success: successHandler,
381             scope: this
382          });
383       },
384 
385       /**
386        * Determine whether a given site should be displayed or not depending on the current filter selection
387        * @method filterAccept
388        * @param filter {string} Filter to set
389        * @param site {object} Site object literal
390        * @return {boolean}
391        */
392       filterAccept: function MySites_filterAccept(filter, site)
393       {
394          switch (filter)
395          {
396             case "all":
397                return true;
398 
399             case "favSites":
400                return (site.isFavourite || (this.options.imapEnabled && site.isIMAPFavourite));
401          }
402          return false;
403       },
404 
405       /**
406        * Generate "Favourite" UI
407        *
408        * @method generateFavourite
409        * @param record {object} DataTable record
410        * @return {string} HTML mark-up for Favourite UI
411        */
412       generateFavourite: function MySites_generateFavourite(record)
413       {
414          var html = "";
415 
416          if (record.getData("isFavourite"))
417          {
418             html = '<a class="favourite-action ' + FAV_EVENTCLASS + ' enabled" title="' + this.msg("favourite.site.remove.tip") + '" tabindex="0"></a>';
419          }
420          else
421          {
422             html = '<a class="favourite-action ' + FAV_EVENTCLASS + '" title="' + this.msg("favourite.site.add.tip") + '" tabindex="0">' + this.msg("favourite.site.add.label") + '</a>';
423          }
424 
425          return html;
426       },
427 
428       /**
429        * Generate "IMAP Favourite" UI
430        *
431        * @method generateIMAPFavourite
432        * @param record {object} DataTable record
433        * @return {string} HTML mark-up for Favourite UI
434        */
435       generateIMAPFavourite: function MySites_generateIMAPFavourite(record)
436       {
437          var html = "";
438 
439          if (record.getData("isIMAPFavourite"))
440          {
441             html = '<a class="favourite-action favourite-imap ' + IMAP_EVENTCLASS + ' enabled" title="' + this.msg("favourite.imap-site.remove.tip") + '" tabindex="0"></a>';
442          }
443          else
444          {
445             html = '<a class="favourite-imap ' + IMAP_EVENTCLASS + '" title="' + this.msg("favourite.imap-site.add.tip") + '" tabindex="0">' + this.msg("favourite.imap-site.add.label") + '</a>';
446          }
447 
448          return html;
449       },
450 
451       /**
452        * Generate "Likes" UI
453        *
454        * @method generateLikes
455        * @param record {object} DataTable record
456        * @return {string} HTML mark-up for Likes UI
457        */
458       generateLikes: function MySites_generateLikes(record)
459       {
460          var likes = record.getData("likes"),
461             html = "";
462 
463          // TODO: Remove when Site Service supports "Likes"
464          likes = YAHOO.lang.merge(
465          {
466             isLiked: false,
467             totalLikes: 0
468          }, likes || {});
469          
470          if (likes.isLiked)
471          {
472             html = '<a class="like-action ' + LIKE_EVENTCLASS + ' enabled" title="' + this.msg("like.site.remove.tip") + '" tabindex="0"></a>';
473          }
474          else
475          {
476             html = '<a class="like-action ' + LIKE_EVENTCLASS + '" title="' + this.msg("like.site.add.tip") + '" tabindex="0">' + this.msg("like.site.add.label") + '</a>';
477          }
478 
479          html += '<span class="likes-count">' + $html(likes.totalLikes) + '</span>';
480 
481          return html;
482       },
483 
484       /**
485        * Icon custom datacell formatter
486        *
487        * @method renderCellIcon
488        * @param elCell {object}
489        * @param oRecord {object}
490        * @param oColumn {object}
491        * @param oData {object|string}
492        */
493       renderCellIcon: function MySites_renderCellIcon(elCell, oRecord, oColumn, oData)
494       {
495          Dom.setStyle(elCell, "width", oColumn.width + "px");
496          Dom.setStyle(elCell.parentNode, "width", oColumn.width + "px");
497          
498          var site = oRecord.getData(),
499             img = site.isInfo ? "help-site-bw-32.png" : "filetypes/generic-site-32.png";
500 
501          elCell.innerHTML = '<img src="' + Alfresco.constants.URL_RESCONTEXT + 'components/images/' + img + '" />';
502       },
503 
504       /**
505        * Name & description custom datacell formatter
506        *
507        * @method renderCellDetail
508        * @param elCell {object}
509        * @param oRecord {object}
510        * @param oColumn {object}
511        * @param oData {object|string}
512        */
513       renderCellDetail: function MySites_renderCellDetail(elCell, oRecord, oColumn, oData)
514       {
515          var site = oRecord.getData(),
516             description = '<span class="faded">' + this.msg("details.description.none") + '</span>',
517             desc = "";
518 
519          if (site.isInfo)
520          {
521             desc += '<div class="empty"><h3>' + site.title + '</h3>';
522             desc += '<span>' + site.description + '</span></div>';
523          }
524          else
525          {
526             // Description non-blank?
527             if (site.description && site.description !== "")
528             {
529                description = $links($html(site.description));
530             }
531 
532             desc += '<h3 class="site-title"><a href="' + Alfresco.constants.URL_PAGECONTEXT + 'site/' + site.shortName + '/dashboard" class="theme-color-1">' + $html(site.title) + '</a></h3>';
533             desc += '<div class="detail"><span>' + description + '</span></div>';
534 
535             /* Favourite / IMAP / (Likes) */
536             desc += '<div class="detail detail-social">';
537             desc +=    '<span class="item item-social">' + this.generateFavourite(oRecord) + '</span>';
538             if (this.options.imapEnabled)
539             {
540                desc +=    '<span class="item item-social item-separator">' + this.generateIMAPFavourite(oRecord) + '</span>';
541             }
542             /**
543              * Not in Alfresco Team
544              *
545             desc +=    '<span class="item item-social">' + this.generateLikes(oRecord) + '</span>';
546             */
547             desc += '</div>';
548          }
549 
550          elCell.innerHTML = desc;
551       },
552 
553       /**
554        * Actions custom datacell formatter
555        *
556        * @method renderCellActions
557        * @param elCell {object}
558        * @param oRecord {object}
559        * @param oColumn {object}
560        * @param oData {object|string}
561        */
562       renderCellActions: function MySites_renderCellActions(elCell, oRecord, oColumn, oData)
563       {
564          Dom.setStyle(elCell, "width", oColumn.width + "px");
565          Dom.setStyle(elCell.parentNode, "width", oColumn.width + "px");
566 
567          var desc = "";
568 
569          if (oRecord.getData("isSiteManager"))
570          {
571             desc += '<a class="delete-site ' + DELETE_EVENTCLASS + '" title="' + this.msg("link.deleteSite") + '"> </a>';
572          }
573          elCell.innerHTML = desc;
574       },
575 
576       /**
577        * Adds an event handler for bringing up the delete site dialog for the specific site
578        *
579        * @method onDeleteSite
580        * @param row {object} DataTable row representing site to be actioned
581        */
582       onDeleteSite: function MySites_onDeleteSite(row)
583       {
584          var record = this.widgets.dataTable.getRecord(row);
585 
586          // Display the delete dialog for the site
587          Alfresco.module.getDeleteSiteInstance().show(
588          {
589             site: record.getData()
590          });
591       },
592 
593       /**
594        * Fired by DeleteSite module when a site has been deleted.
595        *
596        * @method onSiteDeleted
597        * @param layer {object} Event fired (unused)
598        * @param args {array} Event parameters (unused)
599        */
600       onSiteDeleted: function MySites_onSiteDeleted(layer, args)
601       {
602          var site = args[1].site,
603             siteId = site.shortName;
604 
605          // Find the record corresponding to this site
606          var record = this._findRecordByParameter(siteId, "shortName");
607          if (record !== null)
608          {
609             this.widgets.dataTable.deleteRow(record);
610          }
611       },
612 
613       /**
614        * Adds an event handler that adds or removes the site as favourite site
615        *
616        * @method onFavouriteSite
617        * @param row {object} DataTable row representing site to be actioned
618        */
619       onFavouriteSite: function MySites_onFavouriteSite(row)
620       {
621          var record = this.widgets.dataTable.getRecord(row),
622             site = record.getData(),
623             siteId = site.shortName;
624 
625          site.isFavourite = !site.isFavourite;
626 
627          this.widgets.dataTable.updateRow(record, site);
628 
629          // Assume the call will succeed, but register a failure handler to replace the UI state on failure
630          var responseConfig =
631          {
632             failureCallback:
633             {
634                fn: function MySites_onFavouriteSite_failure(event, obj)
635                {
636                   // Reset the flag to it's previous state
637                   var record = obj.record,
638                      site = record.getData();
639 
640                   site.isFavourite = !site.isFavourite;
641                   this.widgets.dataTable.updateRow(record, site);
642                   Alfresco.util.PopupManager.displayPrompt(
643                   {
644                      text: this.msg("message.save.failure")
645                   });
646                },
647                scope: this,
648                obj:
649                {
650                   record: record
651                }
652             },
653             successCallback:
654             {
655                fn: function MySites_onFavouriteSite_success(event, obj)
656                {
657                   var record = obj.record,
658                      site = record.getData();
659 
660                   YAHOO.Bubbling.fire(site.isFavourite ? "favouriteSiteAdded" : "favouriteSiteRemoved", site);
661                },
662                scope: this,
663                obj:
664                {
665                   record: record
666                }
667             }
668          };
669 
670          this.services.preferences.set(Alfresco.service.Preferences.FAVOURITE_SITES + "." + siteId, site.isFavourite, responseConfig);
671       },
672 
673       /**
674        * Adds an event handler that adds or removes the site as favourite site
675        *
676        * @method onImapFavouriteSite
677        * @param row {object} DataTable row representing site to be actioned
678        */
679       onImapFavouriteSite: function MySites_onImapFavouriteSite(row)
680       {
681          var record = this.widgets.dataTable.getRecord(row),
682             site = record.getData(),
683             siteId = site.shortName;
684 
685          site.isIMAPFavourite = !site.isIMAPFavourite;
686 
687          this.widgets.dataTable.updateRow(record, site);
688 
689          // Assume the call will succeed, but register a failure handler to replace the UI state on failure
690          var responseConfig =
691          {
692             failureCallback:
693             {
694                fn: function MySites_onImapFavouriteSite_failure(event, obj)
695                {
696                   // Reset the flag to it's previous state
697                   var record = obj.record,
698                      site = record.getData();
699 
700                   site.isIMAPFavourite = !site.isIMAPFavourite;
701                   this.widgets.dataTable.updateRow(record, site);
702                   Alfresco.util.PopupManager.displayPrompt(
703                   {
704                      text: this.msg("message.save.failure")
705                   });
706                },
707                scope: this,
708                obj:
709                {
710                   record: record
711                }
712             }
713          };
714 
715          this.services.preferences.set(Alfresco.service.Preferences.IMAP_FAVOURITE_SITES + "." + siteId, site.isIMAPFavourite, responseConfig);
716       },
717 
718       /**
719        * Like/Unlike event handler
720        *
721        * @method onLikes
722        * @param row {HTMLElement} DOM reference to a TR element (or child thereof)
723        */
724       onLikes: function MySites_onLikes(row)
725       {
726          var record = this.widgets.dataTable.getRecord(row),
727             site = record.getData(),
728             nodeRef = new Alfresco.util.NodeRef(site.nodeRef),
729             likes = site.likes;
730 
731          likes.isLiked = !likes.isLiked;
732          likes.totalLikes += (likes.isLiked ? 1 : -1);
733 
734          var responseConfig =
735          {
736             successCallback:
737             {
738                fn: function MySites_onLikes_success(event, p_nodeRef)
739                {
740                   var data = event.json.data;
741                   if (data)
742                   {
743                      // Update the record with the server's value
744                      var record = this._findRecordByParameter(p_nodeRef, "nodeRef"),
745                         site = record.getData(),
746                         likes = site.likes;
747 
748                      likes.totalLikes = data.ratingsCount;
749                      this.widgets.dataTable.updateRow(record, site);
750                   }
751                },
752                scope: this,
753                obj: nodeRef.toString()
754             },
755             failureCallback:
756             {
757                fn: function MySites_onLikes_failure(event, p_nodeRef)
758                {
759                   // Reset the flag to it's previous state
760                   var record = this._findRecordByParameter(p_nodeRef, "nodeRef"),
761                      site = record.getData(),
762                      likes = site.likes;
763 
764                   likes.isLiked = !likes.isLiked;
765                   likes.totalLikes += (likes.isLiked ? 1 : -1);
766                   this.widgets.dataTable.updateRow(record, site);
767                   Alfresco.util.PopupManager.displayPrompt(
768                   {
769                      text: this.msg("message.save.failure", site.title)
770                   });
771                },
772                scope: this,
773                obj: nodeRef.toString()
774             }
775          };
776 
777          if (likes.isLiked)
778          {
779             this.services.likes.set(nodeRef, 1, responseConfig);
780          }
781          else
782          {
783             this.services.likes.remove(nodeRef, responseConfig);
784          }
785          this.widgets.dataTable.updateRow(record, site);
786       },
787 
788       /**
789        * Fired by YUI Link when the "Create site" label is clicked
790        * @method onCreateSite
791        * @param event {domEvent} DOM event
792        */
793       onCreateSite: function MySites_onCreateSite(event)
794       {
795          Alfresco.module.getCreateSiteInstance().show();
796          Event.preventDefault(event);
797       },
798 
799       /**
800        * Searches the current recordSet for a record with the given parameter value
801        *
802        * @method _findRecordByParameter
803        * @param p_value {string} Value to find
804        * @param p_parameter {string} Parameter to look for the value in
805        */
806       _findRecordByParameter: function MySites__findRecordByParameter(p_value, p_parameter)
807       {
808         var recordSet = this.widgets.dataTable.getRecordSet();
809         for (var i = 0, j = recordSet.getLength(); i < j; i++)
810         {
811            if (recordSet.getRecord(i).getData(p_parameter) === p_value)
812            {
813               return recordSet.getRecord(i);
814            }
815         }
816         return null;
817       }
818    });
819 })();