Adding gallery tab to a CMS page

In this article I will demonstrate how you can create new tab into admin cms page so you can create image gallery similar as upload images for product.

Step 1

Register your module :

<config>
	<modules>
		<Inchoo_Gallery>
			<active>1</active>
			<codePool>local</codePool>
		</Inchoo_Gallery>
	</modules>
</config>

Step 2

Module configuration :

<config>
	<modules>
		<Inchoo_Gallery>
			<version>1.0.0</version>
		</Inchoo_Gallery>
	</modules>
</config>

Step 3

We need to store images into database so we need to create a model, collection, resource and setup script.Our model will implement JsonSerializable so we need to implement jsonSerialize method which will return required json data about the model.

Config:

<!--....-->
<global>
 <models>
  <inchoo_gallery>
   <class>Inchoo_Gallery_Model</class>
    <resourceModel>inchoo_gallery_resource</resourceModel>
   </inchoo_gallery>
   <inchoo_gallery_resource>
    <class>Inchoo_Gallery_Model_Resource</class>
    <entities>
     <gallery>
      <table>inchoo_cms_gallery</table>
     </gallery>
    </entities>
    </inchoo_gallery_resource>
     </models>
     <resources>
      <inchoo_gallery_setup>
       <setup>
        <module>Inchoo_Gallery</module>
       </setup>
      </inchoo_gallery_setup>
     </resources>
</global>
<!--....-->

Model:

class Inchoo_Gallery_Model_Gallery extends
Mage_Core_Model_Abstract implements JsonSerializable {
 public function _construct() {
  $this->_init( 'inchoo_gallery/gallery' );
	}
 
 public function jsonSerialize() {
  return [
    'id'               => $this->getId(),
    'file'             => $this->getFile(),
    'label'            => $this->getLabel(),
    'position'         => $this->getPosition(),
    'disabled'         => $this->getIsDisabled(),
    'label_default'    => $this->getLabel(),
    'position_default' => $this->getPosition(),
    'disabled_default' => $this->getIsDisabled(),
    'url'              => Mage::getBaseUrl( Mage_Core_Model_Store::URL_TYPE_WEB ) . 'media/gallery/' . $this->getFile(),
    'removed'          => 0
		];
	}
}

Resource:

class Inchoo_Gallery_Model_Resource_Gallery
extends Mage_Core_Model_Resource_Db_Abstract
{
	public function _construct() {
		$this->_init('inchoo_gallery/gallery','id');
	}
}

Collection:

<?php
class Inchoo_Gallery_Model_Resource_Gallery_Collection
extends Mage_Core_Model_Resource_Db_Collection_Abstract
{
	public function _construct() {
		$this->_init('inchoo_gallery/gallery');
	}
}

Setup script:

<?php
$installer = $this;
$installer->startSetup();
 
$table = $installer->getConnection()
                   ->newTable( $installer->getTable( 'inchoo_gallery/gallery' ) )
                   ->addColumn( 'id', Varien_Db_Ddl_Table::TYPE_INTEGER, null,
	                   array(
		                   'identity' => true,
		                   'unsigned' => true,
		                   'nullable' => false,
		                   'primary'  => true,
	                   ), 'Value id' )
                   ->addColumn( 'cms_page_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null,
	                   array(
		                   'nullable' => false,
	                   ), 'Cms Page id' )
                   ->addColumn( 'position', Varien_Db_Ddl_Table::TYPE_INTEGER, null,
	                   array(
		                   'unsigned' => true,
		                   'nullable' => true,
	                   ), 'Position' )
                   ->addColumn( 'file', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255,
	                   array(
		                   'nullable' => false,
	                   ), 'File Name' )
                   ->addColumn( 'label', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255,
	                   array(
		                   'nullable' => true,
	                   ), 'Label' )
                   ->addColumn( 'is_disabled', Varien_Db_Ddl_Table::TYPE_BOOLEAN, null,
	                   array(
		                   'nullable' => true,
	                   ), 'Is Disabled' );
 
