UPDATE #1 : module should work now fine for Magento 1.7

To add a custom field to CMS page we will use Magento event mechanism. You can see all events here.

We need to make a module to extend the functionality of CMS page model. We will name it Flexishore_Cms. Add file named Flexishore_Cms.xml to the app/etc/modules directory. Add the folowing content:

<?xml version="1.0"?>
<config>
    <modules>
        <Flexishore_Cms>
            <active>true</active>
            <codePool>local</codePool>
        </Flexishore_Cms>
    </modules>
</config>

Magento is now aware of new module. Now we can create the module itself, which must be located in the app/code/local/Flexishore/Cms directory. Inside this directory, create an etc directory and to file config.xml add this content:

<?xml version="1.0"?>
<?xml version="1.0"?>
<config>
    <modules>
        <Flexishore_Cms>
            <version>0.1.0</version>
        </Flexishore_Cms>
    </modules>
 
    <global>
 
        <models>
            <flexishore_cms>
                <class>Flexishore_Cms_Model</class>
            </flexishore_cms>
        </models>
 
        <blocks>
            <flexishore_cms>
                <class>Flexishore_Cms_Block</class>
            </flexishore_cms>
            <adminhtml>
                <rewrite>
                    <cms_page_edit_form>Flexishore_Cms_Block_Adminhtml_Cms_Page_Edit_Form</cms_page_edit_form>
                </rewrite>
            </adminhtml>
        </blocks>
 
        <resources>
            <flexishore_cms_setup>
                <setup>
                    <module>Flexishore_Cms</module>
                    <class>Mage_Catalog_Model_Resource_Eav_Mysql4_Setup</class>
                </setup>
                <connection>
                    <use>core_setup</use>
                </connection>
            </flexishore_cms_setup>
            <flexishore_cms_write>
                <connection>
                    <use>core_write</use>
                </connection>
            </flexishore_cms_write>
            <flexishore_cms_read>
                <connection>
                    <use>core_read</use>
                </connection>
            </flexishore_cms_read>
        </resources>
 
    </global>
 
    <adminhtml>        
        <events>
            <cms_page_prepare_save>
                <observers>
                    <flexishore_cms_save_page>
                        <type>singleton</type>
                        <class>flexishore_cms/observer_cms</class>
                        <method>savePage</method>
                    </flexishore_cms_save_page>
                </observers>
            </cms_page_prepare_save>
 
            <adminhtml_cms_page_edit_tab_main_prepare_form>
                <observers>
                    <flexishore_cms_prepare_form>
                        <type>singleton</type>
                        <class>flexishore_cms/observer_cms</class>
                        <method>prepareForm</method>
                    </flexishore_cms_prepare_form>
                </observers>
            </adminhtml_cms_page_edit_tab_main_prepare_form>
        </events>
    </adminhtml>
 
</config>

We have added 2 custom observers which will be used to handle our new attribute. To make it work create new file called Cms.php in app/code/local/Flexishore/Cms/Model/Observer directory. It must contain 2 methods : prepareForm and savePage

Method prepareForm is responsible for adding our new attribute to existing form.

/**
 * Add image field to form
 *
 * @param Varien_Event_Observer $observer
 *
 * @return void
 */
public function prepareForm(Varien_Event_Observer $observer)
{
    $form = $observer->getEvent()->getForm();
 
    $fieldset = $form->addFieldset(
        'image_fieldset',
        array(
             'legend' => 'Image',
             'class' => 'fieldset-wide'
        )
    );
 
    $fieldset->addField('background', 'image', array(
        'name' => 'background',
        'label' => 'Background image',
        'title' => 'Background image'
    ));
}

Method savePage is responsible for saving custom background image on server in media/background directory

/**
 * Save background image
 *
 * @param Varien_Event_Observer $observer
 *
 * @return void
 */
