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