$installer->getConnection()->createTable( $table );
 
$installer->getConnection()->addForeignKey(
	$installer->getFkName(
		'inchoo_gallery/gallery',
		'cms_page_id',
		'cms/page',
		'page_id'
	),
	$installer->getTable( 'inchoo_gallery/gallery' ),
	'cms_page_id',
	$installer->getTable( 'cms/page' ),
	'page_id'
);
$installer->endSetup();

Step 4

Next we need to create our block and template file.

Config:

<!--....-->
 
<global>
<!--....-->
		<blocks>
			<inchoo_gallery>
				<class>Inchoo_Gallery_Block</class>
			</inchoo_gallery>
		</blocks>
</global>
<!--....-->

Create block:

<?php
class Inchoo_Gallery_Block_Adminhtml_Gallery
	extends Mage_Adminhtml_Block_Widget
	implements Mage_Adminhtml_Block_Widget_Tab_Interface {
	protected $_uploaderType = 'uploader/multiple';
 
	public function __construct() {
		parent::__construct();
		$this->setShowGlobalIcon( true );
		Mage_Adminhtml_Block_Template::__construct();
		$this->setTemplate( 'inchoo/gallery.phtml' );
	}
 
	protected function _prepareLayout() {
		$this->setChild( 'uploader',
			$this->getLayout()->createBlock( $this->_uploaderType )
		);
 
		$this->getUploader()->getUploaderConfig()
		     ->setFileParameterName( 'image' )
		     ->setTarget( Mage::getModel( 'adminhtml/url' )->addSessionParam()->getUrl( '*/cms_page/upload' ) );
 
		$browseConfig = $this->getUploader()->getButtonConfig();
		$browseConfig
			->setAttributes( array(
				'accept' => $browseConfig->getMimeTypesByExtensions( 'gif, png, jpeg, jpg' )
			) );
 
		return Mage_Adminhtml_Block_Template::_prepareLayout();
	}
 
	public function getUploader() {
		return $this->getChild( 'uploader' );
	}
 
	public function getUploaderHtml() {
		return $this->getChildHtml( 'uploader' );
	}
 
	public function getHtmlId() {
		return 'media_gallery_content';
	}
 
	public function getGallery() {
		return Mage::getModel( 'inchoo_gallery/gallery' )->getCollection()
		           ->addFieldToFilter( 'cms_page_id', array( 'eq' => $this->getRequest()->getParam( 'page_id' ) ) );
 
	}
 
	public function getImageTypes() {
		return array( 'image' => [ 'label' => 'Base Image', 'field' => 'post[image]' ] );
	}
 
	public function getImageTypesJson() {
		return Mage::helper( 'core' )->jsonEncode( $this->getImageTypes() );
	}
 
	public function getJsObjectName() {
		return 'media_gallery_contentJsObject';
	}
 
	public function getImagesJson() {
 
		$jsonFiles = '';
		$gallery   = $this->getGallery();
		foreach ( $gallery as $images ) {
			$jsonFiles = $jsonFiles . ',' . json_encode( $images );
		}
 
		return '[' . trim( $jsonFiles, ',' ) . ']';
	}
 
	/**
	 * Prepare label for tab
	 *
	 * @return string
	 */
	public function getTabLabel() {
		return Mage::helper( 'cms' )->__( 'Gallery' );
	}
 
	/**
	 * Prepare title for tab
	 *
	 * @return string
	 */
	public function getTabTitle() {
		return Mage::helper( 'cms' )->__( 'Gallery' );
	}
 
	/**
	 * Returns status flag about this tab can be showen or not
	 *
	 * @return true
	 */
	public function canShowTab() {
		return true;
	}
 
	/**
	 * Returns status flag about this tab hidden or not
	 *
	 * @return true
	 */
	public function isHidden() {
		return false;
	}
 
	/**
	 * Check permission for passed action
	 *
	 * @param string $action
	 *
	 * @return bool
	 */
	protected function _isAllowedAction( $action ) {
		return true;
	}
 
}

Create template file :

