Some time ago Matt Cockayne posted a solution to get TinyMce working as a form element inside a Zend_Form_Element. I had a look at his code and thought it was a very good one. But as critical I am, I thought there were some points still able to improve.

Because Soflomo has the open library Sozfo, it's a good idea to put this new element inside the Sozfo library isn't it? When you look at it, you see some similarities between his and mine solution:

  1. We both use a Zend_Form_Element_Textarea which point to a view helper called FormTinyMce. The element is nothing special, the view helper initiates the <textarea> element with all attributes.
  2. We both use another view helper to add the javascript required to initiate the TinyMce editor.

Of course there are some differences. When I read Matt's post about the implementation I noticed he had a lot of special configuration he personally used. For example a template system which used a content_templates class. He also forced to use the php TinyMce compressor.

Another point was the usage of a container. Matt saw the similarities between the jQuery container and a container especially for TinyMce. The container implies you have a $this->tinyMce() inside your template. When a form is loaded, the view helper "enables" itself and adds some code inside your layout.

But actually, it's not necessary to create a container. The TinyMce view helper is able to push all javascript inside the HeadScript view helper. By removing the container the code is better to read and understand I think.

The Sozfo solution

The form element is actually the same as Matt's one, but you can find it online here. The form view helper is made a little bit simpler:

public function FormTinyMce ($name, $value = null, $attribs = null)
{
    $info = $this->_getInfo($name, $value, $attribs);
    extract($info); // name, value, attribs, options, listsep, disable

    $disabled = '';
    if ($disable) {
        $disabled = ' disabled="disabled"';
    }

    if (empty($attribs['rows'])) {
        $attribs['rows'] = (int) $this->rows;
    }
    if (empty($attribs['cols'])) {
        $attribs['cols'] = (int) $this->cols;
    }

    if (isset($attribs['editorOptions'])) {
        if ($attribs['editorOptions'] instanceof Zend_Config) {
            $attribs['editorOptions'] = $attribs['editorOptions']->toArray();
        }
        $this->view->tinyMce()->setOptions($attribs['editorOptions']);
        unset($attribs['editorOptions']);
    }
    $this->view->tinyMce()->render();

    $xhtml = '<textarea name="' . $this->view->escape($name) . '"'
            . ' id="' . $this->view->escape($id) . '"'
            . $disabled
            . $this->_htmlAttribs($attribs) . '>'
            . $this->view->escape($value) . '</textarea>';

    return $xhtml;
}

One important thing to mention is tinyMce will always be enabled. Because the view helper has some defaults, the form element is working even without any configuration!
If a config is present, it's pushed to the view helper.

The TinyMce view helper is too long to post here (at Google Code it's easier to read), but the most important functions are these ones:

public function render()
{
    if (false === $this->_enabled) {
        $this->_renderScript();
        $this->_renderCompressor();
        $this->_renderEditor();
        $this->_enabled = true;
    }
}

protected function _renderScript ()
{
    if (null === $this->_scriptFile) {
        $script = $this->_defaultScript;
    } else {
        $script = $this->_scriptPath . '/' . $this->_scriptFile;
    }

    $this->view->headScript()->appendFile($script);
    return $this;
}

protected function _renderCompressor ()
{
    if (false === $this->_useCompressor) {
        return;
    }
    $script = 'tinyMCE_GZ.init({' . PHP_EOL
            . 'themes: "' . implode(',', $this->_supportedTheme) . '"' . PHP_EOL
            . 'plugins: "'. implode(',', $this->_supportedPlugins) . '"' . PHP_EOL
            . 'languages: "' . implode(',', $this->_supportedLanguages) . '"' . PHP_EOL
            . 'disk_cache: true' . PHP_EOL
            . 'debug: false' . PHP_EOL
            . '});';

    $this->view->headScript()->appendScript($script);
    return $this;
}

protected function _renderEditor ()
{
    $script = 'tinyMCE.init({' . PHP_EOL;

    foreach ($this->_config as $name => $value) {
        if (is_array($value)) {
            $value = implode(',', $value);
        }
        if (!is_bool($value)) {
            $value = '"' . $value . '"';
        }
        $script .= $name . ': ' . $value . ',' . PHP_EOL;
    }

    $script .= '});';

    $this->view->headScript()->appendScript($script);
    return $this;
}

I think it's pretty clear. The render can only be executed once and exists of three parts: the inclusion of the TinyMce script, the compressor options (if required) and the actual initialisation of the TinyMce editor.

Configuration

When you want to configure the TinyMce for, it's really easy to do with an ini file and Zend_Config_Ini. For example I have those two configurations:

[editor]
scriptPath = "/js/tiny_mce/"
scriptFile = "tiny_mce.js"
mode = "textareas"
element_format = "html"
forced_root_block = "p"

[user : editor]
theme = "simple"

[moderator : editor]
theme = "advanced"
width = "580"
height= "300"
plugins= "paste, table"
theme_advanced_resizing = true
theme_advanced_resizing_use_cookie = false
theme_advanced_toolbar_location = "top"
theme_advanced_buttons1 = "formatselect, bold, italic, underline, strikethrough, |, justifyleft, justifycenter, justifyright, justifyfull, |, bullist, numlist, |, outdent, indent, blockquote, |, undo, redo, cleanup, removeformat, pasteword, code "
theme_advanced_buttons2 = "link, unlink, anchor, |, image, hr , sub, sup, charmap, |, forecolor, backcolor, |, tablecontrols"
theme_advanced_buttons3 = ""
theme_advanced_blockformats = "p,h1,h2,h3,blockquote,dt,dd"

If you want to add some configuration for your element, just do so by the following:

$this->addElement('tinyMce', 'message', array(
    'label'      => 'Message',
    'required'   => true,
    'cols'       => '50',
    'rows'       => '10',
    'editorOptions' => new Zend_Config_Ini(APPLICATION_PATH . '/configs/tinymce.ini', 'user')
));

Download

The form element and view helpers are located in trunk of the Sozfo library. I haven't had any time to create a prefab package like a zip or something. But after my exams I'll be able to do so.

Suggestions

If you have more suggestions about this implementation, or comment to improve Matt's or my code, please let me know!