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  * Content control component.
 22  * 
 23  * This component renders an editor appropriate for the the mimetype
 24  * of the content.
 25  * 
 26  * Plain text content is rendered in a textarea.
 27  * Rich text content is rendered in a TinyMCE editor.
 28  * Images are displayed with a file upload control.
 29  * 
 30  * @namespace Alfresco
 31  * @class Alfresco.ContentControl
 32  */
 33 (function()
 34 {
 35    /**
 36     * YUI Library aliases
 37     */
 38    var Dom = YAHOO.util.Dom;
 39 
 40    /**
 41     * ContentControl constructor.
 42     * 
 43     * @param {String} htmlId The HTML id of the parent element
 44     * @return {Alfresco.TextControl} The new ContentControl instance
 45     * @constructor
 46     */
 47    Alfresco.ContentControl = function(htmlId)
 48    {
 49       return Alfresco.ContentControl.superclass.constructor.call(this, htmlId, "Alfresco.ContentControl");
 50    };
 51    
 52    YAHOO.extend(Alfresco.ContentControl, Alfresco.RichTextControl,
 53    {
 54       /**
 55        * Object container for initialization options
 56        *
 57        * @property options
 58        * @type object
 59        */
 60       options:
 61       {
 62          /**
 63           * Current Form Mode: edit or create
 64           * 
 65           * @property formMode
 66           * @type string
 67           */
 68          formMode: "edit",
 69          
 70          /**
 71           * NodeRef of the item the form is for
 72           * 
 73           * @property nodeRef
 74           * @type string
 75           */
 76          nodeRef: null,
 77          
 78          /**
 79           * The mimetype of the content being created
 80           * 
 81           * @property mimeType
 82           * @type string
 83           */
 84          mimeType: null,
 85          
 86          /**
 87           * Comma separated list of mime types that will be shown
 88           * in a textarea
 89           * 
 90           * @property plainMimeTypes
 91           * @type string
 92           */
 93          plainMimeTypes: "text/plain,text/xml",
 94          
 95          /**
 96           * Comma separated list of mime types that will be shown
 97           * in the TinyMCE editor
 98           * 
 99           * @property richMimeTypes
100           * @type string
101           */
102          richMimeTypes: "text/html,text/xhtml",
103          
104          /**
105           * Comma separated list of mime types that will be shown
106           * using the img tag and allow upload of new versions
107           * 
108           * @property imageMimeTypes
109           * @type string
110           */
111          imageMimeTypes: "image/jpeg,image/jpg,image/png",
112          
113          /**
114           * Whether the (plain text) editor should be forced visible, e.g. mimetype unrecognized
115           * 
116           * @property forceEditor
117           * @type boolean
118           */
119          forceEditor: false,
120 
121          /**
122           * Whether (empty) content should be created when editor is hidden, e.g. mimetype unrecognized.
123           * Note: Only relevant when forceEditor = false
124           * 
125           * @property forceContent
126           * @type boolean
127           */
128          forceContent: false
129       },
130 
131       /**
132        * Fired by YUI when parent element is available for scripting.
133        * Component initialisation, including instantiation of YUI widgets and event listener binding.
134        *
135        * @method onReady
136        */
137       onReady: function ContentControl_onReady()
138       {
139          if (Alfresco.logger.isDebugEnabled())
140          {
141             Alfresco.logger.debug("Rendering content control for element '" + this.id + "', value = '" + this.options.currentValue + 
142                   "', nodeRef = '" + this.options.nodeRef + "', mimetype = '" + this.options.mimeType + "'");
143             Alfresco.logger.debug("Configured plain mimetypes for element '" + this.id + "': " + this.options.plainMimeTypes);
144             Alfresco.logger.debug("Configured rich mimetypes for element '" + this.id + "': " + this.options.richMimeTypes);
145             Alfresco.logger.debug("Configured image mimetypes for element '" + this.id + "': " + this.options.imageMimeTypes);
146             Alfresco.logger.debug("Editor parameters for element '" + this.id + "': " + 
147                   YAHOO.lang.dump(this.options.editorParameters));
148          }
149          
150          // get the mimetype of the content
151          var contentMimetype = this._determineMimeType();
152             
153          if (contentMimetype !== null)
154          {
155             if (this._isRichMimeType(contentMimetype))
156             {
157                if (this.options.formMode === "create")
158                {
159                   // in create mode render the editor immediately
160                   if (!this.options.disabled)
161                   {
162                      this._renderEditor();
163                   }
164                }
165                else
166                {
167                   // populate the textarea with the content and
168                   // once that is complete call the provided
169                   // callback function to render the TinyMCE
170                   // editor (when it's not disabled)
171                   this._populateContent(
172                   {
173                      successCallback: 
174                      {
175                         fn: function()
176                         {
177                            if (!this.options.disabled)
178                            {
179                               this._renderEditor();
180                            }
181                         },
182                         scope: this
183                      }
184                   });
185                }
186             }
187             else if (this._isPlainMimeType(contentMimetype) || this.options.forceEditor)
188             {
189                // populate the textarea with the content
190                this._populateContent();
191             }
192             else if (this._isImageMimeType(contentMimetype))
193             {
194                this._hideField();
195                
196                if (Alfresco.logger.isDebugEnabled())
197                   Alfresco.logger.debug("Hidden field '" + this.id + "' as support for images is not completed yet");
198                
199                // TODO: remove textarea from DOM
200                //       add <img> to field DOM programatically
201                //       add <input type="file" /> to DOM programatically
202                //       make the image height and width configurable
203                //       generate URL to the image using the nodeRef (cmis content webscript?)
204                //       investigate whether the picked image can be shown 
205             }
206             else
207             {
208                this._hideField();
209                
210                if (Alfresco.logger.isDebugEnabled())
211                   Alfresco.logger.debug("Hidden field '" + this.id + "' as the content for the mimetype can not be displayed");
212             }
213          }
214          else
215          {
216             this._hideField();
217             
218             if (Alfresco.logger.isDebugEnabled())
219                Alfresco.logger.debug("Hidden field '" + this.id + "' as the mimetype is unknown");
220          }
221       },
222       
223       /**
224        * Retrieves and populates the content for the current control
225        * 
226        * @method _populateContent
227        * @param callback Optional object containing a callback function
228        * @private
229        */
230       _populateContent: function ContentControl__populateContent(callback)
231       {
232          if (this.options.nodeRef !== null && this.options.nodeRef.length > 0)
233          {
234             if (Alfresco.logger.isDebugEnabled())
235                Alfresco.logger.debug("Retrieving content for field '" + this.id + "' using nodeRef: " + this.options.nodeRef);
236             
237             // success handler, show the content
238             var onSuccess = function ContentControl_populateContent_onSuccess(response)
239             {
240                Dom.get(this.id).value = response.serverResponse.responseText;
241                
242                // if a callback was provided, execute it
243                if (callback && callback.successCallback)
244                {
245                   if (Alfresco.logger.isDebugEnabled())
246                      Alfresco.logger.debug("calling callback");
247                   
248                   callback.successCallback.fn.call(callback.successCallback.scope, response);
249                }
250             };
251             
252             // failure handler, display alert
253             var onFailure = function ContentControl_populateContent_onFailure(response)
254             {
255                // hide the whole field so incorrect content does not get re-submitted
256                this._hideField();
257                
258                if (Alfresco.logger.isDebugEnabled())
259                   Alfresco.logger.debug("Hidden field '" + this.id + "' as content retrieval failed");
260             };
261             
262             // attempt to retrieve content
263             var nodeRefUrl = this.options.nodeRef.replace("://", "/");
264             Alfresco.util.Ajax.request(
265             {
266                url: Alfresco.constants.PROXY_URI + "api/node/content/" + nodeRefUrl,
267                method: "GET",
268                successCallback:
269                {
270                   fn: onSuccess,
271                   scope: this
272                },
273                failureCallback:
274                {
275                   fn: onFailure,
276                   scope: this
277                }
278             });
279          }
280          else if (this.options.formMode !== "create")
281          {
282             this._hideField();
283             
284             if (Alfresco.logger.isDebugEnabled())
285                Alfresco.logger.debug("Hidden field '" + this.id + "' as the nodeRef parameter is missing");
286          }
287       },
288       
289       /**
290        * Returns the mimetype for the content property.
291        * 
292        * Returns null if the field is not a content property.
293        * 
294        * If a mimetype can not be determined from the content url of the property
295        * the mimeType parameter is examined, if that is empty or null, null is returned.
296        * 
297        * @method _determineMimeType
298        * @return the mimetype or null if it can not be determined
299        */
300       _determineMimeType: function ContentControl__determineMimeType()
301       {
302          var result = null;
303          
304          if (this.options.currentValue.indexOf("contentUrl=") === 0 &&
305              this.options.currentValue.indexOf("mimetype=") !== -1)
306          {
307             // extract the mimetype from the content url
308             var mtBegIdx = this.options.currentValue.indexOf("mimetype=") + 9,
309                mtEndIdx = this.options.currentValue.indexOf("|", mtBegIdx);
310             result = this.options.currentValue.substring(mtBegIdx, mtEndIdx);
311          }
312          
313          // if the content url did not contain the mimetype examine
314          // the mimeType parameter
315          if (this.options.mimeType !== null && this.options.mimeType.length > 0)
316          {
317             result = this.options.mimeType;
318          }
319          
320          if (Alfresco.logger.isDebugEnabled())
321             Alfresco.logger.debug("Determined mimetype: " + result);
322          
323          return result;
324       },
325       
326       /**
327        * Determines whether the given mimetype is a configured 'rich' mimetype.
328        * 
329        * @method _isRichMimeType
330        * @param mimetype {string} The mimetype to check
331        * @return true if the given mimetype is a 'rich' mimetype
332        */
333       _isRichMimeType: function ContentControl__isRichMimeType(mimetype)
334       {
335          var result = false;
336          
337          if (this.options.richMimeTypes !== null && this.options.richMimeTypes.length > 0 &&
338              this.options.richMimeTypes.indexOf(mimetype) != -1)
339          {
340             result = true;
341          }
342          
343          if (Alfresco.logger.isDebugEnabled())
344             Alfresco.logger.debug("Testing whether '" + mimetype + "' is a configured rich mimetype: " + result);
345          
346          return result;
347       },
348       
349       /**
350        * Determines whether the given mimetype is a configured 'plain' mimetype.
351        * 
352        * @method _isPlainMimeType
353        * @param mimetype {string} The mimetype to check
354        * @return true if the given mimetype is a 'plain' mimetype
355        */
356       _isPlainMimeType: function ContentControl__isPlainMimeType(mimetype)
357       {
358          var result = false;
359          
360          if (this.options.plainMimeTypes !== null && this.options.plainMimeTypes.length > 0 &&
361              this.options.plainMimeTypes.indexOf(mimetype) != -1)
362          {
363             result = true;
364          }
365          
366          if (Alfresco.logger.isDebugEnabled())
367             Alfresco.logger.debug("Testing whether '" + mimetype + "' is a configured plain mimetype: " + result);
368          
369          return result;
370       },
371       
372       /**
373        * Determines whether the given mimetype is a configured 'image' mimetype.
374        * 
375        * @method _isImageMimeType
376        * @param mimetype {string} The mimetype to check
377        * @return true if the given mimetype is an 'image' mimetype
378        */
379       _isImageMimeType: function ContentControl__isImageMimeType(mimetype)
380       {
381          var result = false;
382          
383          if (this.options.imageMimeTypes !== null && this.options.imageMimeTypes.length > 0 &&
384              this.options.imageMimeTypes.indexOf(mimetype) != -1)
385          {
386             result = true;
387          }
388          
389          if (Alfresco.logger.isDebugEnabled())
390             Alfresco.logger.debug("Testing whether '" + mimetype + "' is a configured image mimetype: " + result);
391          
392          return result;
393       },
394       
395       /**
396        * Hides the field, used when a content property can not be shown.
397        * 
398        * @method _hideField
399        * @private
400        */
401       _hideField: function ContentControl__hideField()
402       {
403          if (!this.options.forceContent)
404          {
405             // change the name of the textarea so it is not submitted as new content!
406             Dom.get(this.id).name = "-";
407          }
408          
409          // hide the whole field
410          Dom.get(this.id + "-field").style.display = "none";
411       }
412    });
413 })();
414