Create template. This template is based on gallery.phtml which is used in Mage_Adminhtml_Block_Catalog_Product_Helper_Form_Gallery_Content block. I modified this template to fit for our purpose.

<?php
/* @var $this Inchoo_Gallery_Block_Adminhtml_Gallery */
?>
 
<div class="entry-edit-head">
    <h4 class="icon-head head-edit-form fieldset-legend">Images</h4>
    <div class="form-buttons"></div>
</div>
<div class="fieldset fieldset-wide" id="<?php echo $this->getHtmlId() ?>">
    <div class="hor-scroll">
        <table class="form-list" style="width: 100%;" cellspacing="0">
            <tbody>
            <tr>
                <td class="value" colspan="3" style="width: 100%;">
                    <div id="<?php echo $this->getHtmlId() ?>">
                        <ul class="messages">
                            <li class="notice-msg">
                                <ul>
                                    <li>
										<?php echo Mage::helper( 'catalog' )->__( 'Image type and information need to be specified for each store view.' ); ?>
                                    </li>
                                </ul>
                            </li>
                        </ul>
                        <div class="grid">
                            <table cellspacing="0" class="data border" id="<?php echo $this->getHtmlId() ?>_grid"
                                   width="100%">
                                <col width="1"/>
                                <col/>
                                <col width="70"/>
								<?php foreach ( $this->getImageTypes() as $typeId => $type ): ?>
                                    <col/>
								<?php endforeach; ?>
                                <col width="70"/>
                                <col width="70"/>
                                <thead>
                                <tr class="headings">
                                    <th><?php echo Mage::helper( 'catalog' )->__( 'Image' ) ?></th>
                                    <th><?php echo Mage::helper( 'catalog' )->__( 'Label' ) ?></th>
                                    <th><?php echo Mage::helper( 'catalog' )->__( 'Sort Order' ) ?></th>
									<?php foreach ( $this->getImageTypes() as $typeId => $type ): ?>
                                        <th><?php echo $type['label'] ?></th>
									<?php endforeach; ?>
                                    <th><?php echo Mage::helper( 'catalog' )->__( 'Exclude' ) ?></th>
                                    <th class="last"><?php echo Mage::helper( 'catalog' )->__( 'Remove' ) ?></th>
                                </tr>
                                </thead>
                                <tbody id="<?php echo $this->getHtmlId() ?>_list">
                                <tr id="<?php echo $this->getHtmlId() ?>_template" class="template no-display">
                                    <td class="cell-image">
                                        <div class="place-holder"
                                             onmouseover="<?php echo $this->getJsObjectName(); ?>.loadImage('__file__')">
                                            <span><?php echo Mage::helper( 'catalog' )->__( 'Roll Over for preview' ) ?></span>
                                        </div>
                                        <img src="<?php echo $this->getSkinUrl( 'images/spacer.gif' ) ?>" width="100"
                                             style="display:none;" alt=""/></td>
                                    <td class="cell-label"><input type="text" class="input-text"
                                                                  onkeyup="<?php echo $this->getJsObjectName(); ?>.updateImage('__file__')"
                                                                  onchange="<?php echo $this->getJsObjectName(); ?>.updateImage('__file__')"/>
                                    </td>
                                    <td class="cell-position"><input type="text" class="input-text validate-number"
                                                                     onkeyup="<?php echo $this->getJsObjectName(); ?>.updateImage('__file__')"
                                                                     onchange="<?php echo $this->getJsObjectName(); ?>.updateImage('__file__')"/>
                                    </td>
									<?php foreach ( $this->getImageTypes() as $typeId => $type ): ?>
                                        <td class="cell-<?php echo $typeId ?> a-center"><input type="radio"
                                                                                               name="<?php echo $type['field'] ?>"
                                                                                               onclick="<?php echo $this->getJsObjectName(); ?>.setProductImages('__file__')"
                                                                                               value="__file__"/></td>
									<?php endforeach; ?>
                                    <td class="cell-disable a-center"><input type="checkbox"
                                                                             onclick="<?php echo $this->getJsObjectName(); ?>.updateImage('__file__')"/>
                                    </td>
                                    <td class="cell-remove a-center last"><input type="checkbox"
                                                                                 onclick="<?php echo $this->getJsObjectName(); ?>.updateImage('__file__')"/>
                                    </td>
                                </tr>
                                <tr id="<?php echo $this->getHtmlId() ?>-image-0">
                                    <td class="cell-image"><?php echo Mage::helper( 'catalog' )->__( 'No image' ) ?></td>
                                    <td class="cell-label"><input type="hidden"/>&nbsp;</td>
                                    <td class="cell-position"><input type="hidden"/>&nbsp;</td>
									<?php foreach ( $this->getImageTypes() as $typeId => $type ): ?>
                                        <td class="cell-<?php echo $typeId ?> a-center"><input type="radio"
                                                                                               name="<?php echo $type['field'] ?>"
                                                                                               onclick="<?php echo $this->getJsObjectName(); ?>.setProductImages('no_selection')"
                                                                                               value="no_selection"/>
                                        </td>
									<?php endforeach; ?>
                                    <td class="cell-disable"><input type="hidden"/>&nbsp;</td>
                                    <td class="cell-remove last"><input type="hidden"/>&nbsp;</td>
                                </tr>
                                </tbody>
                                <tfoot>
                                <tr>
                                    <td colspan="100" class="last" style="padding:8px">
										<?php echo Mage::helper( 'catalog' )->__( 'Maximum width and height dimension for upload image is %s.', Mage::getStoreConfig( Mage_Catalog_Helper_Image::XML_NODE_PRODUCT_MAX_DIMENSION ) ); ?>
										<?php echo $this->getUploaderHtml() ?>
                                    </td>
                                </tr>
                                </tfoot>
                            </table>
                        </div>
                    </div>
                    <input type="hidden" id="<?php echo $this->getHtmlId() ?>_save" name="post[media_gallery][images]"
                           value="<?php echo $this->escapeHtml( $this->getImagesJson() ) ?>"/>
                    <input type="hidden" id="<?php echo $this->getHtmlId() ?>_save_image"
                           name="post[media_gallery][values]" value="{}"/>
                    <script type="text/javascript">
                        //<![CDATA[
                        var <?php echo $this->getJsObjectName(); ?> =
                        new Product.Gallery('<?php echo $this->getHtmlId() ?>', <?php echo $this->getImageTypesJson() ?>);
                        //]]>
                    </script>
                </td>
            </tr>
            </tbody>
        </table>
    </div>