public function savePage(Varien_Event_Observer $observer)
{
    $model = $observer->getEvent()->getPage();
    $request = $observer->getEvent()->getRequest();
 
    if (isset($_FILES['background']['name']) && $_FILES['background']['name'] != '') {
        $uploader = new Varien_File_Uploader('background');
 
        $uploader->setAllowedExtensions(array('jpg','jpeg','gif','png'));
        $uploader->setAllowRenameFiles(false);
        $uploader->setFilesDispersion(false);
 
        // Set media as the upload dir
        $media_path  = Mage::getBaseDir('media') . DS . 'background' . DS;
 
        // Set thumbnail name
        $file_name = 'cms_';
 
        // Upload the image
        $uploader->save($media_path, $file_name . $_FILES['background']['name']);
 
        $data['background'] = 'background' . DS . $file_name . $_FILES['background']['name'];
 
        // Set thumbnail name
        $data['background'] = $data['background'];
        $model->setBackground($data['background']);
    } else {
        $data = $request->getPost();
        if($data['background']['delete'] == 1) {
            $data['background'] = '';
            $model->setBackground($data['background']);
        } else {
            unset($data['background']);
            $model->setBackground(implode($request->getPost('background')));
        }
    }
}

To be sure that form will allow us to upload file we have to add enctype=”multipart/form-data”. To do this we have to overwrite cms page edit form :

class Flexishore_Cms_Block_Adminhtml_Cms_Page_Edit_Form extends Mage_Adminhtml_Block_Widget_Form
{
 
  protected function _prepareForm()
  {
    $form = new Varien_Data_Form(array('id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post', 'enctype' => 'multipart/form-data'));
    $form->setUseContainer(true);
    $this->setForm($form);
    return parent::_prepareForm();
  }
 
}

We are almost done now, but we need to add missing attribute to database. To do this, create sql directory in app/code/local/Flexishore/Cms. After that we have to add new file called mysql-install-0.1.0.php with this content:

<?php
/**
 * Flexishore Sunvalley module
 *
 * @category    Flexishore
 * @package     Flexishore_Cms
 * @author      Kos Rafał <rafal.k@flexishore.com>
 * @copyright  Copyright (c) 2011 Flexishore http://flexishore.com
 */
 
$installer = $this;
$setup = new Mage_Eav_Model_Entity_Setup('core_setup');
$installer->startSetup();
 
