English Nederlands

Login

http://juriansluiman.nl

Improved TinyMce solution for the Zend Framework

« A new Flickr service for php Heading towards a proposal for Zend_Service_Flickr »

On Tuesday, June 23, 2009 at 10:26 AM

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!

« A new Flickr service for php Heading towards a proposal for Zend_Service_Flickr »

Reactions

I like the changes you made.

I see you point regarding the fact that i did include a options/plugins of TinyMce that I really should have left out (i.e. templates).

I'm not so sure of the editor always being enabled though as I like to keep the amount of JS loaded to a minimum where possible, so being able to turn it on and off was very useful for me at the time I built the component.

I have given the idea of a container a lot of consideration and am not 100% on either way of implementation. I don't think either way is right or wrong, just that it needs more investigation before I could say I prefer one over the other.

I have just been signed off as a contributor for the Zend Framework so I am hoping to put forward a proposal for a component that will allow direct integration of TinyMCE and any other suggested editors. I'll keep you posted as it develops

I'm still trying to understand the use of TinyMce templates better. When you want to turn off the javascript code, you can also change the element from 'tinyMce' into 'textarea'. Furthermore, I also have some improvements for my own code.

For example, when you pass an array of options (like plugins), the view will look if all items are in the supported list of plugins. This is especially useful when you try to pass an unsuported mode of theme.

PS. I know there are some problems with the reaction form. I'll try to investigate it asap :)

The templates are a pretty useful tool when you get the hang of them. Along with the noneditable plugin it makes the editor very powerful tool for CMS style page editing.

I miss read the bit about it always being enabled (doh). It made more sense on the second read through.

Your alterations have actually prompted me to rethink a whole segment of my code in preparation for putting together a proposal to submit to the framework.

Its not a totally standalone component rather than bits I have tagged together "Zucchi_Wysiwyg". hopefully the structure I have created will also allow me to add different types of editor to the collection as I go along.

I have rebuilt to utilise the resource loader and created a resource to load all the relevant bits

I'm separateing out the more obscure plugins to alow independant configuration per editor loaded.

The compressor is now optional.

Its still VERY buggy while Im working out the kinks but what I have is available at https://subversion.zucchi.co.uk/listing.php?repname=library&path=%2Ftrunk%2FZucchi%2F#path_trunk_Zucchi_

Your thoughts would be very much appreciated.

 

 

that was meant to read

"Its now a totally standalone component rather than bits I have tagged together "Zucchi_Wysiwyg". "

Hi Matt,

I understand your point about facilitating a WYSIWYG editor with a unified interface for any editor. I'd be a very good idea if that's possible, but I have my doubts about it (sorry :p). As long as you have completely different configurations for every editor, it's difficult to provide a single interface for all those elements.

Because I have exams at the moment, I don't have the time to dig through all your code. I see it hasn't change much, but there are still some changes. Do you have actually an idea for an api for the WYSIWYG element? If some people would like to change to FCKeditor, will this be possible with your Zucchi_Wysiwyg?

PS. Sorry I don't let you post any links, that's just to prevent spamming ;)

Mougli

Hi!

I'm trying to implement TinyMce for hours, but I don't succeed. I have no idea what to do. I've checked your SVN repositroy and I have all the files you have in the same places but I'm getting an exception

Message: Plugin by name 'TinyMce' was not found in the registry; used paths: Zend_Form_Element_: Zend/Form/Element/

I guess it should search in App/Form/Form.php? I don't know. I don't understand zf jet :)

Your help will be most appreciated.

Hi Mougli,

You're completely right! I didn't mention it in my explanation, I'm sorry about that.

The form you create is usually a child of Zend_Form. Because I have added another form element, you should add those elements with the method Zend_Form::addPrefixPath(). If you look at the Sozfo library source, you can find the Sozfo_Form adding that prefix (http://code.google.com/p/sozfo/source/browse/trunk/library/Sozfo/Form.php).

If your forms are extending Sofzo_Form instead of Zend_Form, everything will work as expected. Thanks for pointing the missing part. I'll update it as soon as possible.

Mougli

Hi!

I'm still having problems. Now I get no error or exception, everything works fine, I just don't get tinyMce editor. I get ordinary textarea...

<dt id="longDescription-label"><label for="longDescription" class="required">Opis:label>dt>
<dd id="longDescription-element">
<textarea name="longDescription" id="longDescription" cols="40" rows="10">textarea>dd>

This is my source code for tiny...

Any idea? :)

 

 