</div>

Step 5

To add our block as new gallery tab we need to create new layout file and use existing action handle and reference to block which contain all tabs blocks in cms page.

<layout>
	<adminhtml_cms_page_edit>
		<reference name="cms_page_edit_tabs">
			<block type="inchoo_gallery/adminhtml_gallery" name="cms_page_edit_tab_gallery"/>
			<action method="addTab">
				<name>gallery_section</name>
				<block>cms_page_edit_tab_gallery</block>
			</action>
		</reference>
	</adminhtml_cms_page_edit>
</layout>

Step 6

Finally we need to add logic for saving our gallery into database. We need to override Mage_Adminhtml_Cms_PageController and create our logic for saving and upload the data.

Config:

<config>
<!--....-->
	<admin>
		<routers>
			<adminhtml>
				<args>
					<modules>
						<inchoo_gallery before="Mage_Adminhtml">Inchoo_Gallery_Adminhtml_Rewrite</inchoo_gallery>
					</modules>
				</args>
			</adminhtml>
 
		</routers>
	</admin>
<!--....-->
</config>

Create controller:

<?php
require_once Mage::getModuleDir( 'controllers', 'Mage_Adminhtml' ) . DS . 'Cms' . DS . 'PageController.php';
 
class Inchoo_Gallery_Adminhtml_Rewrite_Cms_PageController extends Mage_Adminhtml_Cms_PageController {
 
