Switch IMP to use the new CKEditor driver.
authorMichael M Slusarz <slusarz@curecanti.org>
Sat, 24 Oct 2009 23:55:33 +0000 (17:55 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Sun, 25 Oct 2009 02:33:59 +0000 (20:33 -0600)
imp/compose.php
imp/config/prefs.php.dist
imp/docs/CHANGES
imp/docs/UPGRADING
imp/js/compose-dimp.js
imp/js/compose.js
imp/lib/Application.php
imp/lib/Auth.php
imp/lib/UI/Compose.php
imp/lib/Views/Compose.php

index 66824d5..b29a21f 100644 (file)
@@ -1223,6 +1223,11 @@ if ($redirect) {
     $template_output = $t->fetch(IMP_TEMPLATES . '/compose/compose.html');
 }
 
+if ($rtemode && !$redirect) {
+    $imp_ui->initRTE();
+    Horde::addInlineScript('CKEDITOR.replace("composeMessage", IMP.ckeditor_config)', 'load');
+}
+
 if ($showmenu) {
     IMP::prepareMenu();
 }
@@ -1233,7 +1238,4 @@ if ($showmenu) {
     IMP::menu();
 }
 echo $template_output;
-if ($rtemode && !$redirect) {
-    echo $imp_ui->initRTE();
-}
 require $registry->get('templates', 'horde') . '/common-footer.inc';
index a36e21d..ff09619 100644 (file)
@@ -68,7 +68,7 @@ $prefGroups['compose'] = array(
     'members' => array('stationery_link', 'mailto_handler', 'compose_cc',
                        'compose_bcc', 'compose_spellcheck', 'compose_confirm',
                        'set_priority', 'compose_popup', 'compose_html',
-                       'fckeditor_buttons', 'mail_domain',
+                       'ckeditor_buttons', 'mail_domain',
                        'compose_cursor', 'sending_charset', 'encryptselect',
                        'save_attachments')
 );
@@ -624,12 +624,13 @@ $_prefs['compose_html'] = array(
     'desc' => _("Compose messages with an HTML GUI by default (if browser supports the feature)?")
 );
 
-// The list of buttons to show in FCKeditor
-// See http://www.fckeditor.net/ for details on configuration
-$_prefs['fckeditor_buttons'] = array(
-    'value' => "[['Source','FitWindow','-','Templates'],['Cut','Copy','Paste','PasteText','PasteWord'],['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],'/',['OrderedList','UnorderedList','-','Outdent','Indent','Blockquote'],['JustifyLeft','JustifyCenter','JustifyRight','JustifyFull'],['Link','Unlink'],['Image','Flash','Table','Rule','Smiley','SpecialChar'],'/',['Bold','Italic','Underline','StrikeThrough','-','Subscript','Superscript'],['TextColor','BGColor'],'/',['Style','FontFormat','FontName','FontSize']]",
+// The list of buttons to show in CKeditor
+// See http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Toolbar for
+// details on configuration
+$_prefs['ckeditor_buttons'] = array(
+    'value' => "[['Source','Maximize','-','Templates'],['Cut','Copy','Paste','PasteText','PasteFromWord'],['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],'/',['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'],['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],['Link','Unlink'],['Image','Flash','Table','HorizontalRule','Smiley','SpecialChar'],'/',['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],['TextColor','BGColor'],'/',['Styles','Format','Font','FontSize']]",
     // Use the following line for a very basic set of buttons:
-    // 'value' => "['Bold','Italic','-','OrderedList','UnorderedList','-','Link','Unlink']",
+    // 'value' => "['Bold','Italic','-','NumberedList','BulletedList','-','Link','Unlink']",
     // Locked by default
     'locked' => true,
     'shared' => false,
index 004b95b..f4719a0 100644 (file)
@@ -2,6 +2,7 @@
 v5.0-git
 --------
 
+[mms] Upgrade RTE to CKEditor v3.
 [mms] Sort by display name for to/from fields if supported on IMAP server.
 [mms] Add ability to quickly filter by flags in DIMP.
 [mms] Add ability to select sent-mail mailbox when composing in DIMP.
index 88c3e83..8743542 100644 (file)
@@ -18,8 +18,11 @@ This is a non-exhaustive, quick explanation of what has changed between an IMP
 HTML GUI editor
 ---------------
 
-IMP only supports the FCKeditor javscript HTML GUI editor.  Xinha is no longer
-supported.
+IMP only supports the CKeditor javascript HTML rich text editor.  Xinha is no
+longer supported.
+
+Button configuration is now stored in the prefs value 'ckeditor_buttons'. The
+old 'fckeditor_buttons' setting is no longer used and can be safely removed.
 
 
 Server Options
index dc6ad60..9a972de 100644 (file)
@@ -11,10 +11,9 @@ var DimpCompose = {
     // Variables defaulting to empty/false:
     //   auto_save_interval, button_pressed, compose_cursor, dbtext,
     //   drafts_mbox, editor_on, is_popup, knl, mp_padding, resizebcc,
-    //   resizecc, resizeto, row_height, sbtext, skip_spellcheck, spellcheck,
-    //   uploading
+    //   resizecc, resizeto, row_height, rte, sbtext, skip_spellcheck,
+    //   spellcheck, uploading
     last_msg: '',
-    textarea_ready: true,
 
     confirmCancel: function()
     {
@@ -84,7 +83,7 @@ var DimpCompose = {
 
         // Finally try and replace the signature.
         if (this.editor_on) {
-            msg = FCKeditorAPI.GetInstance('composeMessage').GetHTML().replace(/\r\n/g, '\n');
+            msg = this.rte.getData().replace(/\r\n/g, '\n');
             lastSignature = '<p><!--begin_signature--><!--end_signature--></p>';
             nextSignature = '<p><!--begin_signature-->' + next.sig.replace(/^ ?<br \/>\n/, '').replace(/ +/g, ' ') + '<!--end_signature--></p>';
 
@@ -112,7 +111,7 @@ var DimpCompose = {
 
             msg = msg.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
             if (this.editor_on) {
-                FCKeditorAPI.GetInstance('composeMessage').SetHTML(msg);
+                this.rte.setData(msg);
             } else {
                 msgval.setValue(msg);
             }
@@ -161,10 +160,6 @@ var DimpCompose = {
             DIMP.SpellCheckerObject.isActive()) {
             DIMP.SpellCheckerObject.resume();
             this.skip_spellcheck = true;
-            if (!this.textarea_ready) {
-                this.uniqueSubmit.bind(this, action).defer();
-                return;
-            }
         }
 
         if (action == 'send_message' || action == 'save_draft') {
@@ -220,7 +215,7 @@ var DimpCompose = {
         } else {
             // Move HTML text to textarea field for submission.
             if (this.editor_on) {
-                FCKeditorAPI.GetInstance('composeMessage').UpdateLinkedField();
+                this.rte.updateElement();
             }
 
             // Use an AJAX submit here so that we can do javascript-y stuff
@@ -340,14 +335,14 @@ var DimpCompose = {
             DIMP.SpellCheckerObject.resume();
         }
 
-        var text;
+        var config, text;
 
         if (this.editor_on) {
             this.editor_on = false;
 
-            text = FCKeditorAPI.GetInstance('composeMessage').GetHTML();
+            text = this.rte.getData();
             $('composeMessageParent').childElements().invoke('hide');
-            $('composeMessage').show();
+            $('composeMessage').show().setStyle({ visibility: null }).focus();
 
             DimpCore.doAction('Html2Text', { text: text }, null, this.setMessageText.bind(this), { asynchronous: false });
         } else {
@@ -356,32 +351,39 @@ var DimpCompose = {
                 DimpCore.doAction('Text2Html', { text: $F('composeMessage') }, null, this.setMessageText.bind(this), { asynchronous: false });
             }
 
-            oFCKeditor.Height = this.getMsgAreaHeight();
             // Try to reuse the old fckeditor instance.
             try {
-                FCKeditorAPI.GetInstance('composeMessage').SetHTML($F('composeMessage'));
+                this.rte.setData($F('composeMessage'));
                 $('composeMessageParent').childElements().invoke('show');
                 $('composeMessage').hide();
             } catch (e) {
+                config = Object.clone(IMP.ckeditor_config);
+                if (!config.on) {
+                    config.on = {};
+                }
+                config.on.instanceReady = function(evt) {
+                    this.resizeMsgArea();
+                    this.RTELoading('hide');
+                    this.rte.focus();
+                }.bind(this);
                 this.RTELoading('show');
-                FCKeditor_OnComplete = this.RTELoading.curry('hide');
-                oFCKeditor.ReplaceTextarea();
+                this.rte = CKEDITOR.replace('composeMessage', config);
             }
         }
         $('htmlcheckbox').checked = this.editor_on;
         $('html').setValue(this.editor_on ? 1 : 0);
     },
 
-    RTELoading: function(cmd)
-    {
-        var o, r;
-        if (!$('rteloading')) {
-            r = new Element('DIV', { id: 'rteloading' }).clonePosition($('composeMessageParent'));
-            $(document.body).insert(r);
-            o = r.viewportOffset();
-            $(document.body).insert(new Element('SPAN', { id: 'rteloadingtxt' }).setStyle({ top: (o.top + 15) + 'px', left: (o.left + 15) + 'px' }).insert(DIMP.text.loading));
-        }
-        $('rteloading', 'rteloadingtxt').invoke(cmd);
+     RTELoading: function(cmd)
+     {
+         var o, r;
+         if (!$('rteloading')) {
+             r = new Element('DIV', { id: 'rteloading' }).clonePosition($('composeMessageParent'));
+             $(document.body).insert(r);
+             o = r.viewportOffset();
+             $(document.body).insert(new Element('SPAN', { id: 'rteloadingtxt' }).setStyle({ top: (o.top + 15) + 'px', left: (o.left + 15) + 'px' }).insert(DIMP.text.loading));
+         }
+         $('rteloading', 'rteloadingtxt').invoke(cmd);
     },
 
     getMsgAreaHeight: function()
@@ -411,18 +413,16 @@ var DimpCompose = {
                 return;
             }
             DIMP.SpellCheckerObject.htmlAreaParent = 'composeMessageParent';
-            DIMP.SpellCheckerObject.htmlArea = $('composeMessage').adjacent('iframe[id*=message]').first();
-            $('composeMessage').setValue(FCKeditorAPI.GetInstance('composeMessage').GetHTML());
-            this.textarea_ready = false;
+            this.rte.updateElement();
+            $('composeMessage').next().hide();
         }.bind(this);
         DIMP.SpellCheckerObject.onAfterSpellCheck = function() {
             if (!this.editor_on) {
                 return;
             }
-            DIMP.SpellCheckerObject.htmlArea = DIMP.SpellCheckerObject.htmlAreaParent = null;
-            var ed = FCKeditorAPI.GetInstance('composeMessage');
-            ed.SetHTML($F('composeMessage'));
-            ed.Events.AttachEvent('OnAfterSetHTML', function() { this.textarea_ready = true; }.bind(this));
+            DIMP.SpellCheckerObject.htmlAreaParent = null;
+            this.rte.setData($F('composeMessage'));
+            $('composeMessage').next().show();
         }.bind(this);
     },
 
@@ -449,7 +449,7 @@ var DimpCompose = {
             return;
         }
 
-        var bcc_add, fo,
+        var bcc_add,
             identity = this.get_identity($F('last_identity')),
             msgval = $('composeMessage');
 
@@ -464,7 +464,7 @@ var DimpCompose = {
             !this.auto_save_interval) {
             this.auto_save_interval = new PeriodicalExecuter(function() {
                 var cur_msg = this.editor_on
-                    ? FCKeditorAPI.GetInstance('composeMessage').GetHTML()
+                    ? this.rte.getData()
                     : $F(msgval);
                 cur_msg = cur_msg.replace(/\r/g, '');
                 if (!cur_msg.empty() && this.last_msg != cur_msg) {
@@ -475,9 +475,8 @@ var DimpCompose = {
         }
 
         if (this.editor_on) {
-            fo = FCKeditorAPI.GetInstance('composeMessage');
-            fo.SetHTML(msg);
-            this.last_msg = fo.GetHTML().replace(/\r/g, '');
+            this.rte.setData(msg);
+            this.last_msg = this.rte.getData().replace(/\r/g, '');
         } else {
             msgval.setValue(msg);
             this.setCursorPosition(msgval);
@@ -526,7 +525,7 @@ var DimpCompose = {
     focusEditor: function()
     {
         try {
-            FCKeditorAPI.GetInstance('composeMessage').Focus();
+            this.rte.focus();
         } catch (e) {
             this.focusEditor.bind(this).defer();
         }
@@ -571,12 +570,7 @@ var DimpCompose = {
         }
 
         if (this.editor_on) {
-            m = $('composeMessageParent').select('iframe').last();
-            if (m) {
-                m.setStyle({ height: this.getMsgAreaHeight() + 'px' });
-            } else {
-                this.resizeMsgArea.bind(this).defer();
-            }
+            this.rte.resize('100%', this.getMsgAreaHeight(), false);
             return;
         }
 
@@ -783,7 +777,7 @@ var DimpCompose = {
                 onChoose: this.setSentMailLabel.bind(this)
             });
             this.knl.setSelected(this.get_identity($F('identity'))[3]);
-            $('sent_mail_folder_label').insert({ after: new Element('SPAN', { className: 'popdownImg', id: 'compose_flist_popdown' }).observe('click', function(e) { this.knl.show(); e.stop(); }.bindAsEventListener(this)) });
+            $('sent_mail_folder_label').insert({ after: new Element('SPAN', { className: 'popdownImg', id: 'compose_flist_popdown' }).observe('click', function(e) { this.knl.show(); this.knl.ignoreClick(e); e.stop(); }.bindAsEventListener(this)) });
         }
 
         $('dimpLoading').hide();
@@ -795,7 +789,6 @@ var DimpCompose = {
         if (Prototype.Browser.WebKit) {
             $('submit_frame').writeAttribute({ position: 'absolute', width: '1px', height: '1px' }).setStyle({ left: '-999px' }).show();
         }
-
     }
 
 },
index 28ddccb..c8a1339 100644 (file)
@@ -6,11 +6,10 @@
  */
 
 var ImpCompose = {
-    /* Variables defined in compose.php:
-     *   cancel_url, spellcheck, cursor_pos, identities, max_attachments,
-     *   popup, redirect, reloaded, rtemode, smf_check, skip_spellcheck */
+    // Variables defined in compose.php:
+    //   cancel_url, spellcheck, cursor_pos, identities, max_attachments,
+    //   popup, redirect, reloaded, rtemode, smf_check, skip_spellcheck
     display_unload_warning: true,
-    textarea_ready: true,
 
     confirmCancel: function(e)
     {
@@ -62,14 +61,12 @@ var ImpCompose = {
             bcc = $('bcc'),
             save = $('ssm'),
             smf = $('sent_mail_folder'),
-            ed, lastSignature, msg, nextSignature, pos, re;
+            lastSignature, msg, nextSignature, pos, re;
 
         // If the rich text editor is on, we'll use a regexp to find the
         // signature comment and replace its contents.
         if (this.rtemode) {
-            ed = FCKeditorAPI.GetInstance('composeMessage');
-
-            msg = ed.GetHTML.replace(/\r\n/g, '\n');
+            msg = CKEDITOR.instances.composeMessage.getData().replace(/\r\n/g, '\n');
 
             lastSignature = '<p><!--begin_signature--><!--end_signature--></p>';
             nextSignature = '<p><!--begin_signature-->' + next[0].replace(/^ ?<br \/>\n/, '').replace(/ +/g, ' ') + '<!--end_signature--></p>';
@@ -103,7 +100,7 @@ var ImpCompose = {
         }
 
         if (this.rtemode) {
-            ed.SetHTML(msg);
+            CKEDITOR.instances.composeMessage.setData(msg);
         } else {
             $('composeMessage').setValue(msg);
         }
@@ -211,16 +208,7 @@ var ImpCompose = {
         }
 
         this.display_unload_warning = false;
-        this._uniqSubmit(form);
-    },
-
-    _uniqSubmit: function(form)
-    {
-        if (this.textarea_ready) {
-            form.submit();
-        } else {
-            this._uniqSubmit.bind(this, form).defer();
-        }
+        form.submit();
     },
 
     onNoSpellError: function(actionID, e)
@@ -280,22 +268,15 @@ var ImpCompose = {
     _beforeSpellCheck: function()
     {
         IMP.SpellCheckerObject.htmlAreaParent = 'composeMessageParent';
-        IMP.SpellCheckerObject.htmlArea = $('composeMessage').adjacent('iframe[id*=message]').first();
-        $('composeMessage').setValue(FCKeditorAPI.GetInstance('composeMessage').GetHTML());
-        this.textarea_ready = false;
+        $('composeMessage').next().hide();
+        CKEDITOR.instances.composeMessage.updateElement();
     },
 
     _afterSpellCheck: function()
     {
-        IMP.SpellCheckerObject.htmlArea = IMP.SpellCheckerObject.htmlAreaParent = null;
-        var ed = FCKeditorAPI.GetInstance('composeMessage');
-        ed.SetHTML($('composeMessage').value);
-        ed.Events.AttachEvent('OnAfterSetHTML', this._afterSetHTML.bind(this));
-    },
-
-    _afterSetHTML: function()
-    {
-        this.textarea_ready = true;
+        IMP.SpellCheckerObject.htmlAreaParent = null;
+        CKEDITOR.instances.composeMessage.setData($F('composeMessage'));
+        $('composeMessage').next().show();
     },
 
     clickHandler: function(e)
index 50dc7a4..b53f673 100644 (file)
@@ -892,38 +892,6 @@ class IMP_Application extends Horde_Registry_Application
         );
     }
 
-    /* horde/services/cache.php methods. */
-
-    /**
-     * Application-specific cache output driver.
-     *
-     * @param array $params  A list of params needed (USED: 'id').
-     *
-     * @return array  See Horde::getCacheUrl().
-     * @throws Horde_Exception
-     */
-    public function cacheOutput($params)
-    {
-        try {
-            $this->init(array('authentication' => 'throw'));
-        } catch (Horde_Exception $e) {
-            throw new Horde_Exception('No cache data available');
-        }
-
-        switch ($params['id']) {
-        case 'fckeditor':
-            return array(
-                'data' =>
-                    'FCKConfig.ToolbarSets["ImpToolbar"] = ' . $GLOBALS['prefs']->getValue('fckeditor_buttons') . ";\n" .
-                    /* To more closely match "normal" textarea behavior, send
-                     * send <BR> on enter instead of <P>. */
-                    "FCKConfig.EnterMode = 'br';\n" .
-                    'FCKConfig.ShiftEnterMode = \'p\';',
-                'type' => 'text/javascript'
-            );
-        }
-    }
-
     /* Language change callback. */
 
     /**
index 7a07fed..d3dd584 100644 (file)
@@ -457,7 +457,7 @@ class IMP_Auth
 
         /* Is the HTML editor available? */
         $imp_ui = new IMP_UI_Compose();
-        $editor = $imp_ui->initRTE(null, true);
+        $editor = Horde_Editor::singleton('Ckeditor', array('no_notify' => true));
         $sess['rteavail'] = $editor->supportedByBrowser();
 
         /* Set view in session/cookie. */
index 49bbb19..885ebbb 100644 (file)
@@ -169,30 +169,39 @@ class IMP_UI_Compose
     }
 
     /**
+     * Initialize the Rich Text Editor (RTE).
+     *
+     * @param boolean $mini  Load the basic ckeditor stub?
      */
-    public function initRTE($mode = 'imp', $editoronly = false)
+    public function initRTE($basic = false)
     {
-        $editor = Horde_Editor::singleton('Fckeditor', array('id' => 'composeMessage', 'no_notify' => true));
-        if ($editoronly) {
-            return $editor;
-        }
+        $editor = Horde_Editor::singleton('Ckeditor', array('basic' => $basic));
 
-        $fck_buttons = $GLOBALS['prefs']->getValue('fckeditor_buttons');
-        if (!empty($fck_buttons)) {
-            $js_onload = array(
-                // This needs to work even if Horde_Cache is not available,
-                // so need to use app-specific cache output.
-                'oFCKeditor.Config.CustomConfigurationsPath = "' . Horde::getCacheUrl('app', array('app' => 'imp', 'id' => 'fckeditor')) . '"',
-                'oFCKeditor.ToolbarSet = "ImpToolbar"'
-            );
-            if ($mode == 'imp') {
-                $js_onload[] = 'oFCKeditor.Height = $(\'composeMessage\').getHeight()';
-                $js_onload[] = 'oFCKeditor.ReplaceTextarea()';
-            }
-            Horde::addInlineScript($js_onload, 'load');
+        $config = array(
+            /* To more closely match "normal" textarea behavior, send <BR> on
+             * enter instead of <P>. */
+            'enterMode: CKEDITOR.ENTER_BR',
+            'shiftEnterMode: CKEDITOR.ENTER_P',
+
+            /* Don't load the config.js file. */
+            'customConfig: ""',
+
+            /* Disable resize of the textarea. */
+            'resize_enabled: false',
+
+            /* Use the old skin for now. */
+            'skin: "v2"'
+        );
+
+        $buttons = $GLOBALS['prefs']->getValue('ckeditor_buttons');
+        if (!empty($buttons)) {
+            $config[] = 'toolbar: ' . $GLOBALS['prefs']->getValue('ckeditor_buttons');
         }
 
-        return $editor->getJS();
+        Horde::addInlineScript(array(
+            'if (!window.IMP) { window.IMP = {}; }',
+            'IMP.ckeditor_config = {' . implode(',', $config) . '}'
+        ));
     }
 
 }
index 35e2b0e..478a3ea 100644 (file)
@@ -88,7 +88,7 @@ class IMP_Views_Compose
             $rte = true;
 
             $imp_ui = new IMP_UI_Compose();
-            $result['jsappend'] .= $imp_ui->initRTE('dimp');
+            $imp_ui->initRTE(!$compose_html);
         }
 
         /* Create list for sent-mail selection. */