$installer->run('
  ALTER TABLE  `cms_page` ADD  `background` VARCHAR( 255 ) NULL;
');
 
$installer->endSetup();

After all you should see this on CMS edit page:

Source code of module : https://github.com/Flexishore/flexishore-examples

  • David

    Exactly what I was looking for. Worked perfectly. It really sucks that you have to alter the cms_page table in order to expand the power of pages though. Really makes it seem like pages was a complete afterthought.

    • http://www.webeks.net Miha Trtnik

      The whole magento should be written this way. EAV approach is a disaster since it becomes incredibly slow.

  • http://ceckoslab.com Tsvetan Stoychev

    Thank you @Rafal,

    I used your approach and for sure it saved the day,
    but got problem with uploading the image.

    I resolved it and would like to share about it here.

    Your approach works really well expect when we have
    field which type is “file”. The problem comes from, that
    the generated cms page add / edit form doesn’t have


    'enctype' = 'multipart/form-data'

    It is obvious, that we can’t submit files to the server.

    I resolved that problem quickly by rewriting:
    Mage_Adminhtml_Block_Cms_Page_Edit_Form

    My suggestion is the next:


    class MyCompany_MyModule_Block_Cms_Page_Edit_Form extends Mage_Adminhtml_Block_Widget_Form
    {

    protected function _prepareForm()
    {
    $form = new Varien_Data_Form(
    array(
    'id' => 'edit_form',
    'action' => $this->getData('action'),
    'method' => 'post',
    'enctype' => 'multipart/form-data'
    )
    );
    $form->setUseContainer(true);
    $this->setForm($form);
    return parent::_prepareForm();
    }
    }

    The only difference with the core class is:

    ......
    'enctype' => 'multipart/form-data'
    ......

    Cheers!

    • http://optimiseweb.co.uk/ Sid

      Any solutions for this? At the moment I have a copy of the Form.php in a local folder structure and added ‘enctype’ => ‘multipart/form-data’.

      Surely, must be able to inject this via the observers.

      Any thoughts?

    • Debdatta

      I have tried this module. Its working but is unable to upload image. I am presently working with Magento ver. 1.7.0.2 And used the updated xml “http://pastie.org/3672081″ by iFuel Interactive. Its running without error but doesnot upload image. I have added you code in block.

      Add this xml code in config.xml but still image isnt working.

      Flexishore_Cms_Block_Cms_Page_Edit_Form

      Please help me regarding this. Its quiet urgent.

      Thanks in advance.

  • Vitaliy

    Your solution doesn’t work with Magento 1.6.1…
    Sadly…

    • Rafal

      What kind of problems you have? What exactly is not working?

      • Vitaliy

        Thanks for your responce…

        After making all steps, on front page I have:

        Fatal error: Call to a member function getId() on a non-object in Z:\home\wellmag\www\app\code\core\Mage\Cms\Helper\Page.php on line 67

        protected function _renderPage(Mage_Core_Controller_Varien_Action $action, $pageId = null, $renderLayout = true)
        {

        $page = Mage::getSingleton(‘cms/page’);
        if (!is_null($pageId) && $pageId!==$page->getId()) {
        $delimeterPosition = strrpos($pageId, ‘|’);

        ——

        $page = Mage::getSingleton(‘cms/page’);

        couldn’t get the singleton ‘cms/page’…

        • Rafal

          Hmm, I have checked this code

          $page = Mage::getSingleton('cms/page');
          var_dump(get_class($page));

          on 1.6.1 and everything was ok.

          Did you overwrite Cms model?

          • Vitaliy

            No, I didn’t…
            and I removed maked changes and got the source code of this solution from git …
            and still not worked for me… with the same error…
            very strange…

          • Vitaliy

            Okay…
            I’ve installed clear Magento 1.6.1.
            and copy sources from git archive…
            caches are flished and disabled…

            it’s not work…
            with the same error…
            Magento couldn’t find ‘cms/page’ in registry
            and couldn’t register it with self::register method

            Ohhh…

          • Vitaliy

            * flushed i mean

          • Rafal

            Could you put some exception traceback on http://pastie.org/ ?

          • Vitaliy

            http://pastie.org/3125171

            as i understood Magento try to load model ‘cms/page’ from Flexoshore directory…
            but i’m not sure

          • Vitaliy

            there are 2 lines from system.log, which repeating for each page reload… nothing else for this issue…

          • Vitaliy

            By the way…
            Rafal, many thanks for your help…

            I’m new to Magento of cource…
            but I’ve solve my issue…

            I’ve added some classes which Magento requires (investigated system.log) as descendants from core cms page classes…

            and now it works…

            Thanks for help!

          • Quentin

            I got the same problem as you Vitaliy, Fatal error: Call to a member function getId() on a non-object in /var/sites/xxxxxxx/trunk/app/code/core/Mage/Adminhtml/controllers/Cms/PageController.php on line 99

            Can you tell me which method you added ??

  • Quentin

    I got the same problem as you Vitaliy, Fatal error: Call to a member function getId() on a non-object in /var/sites/xxxxxxx/trunk/app/code/core/Mage/Adminhtml/controllers/Cms/PageController.php on line 99

    Can you tell me which method you added ?

  • ilia

    I got error
    Fatal error: Call to a member function load() on a non-object in /home/www/html/app/code/core/Mage/Adminhtml/controllers/Cms/PageController.php on line 91

    I can’t understand why it don’t work.

  • http://blogs.ifuelinteractive.com iFuel Interactive

    For all of those who are getting the “Fatal error: Call to a member function….” The reason is the config.xml in this post is overwriting the core Mage_Cms module. You need to change your XML to the code to the below. (Sorry for the multiple posts… the code formatting in these comments is very poorly handled. Hopefully this works. If not, go here… http://pastie.org/3672081 )

    <?xml version=”1.0″?>
    <config>
    <modules>
    <Flexishore_Cms>
    <version>0.1.0</version>
    </Flexishore_Cms>
    </modules>
    <global>
    <models>
    <!– <cms>
    <class>Flexishore_Cms_Model</class>
    </cms> –>
    <flexishorecms>
    <class>Flexishore_Cms_Model</class>
    </flexishorecms>
    </models>
    <resources>
    <!– <cms_setup> –> <flexishorecms_setup>
    <setup>
    <module>Flexishore_Cms</module><class>Mage_Catalog_Model_Resource_Eav_Mysql4_Setup</class>
    </setup>
    <connection>
    <use>core_setup</use>
    </connection>
    <!– </cms_setup> –> </flexishorecms_setup>
    <!– <cms_write> –> <flexishorecms_write>
    <connection>
    <use>core_write</use>
    </connection>
    <!– </cms_write> –> </flexishorecms_write>
    <!– <cms_read> –> <flexishorecms_read>
    <connection>
    <use>core_read</use>
    </connection>
    <!– </cms_read> –> </flexishorecms_read>
    </resources>
    </global>
    <adminhtml>
    <events>
    <cms_page_prepare_save>
    <observers>
    <flexishore_cms_save_page>
    <type>singleton</type>
    <!–<class>cms/observer_cms</class>–>
    <class>flexishorecms/observer_cms</class>
    <method>savePage</method>
    </flexishore_cms_save_page>
    </observers>
    </cms_page_prepare_save>
    <adminhtml_cms_page_edit_tab_main_prepare_form>
    <observers>
    <flexishore_cms_prepare_form>
    <type>singleton</type>
    <!–<class>cms/observer_cms</class>–>
    <class>flexishorecms/observer_cms</class>
    <method>prepareForm</method>
    </flexishore_cms_prepare_form>
    </observers>
    </adminhtml_cms_page_edit_tab_main_prepare_form>
    </events>
    </adminhtml>
    </config>

    • Debdatta

      Thanks for the check. But image upload isn’t working. Once I manually change the database everything works fine. But when I upload from cms page the image isn’t uploaded. Otherwise the module is good. Wish one would check this.

  • Kashif

    Thanks, to Rafal and Tsvetan Stoychev.

  • http://atwix.com atwix

    Hi. Thanks for information, it’s very useful. We have an article on the similar topic posted here http://www.atwix.com/magento/adding-custom-attribute-to-a-cms-page/ you’re welcome to check it out and leave your feedback

  • Pingback: Magento: WYSIWYG In Widget Options | Code and Programming

  • http://www.roshanlal.in/ roshan lal

    Good article, saved my time

  • Kos Rafał

    I have updated module to make it work for magento 1.7. There was some changes in handling uploaded file in form.

    It looks like it is not possible to inject “enctype” => “multipart/form-data” through observer. I have done this by overwriting cms page edit form.

  • Kos Rafał

    I have update module. Please check it now. I’ve made some tests on magento 1.7 and it works fine now.

  • Amir Alam

    helllo please tell me how can i show it on front end? please send me the code or email me on amiralamkhan@hotmail.com

  • Dave Sims

    Hi, Works great in order to add the field to Magento, but I am struggling to see how to get the data back out again on the front end?

    • Nick Spiel

      It felt very dirty (as most things seem to in Magento) but i managed to get this to display using the following:

      <div style="background-image: url('getPage()->background; ?>’);”>

  • Melissa

    does anyone have this working in 1.8? Nothing is saving in the table.