	public function saveAction(){
	//Add this code after $model->save
 
				$request    = $this->getRequest()->getPost();
				$formImages = json_decode( $request['post']['media_gallery']['images'] );
 
				foreach ( $formImages as $galleryImage ) {
 
					//if new image is uploaded
					if ( ! isset( $galleryImage->id ) ) {
 
						if ( $galleryImage->removed == 1 ) {
							$filePath = Mage::getBaseDir( 'media' ) . DS . 'gallery' . DS . $galleryImage->file;
							if ( file_exists( $filePath ) ) {
								unlink( $filePath );
							}
 
						} else {
							$galleryModel = Mage::getModel( 'inchoo_gallery/gallery' );
 
							$galleryModel->setCmsPageId( $model->getId() );
							$galleryModel->setFile( $galleryImage->file );
							$galleryModel->setPosition( $galleryImage->position );
							$galleryModel->setLabel( $galleryImage->label );
							$galleryModel->setIsDisabled( $galleryImage->disabled );
 
							$galleryModel->save();
						}
 
					}
 
					if ( isset( $galleryImage->id ) ) {
						if ( $galleryImage->removed == 1 ) {
							$filePath = Mage::getBaseDir( 'media' ) . DS . 'gallery' . DS . $galleryImage->file;
 
							$galleryModel = Mage::getModel( 'inchoo_gallery/gallery' );
							$galleryModel->setId( $galleryImage->id );
							$galleryModel->delete();
 
							if ( file_exists( $filePath ) ) {
								unlink( $filePath );
							}
 
						} else {
 
							$isModified = false;
 
							if ( $galleryImage->label_default != $galleryImage->label ) {
								$isModified = true;
							}
 
							if ( $galleryImage->position_default != $galleryImage->position ) {
								$isModified = true;
							}
 
							if ( $galleryImage->disabled_default != $galleryImage->disabled ) {
								$isModified = true;
							}
 
							if ( $isModified ) {
								$galleryModel = Mage::getModel( 'inchoo_gallery/gallery' );
								$galleryModel->setId( $galleryImage->id );
								$galleryModel->setPosition( $galleryImage->position );
								$galleryModel->setIsDisabled( $galleryImage->disabled );
								$galleryModel->setLabel( $galleryImage->label );
 
								$galleryModel->save();
 
							}
						}
 
					}
 
				}
 
	//other code
       }
	public function uploadAction() {
		try {
			$uploader = new Varien_File_Uploader( 'image' );
			$uploader->setAllowedExtensions( array( 'jpg', 'jpeg', 'gif', 'png' ) );
			$uploader->setAllowRenameFiles( true );
			$uploader->setFilesDispersion( false );
			$path   = Mage::getBaseDir( 'media' ) . DS . 'gallery';
			$result = $uploader->save( $path );
			/**
			 * Workaround for prototype 1.7 methods "isJSON", "evalJSON" on Windows OS
			 */
			$result['tmp_name'] = str_replace( DS, "/", $result['tmp_name'] );
			$result['path']     = str_replace( DS, "/", $result['path'] );
 
			$result['url']    = Mage::getBaseUrl( Mage_Core_Model_Store::URL_TYPE_WEB ) . 'media/gallery/' . $result['name'];
			$result['cookie'] = array(
				'name'     => session_name(),
				'value'    => $this->_getSession()->getSessionId(),
				'lifetime' => $this->_getSession()->getCookieLifetime(),
				'path'     => $this->_getSession()->getCookiePath(),
				'domain'   => $this->_getSession()->getCookieDomain()
			);
 
		} catch ( Exception $e ) {
			$result = array(
				'error'     => $e->getMessage(),
				'errorcode' => $e->getCode()
			);
		}
		$this->getResponse()->setBody( Mage::helper( 'core' )->jsonEncode( $result ) );
	}
 
}

And that’s it! Happy coding!

Antun Martinovic

- Backend Developer

Programming enthusiast, bibliophil, Arduino aficionado

Read more posts by Antun / Visit Antun's profile

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.