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  * BlogPostEdit component.
 22  * 
 23  * Component provides blog post creation/edit functionality.
 24  * 
 25  * @namespace Alfresco
 26  * @class Alfresco.BlogPostEdit
 27  */
 28 (function()
 29 {
 30    /**
 31     * YUI Library aliases
 32     */
 33    var Dom = YAHOO.util.Dom;
 34 
 35    /**
 36     * BlogPostEdit constructor.
 37     * 
 38     * @param {String} htmlId The HTML id of the parent element
 39     * @return {Alfresco.BlogPostEdit} The new Post instance
 40     * @constructor
 41     */
 42    Alfresco.BlogPostEdit = function(htmlId)
 43    {
 44       Alfresco.BlogPostEdit.superclass.constructor.call(this, "Alfresco.BlogPostEdit", htmlId, ["button", "menu", "json"]);
 45       
 46       // Initialise prototype properties
 47       this.blogPostData = null;
 48       this.performExternalPublish = false;
 49       
 50       return this;
 51    };
 52 
 53    YAHOO.extend(Alfresco.BlogPostEdit, Alfresco.component.Base,
 54    {
 55       /**
 56        * Object container for initialization options
 57        *
 58        * @property options
 59        * @type object
 60        */
 61       options:
 62       {
 63          /**
 64           * Current siteId.
 65           * 
 66           * @property siteId
 67           * @type string
 68           */
 69          siteId: "",
 70          
 71          /**
 72           * ContainerId representing root container
 73           *
 74           * @property containerId
 75           * @type string
 76           * @default "blog"
 77           */
 78          containerId: "blog",
 79          
 80          /**
 81           * True if the component should be in edit mode.
 82           *
 83           * @property editMode
 84           * @type boolean
 85           * @default: false
 86           */
 87          editMode: false,
 88          
 89          /**
 90           * Id of the post to edit. Only relevant if editMode is true
 91           *
 92           * @property postId
 93           * @type string
 94           * @default: ""
 95           */
 96          postId: ""
 97       },
 98 
 99       /**
100        * Stores the data of the currently edited blog post
101        */
102       blogPostData: null,
103         
104       /**
105        * If true, an external publish/update will be executed after the post has been
106        * saved/updated.
107        */
108       performExternalPublish: null,
109       
110       /**
111        * Fired by YUI when parent element is available for scripting.
112        * Component initialisation, including instantiation of YUI widgets and event listener binding.
113        *
114        * @method onReady
115        */
116       onReady: function BlogPostEdit_onReady()
117       {
118          if (this.options.editMode)
119          {
120             // load the blog post data prior to initializing the form
121             this._loadBlogPostData();
122          }
123          else
124          {
125             // directly initialize the form
126             this._initializeBlogPostForm();
127          }
128       },
129 
130       /**
131        * Loads the comments for the provided nodeRef and refreshes the ui
132        *
133        * @method _loadBlogPostData
134        * @private
135        */
136       _loadBlogPostData: function BlogPostEdit__loadBlogPostData()
137       {
138          // ajax request success handler
139          var me = this;
140          var loadBlogPostDataSuccess = function BlogPostEdit__loadBlogPostData(response)
141          {
142             // set the blog data
143             var data = response.json.item;
144             me.blogPostData = data;
145             
146             // now initialize the form, which will use the data we just loaded
147             me._initializeBlogPostForm();
148          };
149          
150          // construct the request url
151          var url = YAHOO.lang.substitute(Alfresco.constants.URL_SERVICECONTEXT + "components/blog/post/site/{site}/{container}/{postId}",
152          {
153             site : this.options.siteId,
154             container: this.options.containerId,
155             postId: this.options.postId
156          });
157          
158          // execute ajax request
159          Alfresco.util.Ajax.request(
160          {
161             url: url,
162             method: "GET",
163             responseContentType: "application/json",
164             successCallback:
165             {
166                fn: loadBlogPostDataSuccess,
167                scope: this
168             },
169             failureMessage: this.msg("message.loadpostdata.failure")
170          });
171       },
172 
173       /**
174        * Initializes the blog post form with create/edit dependent data.
175        *
176        * @method _initializeBlogPostForm
177        * @private
178        */
179       _initializeBlogPostForm: function BlogPostEdit__initializeBlogPostForm()
180       {
181          // construct the actionUrl, which is different for creating/updating a post
182          var actionUrl, draft = true, title = "", content = "";
183          if (this.options.editMode)
184          {
185             actionUrl = YAHOO.lang.substitute(Alfresco.constants.PROXY_URI + "api/blog/post/node/{nodeRef}",
186             {
187                nodeRef: this.blogPostData.nodeRef.replace(":/", "")
188             });
189          }
190          else
191          {
192             actionUrl = YAHOO.lang.substitute(Alfresco.constants.PROXY_URI + "api/blog/site/{site}/{container}/posts",
193             {
194                site: this.options.siteId,
195                container: this.options.containerId
196             });
197          }         
198          Dom.get(this.id + "-form").setAttribute("action", actionUrl);
199 
200          // site and container
201          Dom.get(this.id + "-site").setAttribute("value", this.options.siteId);
202          Dom.get(this.id + "-container").setAttribute("value", this.options.containerId);
203                   
204          // draft
205          if (this.options.editMode)
206          {
207             draft = this.blogPostData.isDraft;
208          }
209          Dom.get(this.id + "-draft").setAttribute("value", draft);
210          
211          // title
212          if (this.options.editMode)
213          {
214             title = this.blogPostData.title;
215          }
216          Dom.get(this.id + "-title").setAttribute("value", title);
217          
218          // content
219          if (this.options.editMode)
220          {
221             content = this.blogPostData.content;
222          }
223          Dom.get(this.id + "-content").value = content;
224          
225          // register the behaviour with the form and display the form
226          this._registerBlogPostForm();
227       },
228 
229       /**
230        * Registers the form logic
231        *
232        * @method _registerBlogPostForm
233        * @private
234        */
235       _registerBlogPostForm: function BlogPostEdit__registerBlogPostForm()
236       {
237          // initialize the tag library
238          this.modules.tagLibrary = new Alfresco.module.TagLibrary(this.id);
239          this.modules.tagLibrary.setOptions(
240          {
241             siteId: this.options.siteId
242          });
243          
244          // add the tags that are already set on the post
245          if (this.options.editMode && this.blogPostData.tags.length > 0)
246          {
247             this.modules.tagLibrary.setTags(this.blogPostData.tags);
248          }
249          
250          // create the Button
251          this.widgets.saveButton = new YAHOO.widget.Button(this.id + "-save-button",
252          {
253             type: "submit",
254             label: this.msg(this.options.editMode ? "action.update" : "action.saveAsDraft")
255          });
256 
257          // publishing of a draft post button - only visible if post is a draft
258          if (!this.options.editMode || this.blogPostData.isDraft)
259          {
260             this.widgets.publishButton = Alfresco.util.createYUIButton(this, "publish-button", this.onFormPublishButtonClick);
261             Dom.removeClass(this.id + "-publish-button", "hidden");
262          }
263          
264          // publishing internal and external button / update internal and publish external
265          var publishExternalButtonLabel = "";
266          if (!this.options.editMode)
267          {
268             publishExternalButtonLabel = this.msg("action.publishIntAndExt");
269          }
270          else if (this.blogPostData.isPublished)
271          {
272             publishExternalButtonLabel = this.msg("action.updateIntAndExt");
273          }
274          else
275          {
276             publishExternalButtonLabel = this.msg("action.updateIntAndPublishExt");
277          }
278          this.widgets.publishExternalButton = Alfresco.util.createYUIButton(this, "publishexternal-button", this.onFormPublishExternalButtonClick,
279          {
280             label: publishExternalButtonLabel
281          });
282 
283          // Cancel button
284          this.widgets.cancelButton = Alfresco.util.createYUIButton(this, "cancel-button", this.onFormCancelButtonClick);
285 
286          // Instantiate the simple editor we use for the form
287          this.widgets.editor = new Alfresco.util.RichEditor(Alfresco.constants.HTML_EDITOR, this.id + "-content", this.options.editorConfig);
288          this.widgets.editor.addPageUnloadBehaviour(this.msg("message.unsavedChanges.blog"));
289          this.widgets.editor.render();
290 
291          // Add validation to the rich text editor
292          this.widgets.validateOnZero = 0;
293          var keyUpIdentifier = (Alfresco.constants.HTML_EDITOR === "YAHOO.widget.SimpleEditor") ? "editorKeyUp" : "onKeyUp";         
294          this.widgets.editor.subscribe(keyUpIdentifier, function (e)
295          {
296             this.widgets.validateOnZero++;
297             YAHOO.lang.later(1000, this, this.validateAfterEditorChange);
298          }, this, true);
299 
300          // Create the form that does the validation/submit
301          this.widgets.postForm = new Alfresco.forms.Form(this.id + "-form");
302 
303          // Title is mandatory
304          this.widgets.postForm.addValidation(this.id + "-title", Alfresco.forms.validation.mandatory, null, "blur");
305          this.widgets.postForm.addValidation(this.id + "-title", Alfresco.forms.validation.length,
306          {
307             max: 256,
308             crop: true
309          }, "keyup");
310 
311          // Text is mandatory
312          this.widgets.postForm.addValidation(this.id + "-content", Alfresco.forms.validation.mandatory, null);
313 
314          this.widgets.postForm.setShowSubmitStateDynamically(true, false);
315          if (this.widgets.publishButton)
316          {
317             this.widgets.postForm.setSubmitElements([this.widgets.saveButton, this.widgets.publishExternalButton, this.widgets.publishButton]);            
318          }
319          else
320          {
321             this.widgets.postForm.setSubmitElements([this.widgets.saveButton, this.widgets.publishExternalButton]);
322          }
323          this.widgets.postForm.setAJAXSubmit(true,
324          {
325             successCallback:
326             {
327                fn: this.onFormSubmitSuccess,
328                scope: this
329             },
330             failureMessage: this.msg("message.savepost.failure"),
331             failureCallback:
332             {
333                fn: this.onFormSubmitFailure,
334                scope: this
335             }
336          });
337          if (this.options.editMode)
338          {
339              this.widgets.postForm.setAjaxSubmitMethod(Alfresco.util.Ajax.PUT);
340          }
341          this.widgets.postForm.setSubmitAsJSON(true);
342          this.widgets.postForm.doBeforeFormSubmit =
343          {
344             fn: function(form, obj)
345             {
346                //Put the HTML back into the text area
347                this.widgets.editor.save();
348 
349                // Make sure the user has written a text
350                if (Dom.get(this.id + "-content").value.length === 0)
351                {
352                   Alfresco.util.PopupManager.displayMessage(
353                   {
354                      text: Alfresco.util.message("message.noText", this.name)
355                   });
356                   return;
357                }
358 
359                 // disable ui elements
360                this.widgets.saveButton.set("disabled", true);
361                if (this.widgets.publishButton)
362                {
363                   this.widgets.publishButton.set("disabled", true);
364                }
365                this.widgets.publishExternalButton.set("disabled", true);
366                this.widgets.cancelButton.set("disabled", true);
367 
368                // update the tags set in the form
369                this.modules.tagLibrary.updateForm(this.id + "-form", "tags");
370                
371                // show a wait message
372                this.widgets.feedbackMessage = Alfresco.util.PopupManager.displayMessage(
373                {
374                   text: Alfresco.util.message(this.msg("message.submitting")),
375                   spanClass: "wait",
376                   displayTime: 0
377                });
378             },
379             scope: this
380          };
381          this.modules.tagLibrary.initialize(this.widgets.postForm);
382          this.widgets.postForm.init();
383          
384          // finally display the form
385          Dom.removeClass(this.id + "-div", "hidden");
386          Dom.get(this.id + "-title").focus();
387       },
388 
389       /**
390        * Called when a key was pressed in the rich text editor.
391        * Will trigger form validation after the last key stroke after a seconds pause.
392        *
393        * @method validateAfterEditorChange
394        */
395       validateAfterEditorChange: function BlogPostEdit_validateAfterEditorChange()
396       {
397          this.widgets.validateOnZero--;
398          if (this.widgets.validateOnZero === 0)
399          {
400             var oldLength = Dom.get(this.id + "-content").value.length;
401             this.widgets.editor.save();
402             var newLength = Dom.get(this.id + "-content").value.length;
403             if ((oldLength === 0 && newLength !== 0) || (oldLength > 0 && newLength === 0))
404             {
405                this.widgets.postForm.updateSubmitElements();
406             }
407          }
408       },
409 
410       /**
411        * Publish button click handler
412        *
413        * @method onFormPublishButtonClick
414        */
415       onFormPublishButtonClick: function BlogPostEdit_onFormPublishButtonClick(type, args)
416       {
417          // make sure we set the draft flag to false
418          Dom.get(this.id + "-draft").setAttribute("value", false);
419           
420          // submit the form
421          this.widgets.saveButton.fireEvent("click",
422          {
423             type: "click"
424          });
425       },
426       
427       /**
428        * Publish external button click handler
429        *
430        * @method onFormPublishExternalButtonClick
431        */
432       onFormPublishExternalButtonClick: function BlogPostEdit_onFormPublishExternalButtonClick(type, args)
433       {
434          // make sure we set the draft flag to false
435          Dom.get(this.id + "-draft").setAttribute("value", false);
436           
437          // make sure that the post gets also externally published
438          this.performExternalPublish = true;
439           
440          // submit the form
441          this.widgets.saveButton.fireEvent("click",
442          {
443             type: "click"
444          });
445       },
446       
447       /**
448        * Cancel button click handler
449        *
450        * @method onFormCancelButtonClick
451        */
452       onFormCancelButtonClick: function BlogPostEdit_onFormCancelButtonClick(type, args)
453       {
454          // redirect to the page we came from
455          history.go(-1);
456       },
457       
458       /**
459        * Form submit success handler
460        *
461        * @method onFormSubmitSuccess
462        */
463       onFormSubmitSuccess: function BlogPostEdit_onFormSubmitSuccess(response)
464       {
465          // hide the wait message
466          this.widgets.feedbackMessage.destroy();
467          
468          // check whether we have to do an external publich
469          if (this.performExternalPublish)
470          {
471             // show a new wait message
472             this.widgets.feedbackMessage = Alfresco.util.PopupManager.displayMessage(
473             {
474                text: Alfresco.util.message(this.msg("message.postSavedNowPublish")),
475                spanClass: "wait",
476                displayTime: 0
477             });
478              
479             //var nodeRef = response.json.item.nodeRef;    
480             var postId = response.json.item.name;
481             if (response.json.item.isPublished)
482             {
483                // perform an update
484                this.onUpdateExternal(postId);
485             }
486             else
487             {
488                // perform a publish
489                this.onPublishExternal(postId);
490             }
491          }
492          else
493          {
494             Alfresco.util.PopupManager.displayMessage(
495             {
496                text: this.msg("message.savepost.success")
497             });
498             this._loadPostViewPage(response.json.item.name);
499          }
500       },
501 
502       /**
503        * Reenables the inputs which got disabled as part of a comment submit
504        *
505        * @method onFormSubmitFailure
506        */
507       onFormSubmitFailure: function BlogPostEdit_onFormSubmitFailure()
508       {
509          // enable the buttons
510          this.widgets.saveButton.set("disabled", false);
511          if (this.widgets.publishButton)
512          {
513             this.widgets.publishButton.set("disabled", false);
514          }
515          this.widgets.publishExternalButton.set("disabled", false);
516          this.widgets.cancelButton.set("disabled", false);
517          
518          // hide the wait message
519          this.widgets.feedbackMessage.destroy();
520       },
521 
522       /**
523        * Publishes the blog post to an external blog.
524        *
525        * @method onPublishExternal
526        */
527       onPublishExternal: function BlogPostEdit_onPublishExternal(postId)
528       {
529          // publish request success handler
530          var onPublished = function BlogPostEdit_onPublished(response)
531          {
532             this._loadPostViewPage(postId);
533          };
534          
535          // publish request failure handler
536          var onPublishFailed = function BlogPostEdit_onPublishFailed(response)
537          {
538             // let the user know that the publish failed, then redirect to the view page
539             this.widgets.feedbackMessage.destroy();
540             var me = this;
541             Alfresco.util.PopupManager.displayPrompt(
542             {
543                text: this.msg("message.publishExternal.failure"),
544                buttons: [
545                {
546                   text: this.msg("button.ok"),
547                   handler: function()
548                   {
549                      me._loadPostViewPage(postId);
550                   },
551                   isDefault: true
552                }]
553             });
554             
555          };
556                   
557          // get the url to call
558          var url = Alfresco.util.blog.generatePublishingRestURL(this.options.siteId, this.options.containerId, postId);
559          
560          // execute ajax request
561          Alfresco.util.Ajax.request(
562          {
563             url: url,
564             method: "POST",
565             requestContentType : "application/json",
566             responseContentType : "application/json",
567             dataObj:
568             {
569                action : "publish"
570             },
571             successMessage: this.msg("message.publishExternal.success"),
572             successCallback:
573             {
574                fn: onPublished,
575                scope: this
576             },
577             failureCallback:
578             {
579                fn: onPublishFailed,
580                scope: this
581             }
582          });
583       },
584       
585       /**
586        * Updates the external published blog post.
587        *
588        * @method onUpdateExternal
589        */
590       onUpdateExternal: function BlogPostEdit_onUpdateExternal(postId)
591       {
592          // update request success handler
593          var onUpdated = function BlogPostEdit_onUpdated(response)
594          {
595             this._loadPostViewPage(postId);
596          };
597          
598          // update request failure handler
599          var onUpdateFailed = function BlogPostEdit_onUpdateFailed(response)
600          {
601             // let the user know that the publish failed, then redirect to the view page
602             this.widgets.feedbackMessage.destroy();
603             var me = this;
604             Alfresco.util.PopupManager.displayPrompt(
605             {
606                text: this.msg("message.updateExternal.failure"),
607                buttons: [
608                {
609                   text: this.msg("button.ok"),
610                   handler: function()
611                   {
612                      me._loadPostViewPage(postId);
613                   },
614                   isDefault: true
615                }]
616             });
617             
618          };
619          
620          // get the url to call
621          var url = Alfresco.util.blog.generatePublishingRestURL(this.options.siteId, this.options.containerId, postId);
622          
623          // execute ajax request
624          Alfresco.util.Ajax.request(
625          {
626             url: url,
627             method: "POST",
628             requestContentType : "application/json",
629             responseContentType : "application/json",
630             dataObj:
631             {
632                action : "update"
633             },
634             successMessage: this.msg("message.updateExternal.success"),
635             successCallback:
636             {
637                fn: onUpdated,
638                scope: this
639             },
640             failureCallback:
641             {
642                fn: onUpdateFailed,
643                scope: this
644             }
645          });
646       },
647       
648       /**
649        * PRIVATE FUNCTIONS
650        */
651           
652       /**
653        * Loads the blog post view page
654        *
655        * @method _loadPostViewPage
656        */
657       _loadPostViewPage: function BlogPostEdit__loadPostViewPage(postId)
658       {
659          window.location = Alfresco.util.blog.generateBlogPostViewUrl(this.options.siteId, this.options.containerId, postId);
660       }
661    });
662 })();
663