18
Jul/09
23

Zend Framework: The Dijit Editor

Introduction

In one of my current projects, AircraftConnection.com, I decided to use the dijit.Editor WYSIWYG editor in my user’s forms. I ran into a few problems and could not find much documentation on the topic.

  1. I wanted the label, description, and field in a different order.
  2. I wanted the data entered into the editor to actually be passed to the server when the form was submitted.
  3. I wanted to pre-populate the form with data the user had previously entered.

I was able to find a few posts related to posting the data to the server via a hidden field but these mostly did so via Ajax, which is fine. It just didn’t suit my needs. I adopted and manipulated their methods to work in the way I wanted.

Prerequisites

  • A web server with PHP installed
  • The Zend Framework
  • Dojo and Dijit Javascript libraries

In this post I make the assumption that you already have the above installed as well as your ZF project created with the Javascript libraries included.

Note: I have set up my example application to use the “Blog_” namespace. I have also used the autoloader to add namespaces for Form and Element. Please keep this in mind when you see a line of code such as new Blog_Element_Editor().

Getting Started

The application we are going to build will be very simple. We will have a single controller, IndexController, with two actions, indexAction and editAction. We will use the editAction to edit the content that is displayed in the index action. The editAction is where we will display the form with the dijit.Editor.

So, in reality, the only work we are going to do will be in the IndexController::editAction() method. This will handle displaying the form as well as processing the data posted to the server.

Creating the Form Elements

The first thing in my list was that I wanted to display the label, description, and field in a different order. The default is label, field, description, which really doesn’t make all that much sense. So, the first thing we need to do is create our own Element class and extend the dijit.Editor class. We need to override the loadDefaultDecorators() method.

Editor.php

class Blog_Element_Editor extends  Zend_Dojo_Form_Element_Editor {
 
   public function loadDefaultDecorators() {
      if($this->loadDefaultDecoratorsIsDisabled()) {
         return;
      }
 
      $decorators = $this->getDecorators();
      if(empty($decorators)) {
         $this->addDecorator('DijitElement')
                ->addDecorator('Description', array(
                      'tag' 		=> 'p',
		      'class'		=> 'description',
		      'placement'	=> Zend_Form_Decorator_Abstract::PREPEND))
	         ->addDecorator('HtmlTag', array(
		      'tag'		=> 'dd',
		      'id'	        => $this->getName() . '_element'))
	         ->addDecorator('Label', array(
		      'tag'		=> 'dt',
		      'escape'	=> false));
      }
   }
}

Basically all we are doing here is changing the value of “placement” for our description to be prepend instead of append. One item to not is the use of “DijitElement” instead of the normal “ViewHelper” since we extending a Dijit element. Now we can create our form. The form will consist of a single editor and a submit button.

Creating the Form

As stated, we are going to create a form and add one of the Editor elements we just created as well as a submit button. We will do this by extending Zend_Form.

EditForm.php

class Blog_Form_EditForm extends Zend_Form {
 
   public function init() {
      Zend_Dojo::enableForm($this);
   }
 
   public function __construct($options=null) {
      parent::__construct($options);
 
      $this->setName('editForm');
      $this->setMethod('post');
 
      $this->addElement(new Blog_Element_Editor( 'editor', array(
			'label'		=> 'Edit Page:',
                        'description'=> 'This form allows you to edit the index page.',
			//'plugins'	=> array('undo', '|', 'bold', 'italic'), //<- you can choose your plugins here
                        'dojoType'	=> 'dijit.Editor'
      )));
 
      $this->addElement('submit', 'submit', array(
			'ignore'	=> true,
			'label'		=> 'continue'
      ));
   }
}

And the form is done. If you haven’t already, create your editAction function and the edit view.

The Edit View

At this point the view is very simple. We will add some Javacsript to it in just a bit.

edit.phtml

echo $this->form;
if($this->dojo()->isEnabled()) {
   $this->dojo()->setLocalPath('/scripts/Dojo/dojo/dojo.js')
          ->addStyleSheetModule('dijit.themes.tundra');
   echo $this->dojo();
}

