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  * BlogPostList component.
 22  * 
 23  * @namespace Alfresco
 24  * @class Alfresco.BlogPostList
 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    /**
 42     * BlogPostList constructor.
 43     * 
 44     * @param {String} htmlId The HTML id of the parent element
 45     * @return {Alfresco.BlogPostList} The new BlogPostList instance
 46     * @constructor
 47     */
 48    Alfresco.BlogPostList = function(htmlId)
 49    {
 50       /* Mandatory properties */
 51       this.name = "Alfresco.BlogPostList";
 52       this.id = htmlId;
 53       
 54       /* Initialise prototype properties */
 55       this.widgets = {};
 56       this.currentFilter = {};
 57       this.tagId =
 58       {
 59          id: 0,
 60          tags: {}
 61       };
 62       
 63       /* Register this component */
 64       Alfresco.util.ComponentManager.register(this);
 65 
 66       /* Load YUI Components */
 67       Alfresco.util.YUILoaderHelper.require(["button", "dom", "datasource", "datatable", "paginator", "event", "element"], this.onComponentsLoaded, this);
 68       
 69       /* Decoupled event listeners */
 70       YAHOO.Bubbling.on("tagSelected", this.onTagSelected, this);
 71       YAHOO.Bubbling.on("blogConfigChanged", this.onBlogConfigChanged, this);
 72       YAHOO.Bubbling.on("changeFilter", this.onChangeFilter, this);
 73       YAHOO.Bubbling.on("blogpostlistRefresh", this.onBlogPostListRefresh, this);
 74       YAHOO.Bubbling.on("deactivateAllControls", this.onDeactivateAllControls, this);
 75       
 76       return this;
 77    };
 78    
 79    Alfresco.BlogPostList.prototype =
 80    {
 81       /**
 82        * Object container for initialization options
 83        *
 84        * @property options
 85        * @type object
 86        */
 87       options:
 88       {
 89          /**
 90           * Current siteId.
 91           * 
 92           * @property siteId
 93           * @type string
 94           */
 95          siteId: "",
 96          
 97          /**
 98           * ContainerId representing root container
 99           *
100           * @property containerId
101           * @type string
102           * @default "blog"
103           */
104          containerId: "blog",
105 
106          /**
107           * Initially used filter name and id.
108           */         
109          initialFilter: {},
110 
111          /**
112           * Number of items per page
113           * 
114           * @property pageSize
115           * @type int
116           */
117          pageSize: 10,
118 
119          /**
120           * Flag indicating whether the list shows a detailed view or a simple one.
121           * 
122           * @property simpleView
123           * @type boolean
124           */
125          simpleView: false,
126          
127          /**
128           * Maximum length of post to show in list view
129           *
130           * @property maxContentLength
131           * @type int
132           * @default 512
133           */
134          maxContentLength: 512
135       },
136       
137       /**
138        * Current filter to filter blog post list.
139        * 
140        * @property currentFilter
141        * @type object
142        */
143       currentFilter: null,
144 
145       /**
146        * Object container for storing YUI widget instances.
147        * 
148        * @property widgets
149        * @type object
150        */
151       widgets: null,
152       
153       /**
154        * Object container for storing module instances.
155        * 
156        * @property modules
157        * @type object
158        */
159       modules: null,
160       
161       /**
162        * Object literal used to generate unique tag ids
163        * 
164        * @property tagId
165        * @type object
166        */
167       tagId: null,
168       
169       /**
170        * Tells whether an action is currently ongoing.
171        * 
172        * @property busy
173        * @type boolean
174        * @see _setBusy/_releaseBusy
175        */
176       busy: false,
177       
178       /**
179        * True if publishing actions should be displayed
180        *
181        * @property showPublishingActions
182        * @type boolean
183        * @default false
184        */
185       showPublishingActions: false,
186       
187       /**
188        * Offset of first record on page
189        * 
190        * @property recordOffset
191        * @type int
192        * @default 0
193        */
194       recordOffset: 0,
195 
196       /**
197        * Total number of posts in the current view (across all pages)
198        * 
199        * @property totalRecords
200        * @type int
201        * @default 0
202        */
203       totalRecords: 0,
204 
205       /**
206        * Set multiple initialization options at once.
207        *
208        * @method setOptions
209        * @param obj {object} Object literal specifying a set of options
210        */
211       setOptions: function BlogPostList_setOptions(obj)
212       {
213          this.options = YAHOO.lang.merge(this.options, obj);
214          return this;
215       },
216       
217       /**
218        * Set messages for this component.
219        *
220        * @method setMessages
221        * @param obj {object} Object literal specifying a set of messages
222        * @return {Alfresco.DocumentList} returns 'this' for method chaining
223        */
224       setMessages: function BlogPostList_setMessages(obj)
225       {
226          Alfresco.util.addMessages(obj, this.name);
227          return this;
228       },
229       
230       /**
231        * Fired by YUILoaderHelper when required component script files have
232        * been loaded into the browser.
233        *
234        * @method onComponentsLoaded
235        */
236       onComponentsLoaded: function BlogPostList_onComponentsLoaded()
237       {
238          Event.onContentReady(this.id, this.onReady, this, true);
239       },
240    
241       /**
242        * Fired by YUI when parent element is available for scripting.
243        * Component initialisation, including instantiation of YUI widgets and event listener binding.
244        *
245        * @method onReady
246        */
247       onReady: function BlogPostList_onReady()
248       {
249          // Reference to self used by inline functions
250          var me = this;
251           
252          // Simple view button
253          this.widgets.simpleView = Alfresco.util.createYUIButton(this, "simpleView-button", this.onSimpleView);
254 
255          // called by the paginator on state changes
256          var handlePagination = function BlogPostList_handlePagination(state, dt)
257          {
258             //me.currentPage = state.page;
259             me._updateBlogPostList(
260             {
261                page: state.page
262             });
263          };
264 
265          // YUI Paginator definition
266          this.widgets.paginator = new YAHOO.widget.Paginator(
267          {
268             containers: [this.id + "-paginator"],
269             rowsPerPage: this.options.pageSize,
270             initialPage: 1,
271             template: this._msg("pagination.template"),
272             pageReportTemplate: this._msg("pagination.template.page-report"),
273             previousPageLinkLabel: this._msg("pagination.previousPageLinkLabel"),
274             nextPageLinkLabel: this._msg("pagination.nextPageLinkLabel")
275          });
276          
277          this.widgets.paginator.subscribe("changeRequest", handlePagination);
278 
279          // Hook action events for details view
280          var fnActionHandlerDiv = function BlogPostList_fnActionHandlerDiv(layer, args)
281          {
282             var owner = YAHOO.Bubbling.getOwnerByTagName(args[1].anchor, "div");
283             if (owner !== null)
284             {
285                if (typeof me[owner.className] == "function")
286                {
287                   args[1].stop = true;
288                   me[owner.className].call(me, args[1].target.offsetParent, owner);
289                }
290             }
291             return true;
292          };
293          YAHOO.Bubbling.addDefaultAction("blogpost-action-link-div", fnActionHandlerDiv);
294          
295          // Hook action events for simple view
296          var fnActionHandlerSpan = function BlogPostList_fnActionHandlerSpan(layer, args)
297          {
298             var owner = YAHOO.Bubbling.getOwnerByTagName(args[1].anchor, "span");
299             if (owner !== null)
300             {
301                var action = owner.className;
302                var target = args[1].target;
303                if (typeof me[action] == "function")
304                {
305                   me[action].call(me, target.offsetParent, owner);
306                   args[1].stop = true;
307                }
308             }
309             return true;
310          };
311          YAHOO.Bubbling.addDefaultAction("blogpost-action-link-span", fnActionHandlerSpan);
312          
313          // DataSource definition
314          var uriBlogPostList = YAHOO.lang.substitute(Alfresco.constants.URL_SERVICECONTEXT + "components/blog/site/{site}/{container}/posts",
315          {
316             site: this.options.siteId,
317             container: this.options.containerId
318          });
319          this.widgets.dataSource = new YAHOO.util.DataSource(uriBlogPostList,
320          {
321             responseType: YAHOO.util.DataSource.TYPE_JSON,
322             connXhrMode: "queueRequests",
323             responseSchema:
324             {
325                resultsList: "items",
326                metaFields:
327                {
328                   recordOffset: "startIndex",
329                   totalRecords: "total",
330                   metadata: "metadata"
331                }
332             }
333          });
334          
335          /**
336           * Blog post element. We only have a list and not an acutal table, there is therefore
337           * only one column renderer
338           *
339           * @method renderBlogPost
340           * @param elCell {object}
341           * @param oRecord {object}
342           * @param oColumn {object}
343           * @param oData {object|string}
344           */
345          var renderBlogPost = function BlogPostList_renderBlogPost(elCell, oRecord, oColumn, oData)
346          {
347             // hide the parent temporarily as we first insert the structure and then the content
348             // to avoid problems caused by broken xhtml
349             Dom.addClass(elCell, 'hidden');
350             
351             // fetch the data and pregenerate some values
352             var data = oRecord.getData();
353             var postViewUrl = Alfresco.util.blog.generateBlogPostViewUrl(me.options.siteId, me.options.containerId, data.name);
354             var statusLabel = Alfresco.util.blog.generatePostStatusLabel(me, data);
355             var authorLink = Alfresco.util.people.generateUserLink(data.author);
356             
357             var html = "";
358             // detailed view
359             if (!me.options.simpleView)
360             {
361                html += '<div class="node post">';
362 
363                // actions
364                html += Alfresco.util.blog.generateBlogPostActions(me, data, 'div', me.showPublishingActions);
365    
366                // begin view
367                html += '<div class="nodeContent">';
368                html += '<span class="nodeTitle"><a href="' + postViewUrl + '">' + $html(data.title) + '</a> ';
369                html += '<span class="theme-color-2 nodeStatus">' + statusLabel + '</span></span>';
370                html += '<div class="published">';
371                if (!data.isDraft)
372                {
373                   html += '<span class="nodeAttrLabel">' + me._msg("post.publishedOn") + ': </span>';
374                   html += '<span class="nodeAttrValue">' + Alfresco.util.formatDate(data.releasedOn) + '</span>';
375                   html += '<span class="separator"> </span>';
376                }
377                html += '<span class="nodeAttrLabel">' + me._msg("post.author") + ': </span>';
378                html += '<span class="nodeAttrValue">' + authorLink + '</span>';
379                if (data.isPublished && data.postLink && data.postLink.length > 0)
380                {
381                   html += '<span class="separator"> </span>';
382                   html += '<span class="nodeAttrLabel">' + me._msg("post.externalLink") + ': </span>';
383                   html += '<span class="nodeAttrValue"><a target="_blank" href="' + data.postLink + '">' + me._msg("post.clickHere") + '</a></span>';
384                }
385                html += '</div>';
386                html += '<div class="content yuieditor"></div>';
387                html += '</div>';
388                // end view
389 
390                html += '</div>';
391 
392                // begin footer
393                html += '<div class="nodeFooter">';
394                html += '<span class="nodeAttrLabel replyTo">' + me._msg("post.replies") + ': </span>';
395                html += '<span class="nodeAttrValue">(' + data.commentCount + ')</span>';
396                html += '<span class="separator"> </span>';
397                html += '<span class="nodeAttrValue"><a href="' + postViewUrl + '">' + me._msg("post.read") + '</a></span>';
398                html += '<span class="separator"> </span>';
399                
400                html += '<span class="nodeAttrLabel tagLabel">' + me._msg("label.tags") +': </span>';
401                if (data.tags.length > 0)
402                {
403                   for (var x=0; x < data.tags.length; x++)
404                   {
405                      if (x > 0)
406                      {
407                          html += ', ';
408                      }
409                      html += Alfresco.util.tags.generateTagLink(me, data.tags[x]);
410                   }
411                }
412                else
413                {
414                   html += '<span class="nodeAttrValue">' + me._msg("post.noTags") + '</span>';
415                }
416                html += '</div></div>';
417                // end
418             }
419             
420             // simple view
421             else
422             {
423                // add a class to the parent div so that we can add a separator line in the simple view
424                Dom.addClass(elCell, 'row-separator');
425                
426                html += '<div class="node post simple">';
427                
428                // begin actions
429                html += Alfresco.util.blog.generateBlogPostActions(me, data, 'span', me.showPublishingActions);
430    
431                // begin view
432                html += '<div class="nodeContent">';
433                html += '<span class="nodeTitle"><a href="' + postViewUrl + '">' + $html(data.title) + '</a> ';
434                html += '<span class="theme-color-2 nodeStatus">' + statusLabel + '</span></span>';
435                html += '<div class="published">';
436                if (!data.isDraft)
437                {
438                   html += '<span class="nodeAttrLabel">' + me._msg("post.publishedOn") + ': </span>';
439                   html += '<span class="nodeAttrValue">' + Alfresco.util.formatDate(data.releasedOn) + '</span>';
440                   html += '<span class="separator"> </span>';
441                }
442                html += '<span class="nodeAttrLabel">' + me._msg("post.author") + ': </span>';
443                html += '<span class="nodeAttrValue">' + authorLink + '</span>';
444                if (data.isPublished && data.postLink && data.postLink.length > 0)
445                {
446                   html += '<span class="separator"> </span>';
447                   html += '<span class="nodeAttrLabel">' + me._msg("post.externalLink") + ': </span>';
448                   html += '<span class="nodeAttrValue"><a target="_blank" href="' + data.postLink + '">' + me._msg("post.clickHere") + '</a></span>';
449                }
450                html += '</div>';
451                html += '</div>';
452                html += '</div>';
453             }
454              
455             // assign html        
456             elCell.innerHTML = html;
457             
458             // finally add the content. We do this here to avoid a broken page layout, as
459             // data.content isn't valid xhtml.
460             if (!me.options.simpleView)
461             {
462                var contentElem = Dom.getElementsByClassName("content", "div", elCell);
463                if (contentElem.length == 1)
464                {
465                   contentElem[0].innerHTML = data.content;
466                }
467             }
468             
469             // now show the element
470             Dom.removeClass(elCell, 'hidden');
471          };
472 
473          // DataTable column defintions
474          var columnDefinitions = [
475          {
476             key: "blogposts", label: "BlogPosts", sortable: false, formatter: renderBlogPost
477          }];
478 
479          // DataTable definition
480          this.widgets.dataTable = new YAHOO.widget.DataTable(this.id + "-postlist", columnDefinitions, this.widgets.dataSource,
481          {
482             initialLoad: false,
483             dynamicData: true,
484             MSG_EMPTY: this._msg("message.loading")
485          });
486 
487          // Update totalRecords on the fly with value from server
488          this.widgets.dataTable.handleDataReturnPayload = function DL_handleDataReturnPayload(oRequest, oResponse, oPayload)
489          {
490             // Save totalRecords for Paginator update later
491             me.recordOffset = oResponse.meta.recordOffset;
492             me.totalRecords = oResponse.meta.totalRecords;
493 
494             oPayload = oPayload || {};
495             oPayload.recordOffset = oResponse.meta.recordOffset;
496             oPayload.totalRecords = oResponse.meta.totalRecords;
497             return oPayload;
498          }
499 
500          // Prevent the DataTable from updating the Paginator widget
501          this.widgets.dataTable.doBeforePaginatorChange = function DL_doBeforePaginatorChange(oPaginatorState)
502          {
503             return false;
504          }
505          
506          // Rendering complete event handler
507          this.widgets.dataTable.subscribe("renderEvent", function()
508          {
509             // Update the paginator if it's been created
510             this.widgets.paginator.setState(
511             {
512                recordOffset: this.recordOffset,
513                totalRecords: this.totalRecords
514             });
515             this.widgets.paginator.render();
516          }, this, true);
517 
518          // Custom error messages
519          this._setDefaultDataTableErrors(this.widgets.dataTable);
520 
521          // Hook tableMsgShowEvent to clear out fixed-pixel width on <table> element (breaks resizer)
522          this.widgets.dataTable.subscribe("tableMsgShowEvent", function(oArgs)
523          {
524             // NOTE: Scope needs to be DataTable
525             this._elMsgTbody.parentNode.style.width = "";
526          });
527          
528          // Override abstract function within DataTable to set custom error message
529          this.widgets.dataTable.doBeforeLoadData = function BlogPostList_doBeforeLoadData(sRequest, oResponse, oPayload)
530          {
531             if (oResponse.error)
532             {
533                try
534                {
535                   var response = YAHOO.lang.JSON.parse(oResponse.responseText);
536                   this.set("MSG_ERROR", response.message);
537                }
538                catch(e)
539                {
540                   me._setDefaultDataTableErrors(me.widgets.dataTable);
541                }
542             }
543             else if (oResponse.results && !me.options.usePagination)
544             {
545                this.renderLoopSize = Alfresco.util.RENDERLOOPSIZE;
546             }            
547             
548             // set whether publishing actions should be available
549             me.showPublishingActions = oResponse.meta.metadata.externalBlogConfig;
550             
551             // Must return true to have the "Loading..." message replaced by the error message
552             return true;
553          };
554          
555          // Enable row highlighting
556          this.widgets.dataTable.subscribe("rowMouseoverEvent", this.onEventHighlightRow, this, true);
557          this.widgets.dataTable.subscribe("rowMouseoutEvent", this.onEventUnhighlightRow, this, true);
558          
559          // Load the new blog posts by default
560          var filterObj = YAHOO.lang.merge(
561          {
562             filterId: "new",
563             filterOwner: "Alfresco.BlogPostListFilter",
564             filterData: null
565          }, this.options.initialFilter);
566          YAHOO.Bubbling.fire("changeFilter", filterObj);
567       },
568       
569       // Actions
570 
571       /**
572        * Action handler for the simple view toggle button
573        * 
574        * @method onSimpleView
575        */
576       onSimpleView: function BlogPostList_onSimpleView(e, p_obj)
577       {
578          this.options.simpleView = !this.options.simpleView;
579          p_obj.set("label", this._msg(this.options.simpleView ? "header.detailList" : "header.simpleList"));
580 
581          // refresh the list
582          YAHOO.Bubbling.fire("blogpostlistRefresh");
583          Event.preventDefault(e);
584       },
585       
586       /**
587        * Handler for the view blog post action links
588        *
589        * @method onViewBlogPost
590        * @param row {object} DataTable row representing post to be actioned
591        */
592       onViewBlogPost: function BlogPostList_onViewNode(row)
593       {
594          var record = this.widgets.dataTable.getRecord(row);
595          window.location = this._generatePostViewUrl(record.getData('name'));
596       },
597 
598       /**
599        * Handler for the edit blog post action links
600        *
601        * @method onEditBlogPost
602        * @param row {object} DataTable row representing post to be actioned
603        */
604       onEditBlogPost: function BlogPostList_onEditBlogPost(row)
605       {
606          var record = this.widgets.dataTable.getRecord(row);
607          var url = YAHOO.lang.substitute(Alfresco.constants.URL_PAGECONTEXT + "site/{site}/blog-postedit?postId={postId}",
608          {
609             site: this.options.siteId,
610             postId: record.getData('name')
611          });
612          window.location = url;
613       },
614       
615       /**
616        * Handler for the delete blog post action links
617        *
618        * @method onDeleteBlogPost
619        * @param row {object} DataTable row representing post to be actioned
620        */
621       onDeleteBlogPost: function BlogPostList_onDeleteBlogPost(row)
622       {  
623          var record = this.widgets.dataTable.getRecord(row);
624          var me = this;
625          Alfresco.util.PopupManager.displayPrompt(
626          {
627             title: this._msg("message.confirm.delete.title"),
628             text: this._msg("message.confirm.delete", $html(record.getData('title'))),
629             buttons: [
630             {
631                text: this._msg("button.delete"),
632                handler: function BlogPostList_onDeleteBlogPost_delete()
633                {
634                   this.destroy();
635                   me._deleteBlogPostConfirm.call(me, record.getData('name'));
636                }
637             },
638             {
639                text: this._msg("button.cancel"),
640                handler: function BlogPostList_onDeleteBlogPost_cancel()
641                {
642                   this.destroy();
643                },
644                isDefault: true
645             }]
646          });
647       },
648       
649       /**
650        * Handler for the publish external action links
651        *
652        * @method onPublishExternal
653        * @param row {object} DataTable row representing post to be actioned
654        */
655       onPublishExternal: function BlogPostList_onPublishExternal(row)
656       {
657          var record = this.widgets.dataTable.getRecord(row);
658          this._publishExternal(record.getData('name'));
659       },
660       
661       /**
662        * Handler for the update external action links
663        *
664        * @method onUpdateExternal
665        * @param row {object} DataTable row representing post to be actioned
666        */
667       onUpdateExternal: function BlogPostList_onUpdateExternal(row)
668       {
669          var record = this.widgets.dataTable.getRecord(row);
670          this._updateExternal(record.getData('name'));
671       },
672       
673       /**
674        * Handler for the unpublish external action links
675        *
676        * @method onUnpublishExternal
677        * @param row {object} DataTable row representing post to be actioned
678        */
679       onUnpublishExternal: function BlogPostList_onUnpublishExternal(row)
680       {
681          var record = this.widgets.dataTable.getRecord(row);
682          this._unpublishExternal(record.getData('name'));
683       },
684       
685       /**
686        * Tag selected handler
687        *
688        * @method onTagSelected
689        */
690       onTagSelected: function BlogPostList_onTagSelected(layer, args)
691       {
692          var obj = args[1];
693          if (obj && (obj.tagName !== null))
694          {
695             var filterObj =
696             {
697                filterId: obj.tagName,
698                filterOwner: "Alfresco.BlogPostListTags",
699                filterData: null
700             };
701             YAHOO.Bubbling.fire("changeFilter", filterObj);
702          }
703       },
704       
705       /**
706        * On blog config changed h handler
707        *
708        * @method onTagSelected
709        */
710       onBlogConfigChanged: function BlogPostList_onBlogConfigChanged(layer, args)
711       {
712          // refresh the list
713          this._updateBlogPostList();
714       },
715       
716       // Actions implementation
717       
718       /**
719        * Blog post deletion implementation
720        * 
721        * @method _deleteBlogPostConfirm
722        * @param postId {string} the id of the blog post to delete
723        */
724       _deleteBlogPostConfirm: function BlogPostList__deleteBlogPostConfirm(postId)
725       {
726          // show busy message
727          if (! this._setBusy(this._msg('message.wait')))
728          {
729             return;
730          }
731           
732          // ajax request success handler
733          var onDeletedSuccess = function BlogPostList_deleteBlogPostConfirm_onDeletedSuccess(response)
734          {
735             // remove busy message
736             this._releaseBusy();
737             
738             // reload the table data
739             this._updateBlogPostList();
740          };
741          
742          // get the url to call
743          var url = YAHOO.lang.substitute(Alfresco.constants.PROXY_URI + "api/blog/post/site/{site}/{container}/{postId}?page=blog-postlist",
744          {
745             site: this.options.siteId,
746             container: this.options.containerId,
747             postId: encodeURIComponent(postId)
748          });
749          
750          // execute ajax request
751          Alfresco.util.Ajax.request(
752          {
753             url: url,
754             method: "DELETE",
755             responseContentType : "application/json",
756             successMessage: this._msg("message.delete.success"),
757             successCallback:
758             {
759                fn: onDeletedSuccess,
760                scope: this
761             },
762             failureMessage: this._msg("message.delete.failure"),
763             failureCallback:
764             {
765                fn: function(response)
766                {
767                   this._releaseBusy();
768                },
769                scope: this
770             }
771          });
772       },
773       
774       /**
775        * Publishing of a blog post implementation
776        * 
777        * @method _publishExternal
778        * @param postId {string} the id of the blog post to publish
779        */
780       _publishExternal: function BlogPostList__publishExternal(postId)
781       {
782          // show busy message
783          if (! this._setBusy(this._msg('message.wait')))
784          {
785             return;
786          }
787           
788          // ajax call success handler
789          var onPublishedSuccess = function BlogPostList_onPublishedSuccess(response)
790          {
791             // remove busy message
792             this._releaseBusy();
793             
794             // reload the table data
795             this._updateBlogPostList();
796          };
797          
798          // get the url to call
799          var url = Alfresco.util.blog.generatePublishingRestURL(this.options.siteId, this.options.containerId, postId);
800          
801          // execute ajax request
802          Alfresco.util.Ajax.request(
803          {
804             url: url,
805             method: "POST",
806             requestContentType : "application/json",
807             responseContentType : "application/json",
808             dataObj:
809             {
810                action : "publish"
811             },
812             successMessage: this._msg("message.publishExternal.success"),
813             successCallback:
814             {
815                fn: onPublishedSuccess,
816                scope: this
817             },
818             failureMessage: this._msg("message.publishExternal.failure"),
819             failureCallback:
820             {
821                fn: function(response) { this._releaseBusy(); },
822                scope: this
823             }
824          });
825       },
826       
827 
828       /**
829        * Updating of an external published blog post implementation
830        * 
831        * @method _updateExternal
832        * @param postId {string} the id of the blog post to update
833        */
834       _updateExternal: function BlogPostList__updateExternal(postId)
835       {
836          // show busy message
837          if (! this._setBusy(this._msg('message.wait')))
838          {
839             return;
840          }
841           
842          // ajax request success handler
843          var onUpdatedSuccess = function BlogPostList_onUpdatedSuccess(response)
844          {
845             // remove busy message
846             this._releaseBusy();
847              
848             // reload the table data
849             this._updateBlogPostList();
850          };
851          
852          // get the url to call
853          var url = Alfresco.util.blog.generatePublishingRestURL(this.options.siteId, this.options.containerId, postId);
854          
855          // execute ajax request
856          Alfresco.util.Ajax.request(
857          {
858             url: url,
859             method: "POST",
860             requestContentType : "application/json",
861             responseContentType : "application/json",
862             dataObj:
863             {
864                action : "update"
865             },
866             successMessage: this._msg("message.updateExternal.success"),
867             successCallback:
868             {
869                fn: onUpdatedSuccess,
870                scope: this
871             },
872             failureMessage: this._msg("message.updateExternal.failure"),
873             failureCallback:
874             {
875                fn: function(response) { this._releaseBusy(); },
876                scope: this
877             }
878          });
879       },
880 
881 
882       /**
883        * Unpublishing of an external published blog post implementation
884        * 
885        * @method _unpublishExternal
886        * @param postId {string} the id of the blog post to update
887        */
888       _unpublishExternal: function BlogPostList__onUnpublishExternal(postId)
889       {
890          // show busy message
891          if (! this._setBusy(this._msg('message.wait')))
892          {
893             return;
894          }
895           
896          // ajax request success handler
897          var onUnpublishedSuccess = function BlogPostList_onUnpublishedSuccess(response)
898          {
899             // remove busy message
900             this._releaseBusy();
901              
902             // reload the table data
903             this._updateBlogPostList();
904          };
905           
906          // get the url to call
907          var url = Alfresco.util.blog.generatePublishingRestURL(this.options.siteId, this.options.containerId, postId);
908          
909          // execute ajax request
910          Alfresco.util.Ajax.request(
911          {
912             url: url,
913             method: "POST",
914             requestContentType : "application/json",
915             responseContentType : "application/json",
916             dataObj:
917             {
918                action : "unpublish"
919             },
920             successMessage: this._msg("message.unpublishExternal.success"),
921             successCallback:
922             {
923                fn: onUnpublishedSuccess,
924                scope: this
925             },
926             failureMessage: this._msg("message.unpublishExternal.failure"),
927             failureCallback:
928             {
929                fn: function(response)
930                {
931                   this._releaseBusy();
932                },
933                scope: this
934             }
935          });
936       },
937       
938 
939       // row highlighting
940       
941       /**
942        * Custom event handler to highlight row.
943        *
944        * @method onEventHighlightRow
945        * @param oArgs.event {HTMLEvent} Event object.
946        * @param oArgs.target {HTMLElement} Target element.
947        */
948       onEventHighlightRow: function BlogPostList_onEventHighlightRow(oArgs)
949       {
950          // only highlight if we got actions to show
951          var record = this.widgets.dataTable.getRecord(oArgs.target.id);
952          var permissions = record.getData('permissions');
953          if (!(permissions.edit || permissions["delete"]))
954          {
955             return;
956          }
957           
958          var elem = Dom.getElementsByClassName('post', null, oArgs.target, null);
959          Dom.addClass(elem, 'over');
960       },
961 
962       /**
963        * Custom event handler to unhighlight row.
964        *
965        * @method onEventUnhighlightRow
966        * @param oArgs.event {HTMLEvent} Event object.
967        * @param oArgs.target {HTMLElement} Target element.
968        */
969       onEventUnhighlightRow: function BlogPostList_onEventUnhighlightRow(oArgs)
970       {
971          var elem = Dom.getElementsByClassName('post', null, oArgs.target, null);
972          Dom.removeClass(elem, 'over');
973       },
974       
975       
976       /**
977        * BlogPostList Change filter event handler
978        *
979        * @method onChangeFilter
980        * @param layer {object} Event fired (unused)
981        * @param args {array} Event parameters (new filterId)
982        */
983       onChangeFilter: function BlogPostList_onChangeFilter(layer, args)
984       {
985          var obj = args[1];
986          if ((obj !== null) && (obj.filterId !== null))
987          {
988             this.currentFilter =
989             {
990                filterId: obj.filterId,
991                filterOwner: obj.filterOwner,
992                filterData: obj.filterData
993             };
994             this._updateBlogPostList(
995             {
996                page: 1
997             });
998             YAHOO.Bubbling.fire("filterChanged", this.currentFilter);
999          }
1000       },
1001       
1002       /**
1003        * Deactivate All Controls event handler
1004        *
1005        * @method onDeactivateAllControls
1006        * @param layer {object} Event fired
1007        * @param args {array} Event parameters (depends on event type)
1008        */
1009       onDeactivateAllControls: function BlogPostList_onDeactivateAllControls(layer, args)
1010       {
1011          var index, widget, fnDisable = Alfresco.util.disableYUIButton;
1012          for (index in this.widgets)
1013          {
1014             if (this.widgets.hasOwnProperty(index))
1015             {
1016                fnDisable(this.widgets[index]);
1017             }
1018          }
1019       },
1020       
1021       /**
1022        * Updates the list title considering the current active filter.
1023        */
1024       updateListTitle: function BlogPostList_updateListTitle()
1025       {
1026          var elem = Dom.get(this.id + '-listtitle');
1027          var title = this._msg("title.postlist");
1028 
1029          var filterOwner = this.currentFilter.filterOwner;
1030          var filterId = this.currentFilter.filterId;
1031          var filterData = this.currentFilter.filterData;
1032          if (filterOwner == "Alfresco.BlogPostListFilter")
1033          {
1034             if (filterId == "all")
1035             {
1036                 title = this._msg("title.allposts");
1037             }
1038             if (filterId == "new")
1039             {
1040                title = this._msg("title.newposts");
1041             }
1042             else if (filterId == "mydrafts")
1043             {
1044                title = this._msg("title.mydrafts");
1045             }
1046             else if (filterId == "mypublished")
1047             {
1048                title = this._msg("title.mypublished");
1049             }
1050             else if (filterId == "publishedext")
1051             {
1052                title = this._msg("title.publishedext");
1053             }
1054          }
1055          else if (filterOwner == "Alfresco.BlogPostListTags")
1056          {
1057             title = this._msg("title.bytag", $html(filterData));
1058          }
1059          else if (filterOwner == "Alfresco.BlogPostListArchive" && filterId == "bymonth")
1060          {
1061             var date = new Date(filterData.year, filterData.month, 1);
1062             var formattedDate = Alfresco.util.formatDate(date, this._msg("date-format.monthYear"));
1063             title = this._msg("title.bymonth", formattedDate);
1064          }
1065          
1066          elem.innerHTML = title;
1067       },
1068 
1069 
1070       /**
1071        * BlogPostList Refresh Required event handler
1072        *
1073        * @method onBlogPostListRefresh
1074        * @param layer {object} Event fired (unused)
1075        * @param args {array} Event parameters (unused)
1076        */
1077       onBlogPostListRefresh: function BlogPostList_onBlogPostListRefresh(layer, args)
1078       {
1079          this._updateBlogPostList();
1080       },
1081 
1082       /**
1083        * Displays the provided busyMessage but only in case
1084        * the component isn't busy set.
1085        * 
1086        * @return true if the busy state was set, false if the component is already busy
1087        */
1088       _setBusy: function BlogPostList__setBusy(busyMessage)
1089       {
1090          if (this.busy)
1091          {
1092             return false;
1093          }
1094          this.busy = true;
1095          this.widgets.busyMessage = Alfresco.util.PopupManager.displayMessage(
1096          {
1097             text: busyMessage,
1098             spanClass: "wait",
1099             displayTime: 0
1100          });
1101          return true;
1102       },
1103       
1104       /**
1105        * Removes the busy message and marks the component as non-busy
1106        */
1107       _releaseBusy: function BlogPostList__releaseBusy()
1108       {
1109          if (this.busy)
1110          {
1111             this.widgets.busyMessage.destroy();
1112             this.busy = false;
1113             return true;
1114          }
1115          else
1116          {
1117             return false;
1118          }
1119       },
1120 
1121       /**
1122        * Gets a custom message
1123        *
1124        * @method _msg
1125        * @param messageId {string} The messageId to retrieve
1126        * @return {string} The custom message
1127        * @private
1128        */
1129       _msg: function BlogPostList_msg(messageId)
1130       {
1131          return Alfresco.util.message.call(this, messageId, "Alfresco.BlogPostList", Array.prototype.slice.call(arguments).slice(1));
1132       },
1133 
1134       /**
1135        * Resets the YUI DataTable errors to our custom messages
1136        * NOTE: Scope could be YAHOO.widget.DataTable, so can't use "this"
1137        *
1138        * @method _setDefaultDataTableErrors
1139        * @param dataTable {object} Instance of the DataTable
1140        */
1141       _setDefaultDataTableErrors: function BlogPostList__setDefaultDataTableErrors(dataTable)
1142       {
1143          var msg = Alfresco.util.message;
1144          dataTable.set("MSG_EMPTY", msg("message.empty", "Alfresco.BlogPostList"));
1145          dataTable.set("MSG_ERROR", msg("message.error", "Alfresco.BlogPostList"));
1146       },
1147       
1148       /**
1149        * Updates blog post list by calling data webscript with current site and filter information
1150        *
1151        * @method _updateBlogPostList
1152        */
1153       _updateBlogPostList: function BlogPostList__updateBlogPostList(p_obj)
1154       {
1155          // show busy message
1156          /*if (! this._setBusy(this._msg('message.wait')))
1157          {
1158             return;
1159          }*/
1160           
1161          // Reset the custom error messages
1162          this._setDefaultDataTableErrors(this.widgets.dataTable);
1163          
1164          // ajax request success handler
1165          var successHandler = function BlogPostList__updateBlogPostList_successHandler(sRequest, oResponse, oPayload)
1166          {
1167             // remove busy message
1168             //this._releaseBusy();
1169             
1170             this.widgets.dataTable.onDataReturnInitializeTable.call(this.widgets.dataTable, sRequest, oResponse, oPayload);
1171             this.updateListTitle();
1172          };
1173          
1174          // ajax request failure handler
1175          var failureHandler = function BlogPostList__updateBlogPostList_failureHandler(sRequest, oResponse)
1176          {
1177             // remove busy message
1178             //this._releaseBusy();
1179             
1180             if (oResponse.status == 401)
1181             {
1182                // Our session has likely timed-out, so refresh to offer the login page
1183                window.location.reload(true);
1184             }
1185             else
1186             {
1187                try
1188                {
1189                   var response = YAHOO.lang.JSON.parse(oResponse.responseText);
1190                   this.widgets.dataTable.set("MSG_ERROR", response.message);
1191                   this.widgets.dataTable.showTableMessage(response.message, YAHOO.widget.DataTable.CLASS_ERROR);
1192                   if (oResponse.status == 404)
1193                   {
1194                      // Site or container not found - deactivate controls
1195                      YAHOO.Bubbling.fire("deactivateAllControls");
1196                   }
1197                }
1198                catch(e)
1199                {
1200                   this._setDefaultDataTableErrors(this.widgets.dataTable);
1201                }
1202             }
1203          };
1204          
1205          // get the url to call
1206          this.widgets.dataSource.sendRequest(this._buildBlogPostListParams(p_obj || {}),
1207          {
1208             success: successHandler,
1209             failure: failureHandler,
1210             scope: this
1211          });
1212       },
1213       
1214       /**
1215        * Build URI parameter string for doclist JSON data webscript
1216        *
1217        * @method _buildDocListParams
1218        * @param p_obj.page {string} Page number
1219        * @param p_obj.pageSize {string} Number of items per page
1220        */
1221       _buildBlogPostListParams: function BlogPostList__buildDocListParams(p_obj)
1222       {
1223          var params =
1224          {
1225             contentLength: this.options.maxContentLength,
1226             fromDate: null,
1227             toDate: null,
1228             tag: null,
1229             page: this.widgets.paginator.getCurrentPage() || "1",
1230             pageSize: this.widgets.paginator.getRowsPerPage()
1231          };
1232          
1233          // Passed-in overrides
1234          if (typeof p_obj == "object")
1235          {
1236             params = YAHOO.lang.merge(params, p_obj);
1237          }
1238 
1239          // calculate the startIndex param
1240          params.startIndex = (params.page-1) * params.pageSize;
1241 
1242          // check what url to call and with what parameters
1243          var filterOwner = this.currentFilter.filterOwner;
1244          var filterId = this.currentFilter.filterId;
1245          var filterData = this.currentFilter.filterData;       
1246          
1247          // check whether we got a filter or not
1248          var url = "";
1249          if (filterOwner == "Alfresco.BlogPostListFilter")
1250          {
1251             // latest only
1252             if (filterId == "all")
1253             {
1254                 url = "";
1255             }
1256             if (filterId == "new")
1257             {
1258                 url = "/new";
1259             }
1260             else if (filterId == "mydrafts")
1261             {
1262                 url = "/mydrafts";
1263             }
1264             else if (filterId == "mypublished")
1265             {
1266                 url = "/mypublished";
1267             }
1268             else if (filterId == "publishedext")
1269             {
1270                 url = "/publishedext";
1271             }
1272          }
1273          else if (filterOwner == "Alfresco.TagFilter")
1274          {
1275             params.tag = encodeURIComponent(filterData);
1276          }
1277          else if (filterOwner == "Alfresco.BlogPostListArchive" && filterId == "bymonth")
1278          {
1279             var fromDate = new Date(filterData.year, filterData.month, 1);
1280             var toDate = new Date(filterData.year, filterData.month + 1, 1);
1281             toDate = new Date(toDate.getTime() - 1);
1282             params.fromDate = fromDate.getTime();
1283             params.toDate = toDate.getTime();
1284          }
1285          
1286          // build the url extension
1287          var urlExt = "", paramName;
1288          for (paramName in params)
1289          {
1290             if (params[paramName] !== null)
1291             {
1292                urlExt += "&" + paramName + "=" + encodeURIComponent(params[paramName]);
1293             }
1294          }
1295          if (urlExt.length > 0)
1296          {
1297             urlExt = urlExt.substring(1);
1298          }
1299          return url + "?" + urlExt;
1300       }
1301    };
1302 })();
1303