1 /**
  2  * BlogPostView component.
  3  * 
  4  * Component to view a blog post
  5  * 
  6  * @namespace Alfresco
  7  * @class Alfresco.BlogPostView
  8  */
  9 (function()
 10 {
 11    /**
 12     * YUI Library aliases
 13     */
 14    var Dom = YAHOO.util.Dom,
 15        Event = YAHOO.util.Event,
 16        Element = YAHOO.util.Element;
 17 
 18    /**
 19     * Alfresco Slingshot aliases
 20     */
 21    var $html = Alfresco.util.encodeHTML;
 22     
 23    /**
 24     * BlogPostView constructor.
 25     * 
 26     * @param {String} htmlId The HTML id of the parent element
 27     * @return {Alfresco.BlogPostView} The new Post instance
 28     * @constructor
 29     */
 30    Alfresco.BlogPostView = function(htmlId)
 31    {
 32       /* Mandatory properties */
 33       this.name = "Alfresco.BlogPostView";
 34       this.id = htmlId;
 35       
 36       /* Initialise prototype properties */
 37       this.widgets = {};
 38       this.tagId =
 39       {
 40          id: 0,
 41          tags: {}
 42       };
 43       
 44       /* Register this component */
 45       Alfresco.util.ComponentManager.register(this);
 46 
 47       /* Load YUI Components */
 48       Alfresco.util.YUILoaderHelper.require(["json", "connection", "event", "button", "menu"], this.onComponentsLoaded, this);
 49       
 50       /* Decoupled event listeners */
 51       YAHOO.Bubbling.on("tagSelected", this.onTagSelected, this);
 52 
 53       return this;
 54    };
 55    
 56    Alfresco.BlogPostView.prototype =
 57    {
 58       /**
 59        * Object container for initialization options
 60        *
 61        * @property options
 62        * @type object
 63        */
 64       options:
 65       {
 66          /**
 67           * Current siteId.
 68           * 
 69           * @property siteId
 70           * @type string
 71           */
 72          siteId: "",
 73          
 74          /**
 75           * ContainerId representing root container
 76           *
 77           * @property containerId
 78           * @type string
 79           * @default "blog"
 80           */
 81          containerId: "blog",
 82          
 83          /**
 84           * Id of the displayed blog post.
 85           */
 86          postId: ""
 87       },
 88       
 89       /**
 90        * Stores the data displayed by this component
 91        */
 92       blogPostData: null,
 93       
 94       /**
 95        * Object container for storing YUI widget instances.
 96        * 
 97        * @property widgets
 98        * @type object
 99        */
100       widgets : null,
101       
102       /**
103        * Object literal used to generate unique tag ids
104        * 
105        * @property tagId
106        * @type object
107        */
108       tagId: null,
109       
110       /**
111        * Tells whether an action is currently ongoing.
112        * 
113        * @property busy
114        * @type boolean
115        * @see setBusy/releaseBusy
116        */
117       busy: false,
118       
119       /**
120        * True if publishing actions should be displayed
121        * 
122        * @property showPublishingActions
123        * @type boolean
124        */
125       showPublishingActions: false,
126       
127       /**
128        * Set multiple initialization options at once.
129        *
130        * @method setOptions
131        * @param obj {object} Object literal specifying a set of options
132        */
133       setOptions: function BlogPostView_setOptions(obj)
134       {
135          this.options = YAHOO.lang.merge(this.options, obj);
136          return this;
137       },
138 
139       /**
140        * Set messages for this component.
141        *
142        * @method setMessages
143        * @param obj {object} Object literal specifying a set of messages
144        * @return {Alfresco.BlogPostView} returns 'this' for method chaining
145        */
146       setMessages: function BlogPostView_setMessages(obj)
147       {
148          Alfresco.util.addMessages(obj, this.name);
149          return this;
150       },
151       
152       /**
153        * Fired by YUILoaderHelper when required component script files have
154        * been loaded into the browser.
155        *
156        * @method onComponentsLoaded
157        */
158       onComponentsLoaded: function BlogPostView_onComponentsLoaded()
159       {
160          Event.onContentReady(this.id, this.onReady, this, true);
161       },
162    
163       /**
164        * Fired by YUI when parent element is available for scripting.
165        * Component initialisation, including instantiation of YUI widgets and event listener binding.
166        *
167        * @method onReady
168        */
169       onReady: function BlogPostView_onReady()
170       {
171          var me = this;
172          
173          // Hook action events.
174          var fnActionHandlerDiv = function BlogPostView_fnActionHandlerDiv(layer, args)
175          {
176             var owner = YAHOO.Bubbling.getOwnerByTagName(args[1].anchor, "div");
177             if (owner !== null)
178             {
179                var action = "";
180                action = owner.className;
181                if (typeof me[action] == "function")
182                {
183                   me[action].call(me, me.blogPostData.name);
184                   args[1].stop = true;
185                }
186             }
187             return true;
188          };
189          YAHOO.Bubbling.addDefaultAction("blogpost-action-link-div", fnActionHandlerDiv);
190          
191          // Hook tag clicks
192          Alfresco.util.tags.registerTagActionHandler(this);
193           
194          // initialize the mouse over listener
195          Alfresco.util.rollover.registerHandlerFunctions(this.id, this.onPostElementMouseEntered, this.onPostElementMouseExited, this);
196           
197          // load the post data
198          this._loadBlogPostData();
199       },
200       
201       /**
202        * Loads the comments for the provided nodeRef and refreshes the ui
203        */
204       _loadBlogPostData: function BlogPostView__loadBlogPostData()
205       {
206          // construct the request url
207          var url = YAHOO.lang.substitute(Alfresco.constants.URL_SERVICECONTEXT + "components/blog/post/site/{site}/{container}/{postId}",
208          {
209             site : this.options.siteId,
210             container: this.options.containerId,
211             postId: this.options.postId
212          });
213          
214          // execute ajax request
215          Alfresco.util.Ajax.request(
216          {
217             url: url,
218             successCallback:
219             {
220                fn: this.loadBlogPostDataSuccess,
221                scope: this
222             },
223             failureMessage: this._msg("message.loadpostdata.failure")
224          });
225       },
226 
227       /**
228        * Success handler for a blog post request. Updates the UI using the blog post data
229        * provided in the response object.
230        * 
231        * @param response {object} the ajax request response
232        */
233       loadBlogPostDataSuccess: function BlogPostView_loadCommentsSuccess(response)
234       {
235          // store the returned data locally
236          var data = response.json.item;
237          this.blogPostData = data;
238          this.showPublishingActions = response.json.metadata.externalBlogConfig;
239          
240          // get the container div to insert the the post into
241          var viewDiv = Dom.get(this.id + '-post-view-div');
242          
243          // render the blog post and insert it into the div
244          var html = this.renderBlogPost(data);
245          viewDiv.innerHTML = html;
246          
247          // attach the rollover listeners
248          Alfresco.util.rollover.registerListenersByClassName(this.id, 'post', 'div');
249          
250          // inform interested comment components about the loaded blog post
251          this.sendCommentedNodeEvent();
252       },
253       
254       /**
255        * Sends out a setCommentedNode bubble event.
256        */
257       sendCommentedNodeEvent: function BlogPostView_sendCommentedNodeEvent()
258       {
259          var eventData =
260          {
261             nodeRef: this.blogPostData.nodeRef,
262             title: this.blogPostData.title,
263             page: "blog-postview",
264             pageParams:
265             {
266                postId: this.blogPostData.name
267             }
268          };
269          YAHOO.Bubbling.fire("setCommentedNode", eventData);
270       },
271       
272       /**
273        * Renders the blog post given a blog post data object returned by the server.
274        */
275       renderBlogPost: function BlogPostView_renderBlogPost(data)
276       {
277          // preformat some values
278          var postViewUrl = Alfresco.util.blog.generateBlogPostViewUrl(this.options.siteId, this.options.containerId, data.name);
279          var statusLabel = Alfresco.util.blog.generatePostStatusLabel(this, data);
280          var authorLink = Alfresco.util.people.generateUserLink(data.author);
281           
282          var html = '<div id="' + this.id + '-postview" class="node post postview theme-bg-color-6 theme-border-3">';
283          html += Alfresco.util.blog.generateBlogPostActions(this, data, 'div', this.showPublishingActions);
284   
285          // content
286          html += '<div class="nodeContent">';
287          html += '<div class="nodeTitle"><a href="' + postViewUrl + '">' + $html(data.title) + '</a> ';
288          html += '<span class="theme-color-2 nodeStatus">' + statusLabel + '</span>';
289          html += '</div>';
290           
291          html += '<div class="published">';
292          if (!data.isDraft)
293          {
294             html += '<span class="nodeAttrLabel">' + this._msg("post.publishedOn") + ': </span>';
295             html += '<span class="nodeAttrValue">' + Alfresco.util.formatDate(data.releasedOn) + '</span>';
296             html += '<span class="separator"> </span>';
297          }
298  
299          html += '<span class="nodeAttrLabel">' + this._msg("post.author") + ': </span>';
300          html += '<span class="nodeAttrValue">' + authorLink + '</span>';
301 
302          if (data.isPublished && data.postLink !== undefined && data.postLink.length > 0)
303          {
304             html += '<span class="separator"> </span>';
305             html += '<span class="nodeAttrLabel">' + this._msg("post.externalLink") + ': </span>';
306             html += '<span class="nodeAttrValue"><a target="_blank" href="' + data.postLink + '">' + this._msg("post.clickHere") + '</a></span>';
307          }
308          
309          html += '<span class="separator"> </span>';
310          html += '<span class="nodeAttrLabel tagLabel">' + this._msg("label.tags") + ': </span>';
311          if (data.tags.length > 0)
312          {
313             for (var x=0; x < data.tags.length; x++)
314             {
315                if (x > 0)
316                {
317                   html += ', ';
318                }
319                html += Alfresco.util.tags.generateTagLink(this, data.tags[x]);
320             }
321          }
322          else
323          {
324             html += '<span class="nodeAttrValue">' + this._msg("post.noTags") + '</span>';
325          }
326          html += '</div>';
327       
328          html += '<div class="content yuieditor">' + data.content + '</div>';
329          html += '</div></div>';
330          return html;
331       },
332 
333       
334       // Actions
335       
336       /**
337        * Tag selected handler.
338        *
339        * @method onTagSelected
340        * @param tagId {string} Tag name.
341        * @param target {HTMLElement} Target element clicked.
342        */
343       onTagSelected: function BlogPostView_onTagSelected(layer, args)
344       {
345          var obj = args[1];
346          if (obj && (obj.tagName !== null))
347          {
348             var url = YAHOO.lang.substitute(Alfresco.constants.URL_PAGECONTEXT + "site/{site}/blog-postlist?filterId={filterId}&filterOwner={filterOwner}&filterData={filterData}",
349             {
350                site: this.options.siteId,
351                filterId: "tag",
352                filterOwner: "Alfresco.BlogPostListTags",
353                filterData: encodeURIComponent(obj.tagName)
354             });
355             window.location = url;
356          }
357       },
358 
359       /**
360        * Loads the edit post form and displays it instead of the content
361        * The div class should have the same name as the above function (onEditNode)
362        */
363       onEditBlogPost: function BlogPostView_onEditNode(postId)
364       {
365          var url = YAHOO.lang.substitute(Alfresco.constants.URL_PAGECONTEXT + "site/{site}/blog-postedit?postId={postId}",
366          {
367             site: this.options.siteId,
368             postId: postId
369          });    
370          window.location = url;
371       },
372 
373       /**
374        * Blog post deletion implementation
375        * 
376        * @method onDeleteBlogPost
377        * @param postId {string} the id of the blog post to delete
378        */
379       onDeleteBlogPost: function BlogPostView_onDeleteBlogPost(postId)
380       {
381          var me = this;
382          Alfresco.util.PopupManager.displayPrompt(
383          {
384             title: this._msg("message.confirm.delete.title"),
385             text: this._msg("message.confirm.delete", $html(this.blogPostData.title)),
386             buttons: [
387             {
388                text: this._msg("button.delete"),
389                handler: function BlogPostList_onDeleteBlogPost_delete()
390                {
391                   this.destroy();
392                   me._deleteBlogPostConfirm.call(me, me.blogPostData.name);
393                }
394             },
395             {
396                text: this._msg("button.cancel"),
397                handler: function BlogPostList_onDeleteBlogPost_cancel()
398                {
399                   this.destroy();
400                },
401                isDefault: true
402             }]
403          });
404       },
405       
406       /**
407        * Blog post deletion implementation
408        * 
409        * @method _deleteBlogPostConfirm
410        * @param postId {string} the id of the blog post to delete
411        */
412       _deleteBlogPostConfirm: function BlogPostView__deleteBlogPostConfirm(postId)
413       {
414          // show busy message
415          if (! this._setBusy(this._msg('message.wait')))
416          {
417             return;
418          }
419           
420          // ajax request success handler
421          var onDeletedSuccess = function BlogPostList_onDeletedSuccess(response)
422          {
423             // remove busy message
424             this._releaseBusy();
425             
426             // load the blog post list page
427             var url = YAHOO.lang.substitute(Alfresco.constants.URL_PAGECONTEXT + "site/{site}/blog-postlist",
428             {
429                site: this.options.siteId
430             });    
431             window.location = url;
432          };
433          
434          // get the url to call
435          var url = YAHOO.lang.substitute(Alfresco.constants.PROXY_URI + "api/blog/post/site/{site}/{container}/{postId}?page={page}",
436          {
437             site: this.options.siteId,
438             container: this.options.containerId,
439             postId: encodeURIComponent(postId),
440             page: "blog-postlist"
441          });
442          
443          // execute ajax request
444          Alfresco.util.Ajax.request(
445          {
446             url: url,
447             method: "DELETE",
448             responseContentType : "application/json",
449             successMessage: this._msg("message.delete.success"),
450             successCallback:
451             {
452                fn: onDeletedSuccess,
453                scope: this
454             },
455             failureMessage: this._msg("message.delete.failure"),
456             failureCallback:
457             {
458                fn: function(response)
459                {
460                   this._releaseBusy();
461                },
462                scope: this
463             }
464          });
465       },
466        
467        
468       /**
469        * Publishing of a blog post
470        * 
471        * @method onPublishExternal
472        * @param postId {string} the id of the blog post to publish
473        */
474       onPublishExternal: function BlogPostView_onPublishExternal(postId)
475       {
476          // show busy message
477          if (! this._setBusy(this._msg('message.wait')))
478          {
479             return;
480          }
481          
482          // ajax call success handler
483          var onPublishedSuccess = function BlogPostList_onPublishedSuccess(response)
484          {
485             // remove busy message
486             this._releaseBusy();
487             
488             // re-render the post
489             this.loadBlogPostDataSuccess(response);
490          };
491          
492          // get the url to call
493          var url = Alfresco.util.blog.generatePublishingRestURL(this.options.siteId, this.options.containerId, postId);
494          
495          // execute ajax request
496          Alfresco.util.Ajax.request(
497          {
498             url: url,
499             method: "POST",
500             requestContentType : "application/json",
501             responseContentType : "application/json",
502             dataObj:
503             {
504                action : "publish"
505             },
506             successMessage: this._msg("message.publishExternal.success"),
507             successCallback:
508             {
509                fn: onPublishedSuccess,
510                scope: this
511             },
512             failureMessage: this._msg("message.publishExternal.failure"),
513             failureCallback:
514             {
515                fn: function(response) { this._releaseBusy(); },
516                scope: this
517             }
518          });
519       },
520       
521 
522       /**
523        * Updating of an external published blog post implementation
524        * 
525        * @method onUpdateExternal
526        * @param postId {string} the id of the blog post to update
527        */
528       onUpdateExternal: function BlogPostView_onUpdateExternal(postId)
529       {
530          // show busy message
531          if (! this._setBusy(this._msg('message.wait')))
532          {
533             return;
534          }
535          
536          // ajax request success handler
537          var onUpdatedSuccess = function BlogPostList_onUpdatedSuccess(response)
538          {
539             // remove busy message
540             this._releaseBusy();
541             
542             // re-render the post
543             this.loadBlogPostDataSuccess(response);
544          };
545          
546          // get the url to call
547          var url = Alfresco.util.blog.generatePublishingRestURL(this.options.siteId, this.options.containerId, postId);
548          
549          // execute ajax request
550          Alfresco.util.Ajax.request(
551          {
552             url: url,
553             method: "POST",
554             requestContentType : "application/json",
555             responseContentType : "application/json",
556             dataObj:
557             {
558                action : "update"
559             },
560             successMessage: this._msg("message.updateExternal.success"),
561             successCallback:
562             {
563                fn: onUpdatedSuccess,
564                scope: this
565             },
566             failureMessage: this._msg("message.updateExternal.failure"),
567             failureCallback:
568             {
569                fn: function(response) { this._releaseBusy(); },
570                scope: this
571             }
572          });
573       },
574 
575 
576       /**
577        * Unpublishing of an external published blog post implementation
578        * 
579        * @method onUnpublishExternal
580        * @param postId {string} the id of the blog post to update
581        */
582       onUnpublishExternal: function BlogPostView_onUnpublishExternal(postId)
583       {
584          // show busy message
585          if (! this._setBusy(this._msg('message.wait')))
586          {
587             return;
588          }
589          
590          // ajax request success handler
591          var onUnpublishedSuccess = function BlogPostList_onUnpublishedSuccess(response)
592          {
593             // remove busy message
594             this._releaseBusy();
595             
596             // re-render the post
597             this.loadBlogPostDataSuccess(response);
598          };
599           
600          // get the url to call
601          var url = Alfresco.util.blog.generatePublishingRestURL(this.options.siteId, this.options.containerId, postId);
602          
603          // execute ajax request
604          Alfresco.util.Ajax.request(
605          {
606             url: url,
607             method: "POST",
608             requestContentType : "application/json",
609             responseContentType : "application/json",
610             dataObj:
611             {
612                action : "unpublish"
613             },
614             successMessage: this._msg("message.unpublishExternal.success"),
615             successCallback:
616             {
617                fn: onUnpublishedSuccess,
618                scope: this
619             },
620             failureMessage: this._msg("message.unpublishExternal.failure"),
621             failureCallback:
622             {
623                fn: function(response) { this._releaseBusy(); },
624                scope: this
625             }
626          });
627       },
628 
629       
630       // mouse hover functionality
631       
632       /** Called when the mouse enters into a list item. */
633       onPostElementMouseEntered: function BlogPostView_onListElementMouseEntered(layer, args)
634       {
635          // make sure the user sees at least one action, otherwise we won't highlight
636          var permissions = this.blogPostData.permissions;
637          if (!(permissions.edit || permissions["delete"]))
638          {
639             return;
640          }
641           
642          Dom.addClass(args[1].target, 'over');
643       },
644       
645       /** Called whenever the mouse exits a list item. */
646       onPostElementMouseExited: function BlogPostView_onListElementMouseExited(layer, args)
647       {
648          Dom.removeClass(args[1].target, 'over');
649       },
650 
651    
652       /**
653        * PRIVATE FUNCTIONS
654        */
655 
656       /**
657        * Displays the provided busyMessage but only in case
658        * the component isn't busy set.
659        * 
660        * @return true if the busy state was set, false if the component is already busy
661        */
662       _setBusy: function BlogPostList__setBusy(busyMessage)
663       {
664          if (this.busy)
665          {
666             return false;
667          }
668          this.busy = true;
669          this.widgets.busyMessage = Alfresco.util.PopupManager.displayMessage(
670          {
671             text: busyMessage,
672             spanClass: "wait",
673             displayTime: 0
674          });
675          return true;
676       },
677       
678       /**
679        * Removes the busy message and marks the component as non-busy
680        */
681       _releaseBusy: function BlogPostList__releaseBusy()
682       {
683          if (this.busy)
684          {
685             this.widgets.busyMessage.destroy();
686             this.busy = false;
687             return true;
688          }
689          else
690          {
691             return false;
692          }
693       },
694 
695       /**
696        * Gets a custom message
697        *
698        * @method _msg
699        * @param messageId {string} The messageId to retrieve
700        * @return {string} The custom message
701        * @private
702        */
703       _msg: function BlogPostView_msg(messageId)
704       {
705          return Alfresco.util.message.call(this, messageId, "Alfresco.BlogPostView", Array.prototype.slice.call(arguments).slice(1));
706       }
707    };
708 })();
709