Here we are checking to see if dojo is enabled, which we did enable via our form in the init() function. We also set the path to the dojo.js file (change this to wherever this file exists in your project relative to the public directory) and also select a stylesheet to use for the editor. Be sure to echo out the form prior to checking to see if dojo is enabled or it will fail. Now on to the controller.

Note: In the below code I make use of a logger. If you do not wish to do so you can simply echo out any errors, send them to firebug, etc. Your choice.

IndexController.php

class IndexController extends Zend_Controller_Action
{
   private $logger;
 
   public function init() {
      $this->logger = Zend_Registry::get('logger');
   }
 
   /**
    * The default action - show the home page
    */
    public function indexAction()
    {
        // TODO Auto-generated IndexController::indexAction() action
    }
 
    public function editAction() {
    	$form = new Blog_Form_EditForm();
 
    	$this->view->form = $form;
    }
}

All we are doing here is creating an instance of our form, assigning it to the view, and displaying the page. If you bring up your browser and view http://localhost/index/edit/ you will be able to see the form with all of it’s fancy tools.

Populating the Editor

The next thing we need to do is populate the editor with the data we want to edit. In this example we are going to pull the contents of our index page into the editor and save the edited content back to the file. The first thing we need to do is read the content from the file. Let’s update our editAction() function to the following.

IndexController.php

public function editAction() {
   $form = new Blog_Form_EditForm();
   $this->view->form = $form;
 
    // get the contents of the index file
    $file = APPLICATION_PATH . '/views/scripts/index/index.phtml';
    $contents = trim(file_get_contents($file, FILE_TEXT));
    // remove all line breaks because they will cause problems with the Javascript
    $contents = str_replace("\r\n", "", $contents);
 
   // pass the file contents to our view
   $this->view->fileContents = $contents;
}

Our view is now aware of the contents of the index.phtml file but it is still not available to our editor yet. Here is where a little Javascript trickery happens. Essentially what we are going to do is populate a Javascript variable with the contents of our file. We will then use some Javascript to populate the editor. Since the dijit editor is not a textarea but is actually an iframe we will need to get the body of the iframe and add the contents via dijit’s setValue() function. Let’s go back to the edit.phtml file.

Note: WordPress keeps stripping out my Javascript. So wherever you see — script tag — use a real script tag

edit.phtml

-- script tag --
   var fileContents = '<?php echo $this->fileContents ?>';
-- end script tag --
<?php
   echo $this->form;
   if($this->dojo()->isEnabled()) {
      $this->dojo()->setLocalPath('/scripts/Dojo/dojo/dojo.js')
                        ->addStyleSheetModule('dijit.themes.tundra');
      $this->dojo();
   }