I think something's going wrong with initializing the javascript. If you have Firefox, try to look into the error console for some errors. The script defaults are online at the Google code project, but that might give some errors.

I'm using always the configuration option to set the scriptPath and scriptFile locally (i.e. at the same place the application is running). You might want to download tinyMce yourself and point the form element to that location.

Mougli

Ok, I fixed that. In case someone else will have the same problem...

In function _renderScript() I changed

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

to

$this->view->headScript()->appendFile($this->view->baseUrl() . $script);

 

Jurian, tnx for your help :)

Paul

I am still getting the message "Warning: Plugin by name 'FormTinyMce' was not found in the registry".  I have tried tweaking the bootstrap, moving the files around. 

What is the trick getting Zend to find the plugin.

I am extended using Sofzo_Form with no errors.

Any help really appreciated.

Thanks

Paul

 

Mads

Hi

Thank for this, worked well for my implementation.

Found a small bug using it in IE7. The last line of the config it inserts produce a JS error in IE7 because it has a trailing comma.

Fixed it in the TinyMce view helper by adding a small change to the _renderEditor method to this result:

 protected function _renderEditor ()
    {
        $script = 'tinyMCE.init({' . PHP_EOL;
        $config_count = count($this->_config);
        $count = 1;
        foreach ($this->_config as $name => $value) {
            if (is_array($value)) {
                $value = implode(',', $value);
            }
            if (!is_bool($value)) {
                $value = '"' . $value . '"';
            }
            $comma = ($count <= $config_count-1) ? ',' : '';
            $script .= $name . ': ' . $value . $comma . PHP_EOL;
            $count++;
        }

        $script .= '});';

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

Hi Mads, I think you're pointing to the bug of elided commas in IE7 (http://www.nabble.com/extra-comma-in-%7Ba:1,%7D-is-OK-td14657892.html).

Your solution adds a ":" after the last parameter, but is that correct? I think you should keep it blank :) And because I'm lazy and think it's too much work to count the items, I'd rather use arrays and the implode function:

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

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

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

I'll fix it in svn (and talking about it, I should tag the trunk asap for consistency). Thanks for the report!

Kuzma

Hi!

I wonder can I use this plugin in another way: register a view helper in bootstrap, add your 2 files and just write

in template <?php echo $this-> TinyMce() ?> in order to render TinyMce?

Thank you!

Kuzma, the TinyMce view helper is only meant to render the javascript at top of the page. So if you have already a textarea defined and want to enable the tinyMce window for that textarea, it's possible (but actually not recommended).

You should understand you need to set the options first ($this->tinyMce()->setOptions()) and after that render it ($this->tinyMce()->render()). It is possible to chain those methods (so you get $this->tinyMce()->setOptions()->render()).

The array of options you need to pass to the setOptions() method could be derived when you look at the TinyMce view helper class (here: http://code.google.com/p/sozfo/source/browse/trunk/library/Sozfo/View/Helper/TinyMce.php).

Branko

Paul, answer to your problem is to define helper path. You should (in Bootstrap.php) add following line

$view->addHelperPath('Sozfo/View/Helper', 'Sozfo_View_Helper');

And of course you should have Sozfo in your library path.

Kuzma

Jurian, thank you for your kind reply. I made output in that way:

echo $this->entryForm;
    $this->tinyMce()->setOptions(array(
    'label'      => 'entrybody',
    'required'   => true,
    'cols'       => '80',
    'rows'       => '24',
    'editorOptions' => new Zend_Config_Ini(APPLICATION_PATH . '/configs/tinymce.ini'
)))->render();

But my textarea is not covered by tinymce. I'm using custom form: entryForm is made of class ZFBlog_Form_EntryAdd which extends ZFBlog_Form and ZFBlog_Form extends Zend_Form. Or I making mistake and my form must extend FormTinyMce? In that way (ZFBlog_Form_EntryAdd extends Tinymce_View_Helper_FormTinyMce) I'm getting Object of class ZFBlog_Form_EntryAdd could not be converted to string error :/

Thank you!

Hi Kuzma. There are several objects involved in rendering the element. The element itself (Sozfo_Form_Element_TinyMce) is extending Zend_Form_Element_Textarea (the elements are 99% the same). A form element uses a view helper to render the element. For the tinyMce element, this is the Sozfo_View_Helper_FormTinyMce, extending the Zend_View_Helper_FormTextarea.

The FormTinyMce view helper renders the <textarea> html. The helper calls also the TinyMce view helper. This helper adds, based on the options set, the required javascript files (both the links to the javascript files and the inline javascript).

You should understand those view helpers (both FormTinyMce and TinyMce) are required to render the elements. You're talking about the form. You can't create a form which extends FormTinyMce because the first is a Zend_Form, the latter a view helper.

The most important thing to notice is your error message. It's talking about the entryForm form, which is a ZFBlog_Form_EntryAdd. This object has some strange things inside and that's why it throws an exception. This has nothing to do with the tinyMce rendering, because it happens bfeore you even try to convert the textarea into a TinyMce editor. If you don't know what's wrong, you could ask the people from Zend (at the mailng list) or others at e.g. zfforums.com about the problem :)
Good luck!