?>
-- script tag --
   dojo.addOnLoad(function() {
      dijit.byId('editor-Editor').setValue(fileContents);
   }
-- end script tag --

Now you can reload the page in your browser and you should see the contents of your index.phtml file there. If you have Firebug, or a similar tool, you can walk through the DOM and take a look at how dijit creates the editor. Notice that there is a hidden field with the name “editor” which just happens to be the name of the form element we created. Dijit creates this hidden field for us to use to pass the contents of the form back to the server.

Now our data is being passed back to the server. We need to update our editAction() function to handle the post data and update the file.

IndexController.php

public function editAction() {
   $form = new Blog_Form_EditForm();
   $this->view-form = $form;
 
   // the file we are editing
   $file = APPLICATION_PATH . '/views/scripts/index/index.phtml';
 
   // check to see if the form has been submitted
   $request = $this->getRequest();
   if($request->isPost()) {
      $editorContents = $request->getParam('editor');
      file_put_contents($file, $editorContents);
      // send the user to the index page
      $this->_helper->redirector('index', 'index');
 
   } else {
      // form has not been submitted
      $contents = trim(file_get_contents($file, FILE_TEXT));
      // remove all line breaks because they will cause problems with the Javascript
      $contents = str_replace("\r\n", "", $contents);
      $this->view->fileContents = $contents;
   }
}

After you submit the form you will be redirected to your newly updated index page. There you have it. The dijit.Editor in action and we achieved the three goals listed at the beginning. It took me some experimenting to get it working so I hope this helps someone out there!

Conclusion

You can extend the Zend_Dojo_Form_Element_* or Zend_Form_Element_* classes to alter the order of your decorators, add new ones, or override functionality. Inheritance in action!

Getting the editor to do what you want takes a little Javascript magic but the functionality and usability it adds is well worth it. Some users still seem to be pleasantly surprised by having this functionality but it is starting to become common place and in the near future all of our textareas will be replaced by WYSIWYG editors.

Comments (23) Trackbacks (3)
  1. Jason Houle
    10:35 pm on July 18th, 2009

    I just realized that I did not include which versions I am using. They are:

    Zend Framework 1.8.2
    Dojo 1.3 (I believe this is the version that was packaged with the Zend Framework)

  2. Mike Girouard
    12:16 pm on July 21st, 2009

    Its awesome to see you contributing such great content.

    Stay good brother,
    Mike G.

  3. Giorgio Sironi
    1:06 pm on July 21st, 2009

    The dijit editor is powerful. I made a patch for the inclusion of other features like links and images but it sits in the tracker from months:
    http://framework.zend.com/issues/browse/ZF-4461

  4. Bastiaan DeJong
    10:49 am on August 11th, 2009

    Thanks for the example.

    Do you have any other examples or links you could share dijit?

  5. Jason Houle
    7:31 am on August 12th, 2009

    Bastiaan – unfortunately there doesn’t seem to be much documentation out there other than dojotoolkit.org. I will continue to update this blog as I experiment with Dojo and Dijit and I will eventually include a links page to other good resources.

    Is there something specific you are looking for? Perhaps I could base my next post on that topic.

  6. BEREGU
    8:31 am on August 14th, 2009

    Hello,
    I’m getting the following error:
    =======================
    Application error
    Exception information:
    Message: Zend_Form_Element requires each element to have a name
    Stack trace:
    #0 C:\host\library\Zend\Dojo\Form\Element\Dijit.php(60): Zend_Form_Element->__construct(NULL, NULL)
    #1 C:\host\ajax\application\forms\Article.php(20): Zend_Dojo_Form_Element_Dijit->__construct()
    #2 C:\host\ajax\application\controllers\IndexController.php(14): BEREGU_Form_Article->__construct()
    #3 C:\host\library\Zend\Controller\Action.php(513): IndexController->indexAction()
    #4 C:\host\library\Zend\Controller\Dispatcher\Standard.php(289): Zend_Controller_Action->dispatch(’indexAction’)
    #5 C:\host\library\Zend\Controller\Front.php(946): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http))
    #6 C:\host\library\Zend\Application\Bootstrap\Bootstrap.php(77): Zend_Controller_Front->dispatch()
    #7 C:\host\library\Zend\Application.php(335): Zend_Application_Bootstrap_Bootstrap->run()
    #8 C:\host\ajax\public\index.php(27): Zend_Application->run()
    #9 {main}
    Request Parameters:
    array(3) {
    ["controller"]=>
    string(5) “index”
    ["action"]=>
    string(5) “index”
    ["module"]=>
    string(7) “default”
    }
    =======================
    It seems I couldn’t add form element to autoloader. The following is in my bootstrap for autoloading:
    $moduleLoader = new Zend_Application_Module_Autoloader(array(
    ‘namespace’ => ‘BEREGU’,
    ‘basePath’ => APPLICATION_PATH,
    ‘resourceTypes’ => array(
    ‘Form_Element’ => array(
    ‘namespace’ => ‘Form_Element’,
    ‘path’ => ‘forms/Elements/’
    )
    )
    ));

    Could you post the link of the editor small application? I believe that real application example will be very helpful for the beginners and pre-intermediates.

    Thank you, Jason Houle. ( plus I really like your site design )

  7. Jason Houle
    9:16 am on August 15th, 2009

    Hello Beregu,

    I will be happy to post the source code but I am currently out of town. I will upload it in a week when I am back at home. Thanks for coming by.

    Jason

  8. Jason Houle
    9:20 am on August 15th, 2009

    I have found a typo in the code which would cause the problem you are seeing. The following is incorrect:

    $this->addElement(new Blog_Element_Editor(), 'editor', array(
       'label'         => 'Edit Page:',
       'description' => 'This form allows you to edit the index page.',
       //'plugins'	   => array('undo', '|', 'bold', 'italic'), //<- you can choose your plugins here
       'dojoType'   => 'dijit.Editor'
    ));

    It should be changed to:

    $this->addElement(new Blog_Element_Editor('editor', array(
       'label'          => 'Edit Page:',
       'description' => 'This form allows you to edit the index page.',
       //'plugins'	    => array('undo', '|', 'bold', 'italic'), //<- you can choose your plugins here
       'dojoType'   => 'dijit.Editor'
    )));

    Notice the placement of the opening and closing parentheses. My mistake! I will fix it in the example above. Thank you for pointing it out.

  9. BEREGU
    3:42 am on August 16th, 2009

    Thank you Jason Houle. I just corrected the typo mistake, and the form with dijit.editor is now ok. ;-)
    Expecting more great tutorials like this from you, Jason Houle ;-)

  10. BEREGU
    4:32 am on August 16th, 2009

    Also it seems a class tundra should be specified in the body of the layout script.

    Like:
    ==========

    OR

    <body
    dojo()->isEnabled())
    echo ‘class=”tundra”‘;
    ?>
    >
    ==========

    So that the Tundra theme will be shown.

  11. BEREGU
    4:33 am on August 16th, 2009

    one line is missing from the above comment….

  12. BEREGU
    4:36 am on August 16th, 2009

    Alright, sorry for the empty comments. It seems the comment script strips out all the html tags from the comment.

    The missing line should be like (before OR)

  13. Jason Houle
    9:20 am on August 16th, 2009

    You are correct. I believe what I had done was set the class in the main template which is not shown here.

  14. Gilles
    10:54 am on August 25th, 2009

    Thanks for this example.

    I’ve just a little problem : I can’t populate the Dijit Editor. Can you help me ?

    Index.phtml (the Dijit works, so all the Dojo stuff works in my layout)

    var fileContents = ‘contenu ?>’ ;

    Test de Dijit Editor

    form; ?>

    dojo.addOnLoad ( function() {
    dijit.byId(‘editor-Editor’).setValue(fileContents);
    }

    Thans in advance,

    Gilles

    • Jason Houle
      10:44 am on September 11th, 2009

      Gilles,

      It seems that some of your code was cut off. Could you please email me the source and I will be happy to help. Also, I apologize for the delayed response. I have been on the road for a large majority of the past month.

      Jason

    • James Johnson
      8:55 pm on December 11th, 2009

      Can’t you populate the dijit editor by using setValue()?

      $form->editor->setValue(‘foo’);

  15. hanum
    7:58 am on October 27th, 2009

    nice info sharing. Thank’s a lot for informatif posting ^_^

  16. Alfred Lopez
    10:06 am on December 14th, 2009

    Very cool! I’m also working with Dojo after using jQuery for some time.

    • Marc Enriquez
      4:26 am on April 4th, 2010

      Never mind, just found out it was because the theme wasn’t loaded.

      Just added the following line to the element:
      $this->setAttrib(‘class’,'tundra’);

  17. Marc Enriquez
    4:17 am on April 4th, 2010

    Awesome guide!! Only I got a little error: buttons of the upper bar are not showing up. Any ideas?

  18. Irfan Ashraf
    1:07 pm on August 26th, 2010

    Nice article. Can anyone show working example of “viewsource” plugin enabled. I am having hard time. Thanks

Leave a comment

Get Adobe Flash playerPlugin by wpburn.com wordpress themes