Kuzma

Thank you Jurian! Now everyhing works fine. It was my mistake with paths.

And I've last question about confuging TinyMce with ini files...

I'm using yours ini example and I'm doing in that way:

    $this->tinyMce()->setOptions(array(
    'mode' => 'textareas',
    'theme' => 'advanced',
    'editorOptions' => new Zend_Config_Ini(APPLICATION_PATH . '/configs/tinymce.ini','moderator')
    ))->render();

With this line:     'editorOptions' => new Zend_Config_Ini(APPLICATION_PATH . '/configs/tinymce.ini','moderator')

I'm getting an error:Catchable fatal error: Object of class Zend_Config_Ini could not be converted to string in ...View\Helper\TinyMce.php on line 155

Thank you!

Kate
Hi! I've tried your solution but I have on problem. Everything is great, I have tinyMce editor but when I click submit the only sent data is one from original textarea. Nothing from tinyMce editor is send. Do you have any idea why it happens? Thanks in advance for your help. Kate
Deezmo

Hi guys! Great job!

Kate, check filters in your Zend_Form class. I think you have some filters added there (maybe StripTags or something similar). Good luck!

Emile

Hi Jurian,

Great Tutorial, but i don't know were i have to place the tiny_mce folder to get it worked?

 

Hi Emile,

The configuration has the options scriptPath and scriptFile. In this example the scriptPath is /js/tiny_mce and inside it there is the file tiny_mce.js. 

If you want another path, you can configure it yourself by changing the lines in the ini file.

Emile

Hi Jurian,

Thnx for your answer, but i am getting the HTTP 500 Internal Server Error message when i try to access the form. I created the TinyMCE element as follow in my form:

$pcontent = new Zend_Form_Element_TinyMce('Page', array(
'editorOptions' => new Zend_Config_Ini(APPLICATION_PATH . '/configs/tinymce.ini', 'moderator'
)));

$pcontent->setRequired(true)
              ->addErrorMessage(
"The content field is required and can't be empty")
              ->setAttrib(
'cols', 130)
              ->setAttrib(
'rows', 30)
              ->addValidator(
'NotEmpty', true);

Thanx and regards

Emile

naada

Hi Jurian,

Thnx for your answer, but i am getting the HTTP 500 Internal Server Error message when i try to access the form. I created the TinyMCE element as follow in my form:

$pcontent = new Zend_Form_Element_TinyMce('Page', array(
'editorOptions' => new Zend_Config_Ini(APPLICATION_PATH . '/configs/tinymce.ini', 'moderator'
)));

$pcontent->setRequired(true)
              ->addErrorMessage(
"The content field is required and can't be empty")
              ->setAttrib(
'cols', 130)
              ->setAttrib(
'rows', 30)
              ->addValidator(
'NotEmpty', true);

Thanx and regards

Emile

Marko

Hello,

I read the whole tutorial and i think it is great! I managed to get Zend_Form extend Sozfo_Form without errors. In my view source I see jQuery included as well as tiny_mce.js

But i am stil just getting plain textarea instead of tinymce textarea.

Can you please give me some more directions to get it working.

Thank you in advace

NK

I'm having a problem!

I've spent many hours working in order to get TinyMCE implemented in my Zend Framework application, yet I am still uncertain about the fate of mankind!

But seriously, thank you!  Great simplification of a great original project from matt.

nk

React

This address is kept private.