MANUAL
All the doumentation of this web site is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License
How to build a complete plugin for W3StudioCMS
In the previous tutorial we have seen how to build an helper based on an existing javascript tool. Now it could be a good idea to learn how to add and use it in W3StudioCMS.
In the next paragraphs you will learn how to build a content and the editor to manage it from the CMS, then you will learn how to use the configuration files to configure the plugin and how to define all the client-side objects which manages the ajax transactions for the content's editor.
Build the content manager
Under the lib folder create a new directory and call it w3studiocms. This is not mandatory for the plugin, but it is useful to separate the helper which can be used in each symfony project from the objects related to the CMS.
Inside this folder create a new file and call it BaseW3sContentManagerMaskedGallery.class.php. This object will inherit from the w3sContentManager class, which is an abstract object that inherits from the base object that manages contents in W3StudioCMS and will contain all the mandatory methods required by the father object. Extending this object instead the real object, give you the chance to change the base object whitout coping all the core object's class.
Add the following code to the class:
abstract class w3sContentManagerMaskedGallery extends w3sContentManager
{
}
As you may notice this class is declared abstract. This object will contain all the methos needed but it cannot be called directly and another object has to be created to implement this class but this will be made next.
I don't spend a lot of words to explain how this base object works because it is explained in detail in this chapter. Add the following code to the class:
public function getDefaultValue()
{
return 'galleryName=w3s_image_gallery_1&imagesPath=screenshots/&type=normal&galleryMask=oblo.png&galleryUrl=&galleryColor=white&galleryLoader=loader_white.gif&loaderOpacity=&loader=true&fadeTime=400&slideTimer=5000&changeOnClick=false&nextPath=next.png&prevPath=prev.png&navId=';
}
public function getInteractiveMenu()
{
$editor = sprintf('activeEditor = new maskedGalleryEditor(%s, %s);', $this->content->getId(), $this->getContentMapping()->getSlotId());
return sprintf($this->defaultInteractiveMenu, $this->content->getId(), $this->getContentMapping()->getSlotId(), $editor);
}
public function getDisplayContentForEditorMode()
{
return '<img src="/w3sExtensionMbMaskedGalleryPlugin/images/w3s-masked-gallery.png" width="85" height="14" />';
}
public function getDisplayContentForPreviewMode()
{
}
public function getDisplayContentForPublishMode()
{
}
These are all the required methods by the parent object.
Some words are needed for the getDefaultValue method. The content that will be saved to the database, for the masked gallery, is a serialized form, so the default params should reflect the string produced when a form is serialized.
You should think that the default content could not be defined because the helper has its own default values, but this is not correct because each time W3StudioCMS initializes the form, it passes the value of content to the form itself, so if the default will not be defined, the first time the editor is opened, the form with the parameters will be empty.
The getDisplayContentForPreviewMode has to show the gallery in preview mode. Add the following code to that method:
$options = array();
$params = explode('&', urldecode($this->content->getContent()));
foreach($params as $param)
{
$values = explode('=', $param);
$options[$values[0]] = sprintf('%s', $values[1]);
}
sfContext::getInstance()->getConfiguration()->loadHelpers(array('Tag', 'Asset', 'w3sMbMaskedGallery'));
return sprintf('<center>%s</center>', initMbMaskedGallery($options));
The options are retrieved from the content and then they are formatted. The helper w3sMbMaskedGallery is called and then it is processed and returned. The getDisplayContentForPublishMode is quite similar. Add this code to that function:
$stringOptions = '';
$options = array();
$params = explode('&', urldecode($this->content->getContent()));
foreach($params as $param)
{
$values = explode('=', $param);
$options[$values[0]] = sprintf('%s', $values[1]);
}
foreach($options as $param => $options)
{
$stringOptions .= sprintf('"%s" => "%s",', $param, $options);
}
$content = '<?php use_helper(\'w3sMbMaskedGallery\');
$options = array(' . $stringOptions . ');
echo sprintf(\'<center>%s</center>\', initMbMaskedGallery($options));?>';
return $content;
You should notice that the options are formatted exactly as the ones in the getDisplayContentForPreviewMode. It is a good idea to refactor this code. Add the following function to the class:
protected function setOptions()
{
$options = array();
$params = explode('&', urldecode($this->content->getContent()));
foreach($params as $param)
{
$values = explode('=', $param);
$options[$values[0]] = sprintf('%s', $values[1]);
}
return $options;
}
Refactor now the previous methods as follows:
public function getDisplayContentForPreviewMode()
{
sfContext::getInstance()->getConfiguration()->loadHelpers(array('Tag', 'Asset', 'w3sMbMaskedGallery'));
return sprintf('<center>%s</center>', initMbMaskedGallery($this->setOptions()));
}
public function getDisplayContentForPublishMode()
{
$stringOptions = '';
$options = $this->setOptions();
foreach($options as $param => $options)
{
$stringOptions .= sprintf('"%s" => "%s",', $param, $options);
}
$content = '<?php use_helper(\'w3sMbMaskedGallery\');
$options = array(' . $stringOptions . ');
echo sprintf(\'<center>%s</center>\', initMbMaskedGallery($options));?>';
return $content;
}
This function has to return the php code which is inserted into the published page, so the options array has to be converted into a string. The static javascripts in the preview mode are automatically load from W3StudioCMS when the editor is loaded.
As explained at the beginning of this paragraph, this object cannot be used directly but it requires another object that inherits from it. Create a new file and call it w3sContentManagerMaskedGallery.class.php, open it and add the following code:
class w3sContentManagerMaskedGallery extends BaseW3sContentManagerMaskedGallery
{
}
The object to manage the new content is now done.
Build the content editor
To manage the params that have to be passed to the gallery we will build a form which can be used to fill all the parameters required by the mb-masked-gallery. Inside the w3studiocms folder create a new directory and call it form, open it and create a new file inside and call it w3sMaskedGalleryForm.php. Insert the following code into the file just created to define the form.
class w3sMaskedGalleryForm extends sfForm
{
public function configure()
{
}
}
The form has to receive as input the current options saved in the database to fill the form itself when the editor is opened. To do this add the following code inside the form:
protected $values;
public function __construct($options = null)
{
$this->values = ($options != null) ? $options : array("galleryName" => "w3s_image_gallery_1",
"imagesPath" => 'screenshots/',
"type" => 'normal',
"galleryMask" => 'monitor.png',
"galleryUrl" => '',
"galleryColor" => 'white',
"galleryLoader" => 'loader_white.gif',
"loaderOpacity" => 1,
"loader" => true,
"fadeTime" => 400,
"slideTimer" => 5000,
"changeOnClick" => true,
"nextPath" => 'next.png',
"prevPath" => 'prev.png',
"navId" => '',);
parent::__construct();
}
The form's constructor accepts as optional parameter an array of options when it is passed to the constructor.
When the array is passed it is assigned to the protected variable $values, which defines the vaules the form will use, otherwise a default array of options is defined and used. Now we will build the form.
Inside the configure method, insert this code:
$this->setWidgets(array(
'galleryName' => new sfWidgetFormInput(),
'imagesPath' => new sfWidgetFormInput(),
'type' => new sfWidgetFormSelect(array('choices' => array('normal' => 'normal', 'random' => 'random'))),
'galleryMask' => new sfWidgetFormSelect(),
'galleryUrl' => new sfWidgetFormInput(),
'galleryColor' => new sfWidgetFormInput(),
'galleryLoader' => new sfWidgetFormSelect(),
'loaderOpacity' => new sfWidgetFormInput(),
'loader' => new sfWidgetFormSelect(),
'fadeTime' => new sfWidgetFormInput(),
'slideTimer' => new sfWidgetFormInput(),
'changeOnClick' => new sfWidgetFormSelect(),
'nextPath' => new sfWidgetFormSelect(),
'prevPath' => new sfWidgetFormSelect(),
'navId' => new sfWidgetFormInput(),
));
This is the base skeleton we use to define the form's parameters that are passed to the gallery.
At the moment in that form the only parameter that gets a values is the type param. Looking into the mb-masked-gallery documentation you will discover that some parameters want a boolean value.
To fit this ones we will define an array of boolean options. Insert the following code before the widgets declaration:
$booleans = array('true' => 'true', 'false' => 'false');
then fit the parameters that must be booleans as shown below:
$this->setWidgets(array(
'galleryName' => new sfWidgetFormInput(),
'imagesPath' => new sfWidgetFormInput(),
'type' => new sfWidgetFormSelect(array('choices' => array('normal' => 'normal', 'random' => 'random'))),
'galleryMask' => new sfWidgetFormSelect(),
'galleryUrl' => new sfWidgetFormInput(),
'galleryColor' => new sfWidgetFormInput(),
'galleryLoader' => new sfWidgetFormSelect(),
'loaderOpacity' => new sfWidgetFormInput(),
'loader' => new sfWidgetFormSelect(array('choices' => $booleans)),
'fadeTime' => new sfWidgetFormInput(),
'slideTimer' => new sfWidgetFormInput(),
'changeOnClick' => new sfWidgetFormSelect(array('choices' => $booleans)),
'nextPath' => new sfWidgetFormSelect(),
'prevPath' => new sfWidgetFormSelect(),
'navId' => new sfWidgetFormInput(),
));
There are some parameters that wants an image, as the galleryMask, the galleryLoader, the nextPath and the prevPath params.
To define these paths we could choose to hardcode them, but this is not really a good idea. To avoid this problem we could define some parameters at application level.
Create a new folder under the plugin's directory and call it config, then create a new app.yml file inside. Add the following code to that file:
all:
.settings:
gallery_masks_images_dir: /w3sExtensionMbMaskedGalleryPlugin/images/mask
gallery_loaders_images_dir: /w3sExtensionMbMaskedGalleryPlugin/images/loader
gallery_controls_images_dir: /w3sExtensionMbMaskedGalleryPlugin/images/controls
Each option gets the absolute path where the images are placed. In this way it's simple to change the paths for the mask, loaders and controls, overriding the param at application level.
Now in the form class add the following code:
$masksPath = sfConfig::get('sf_web_dir') . sfConfig::get('app_gallery_masks_images_dir');
$masks = sfFinder::type('file')->maxdepth(0)->relative()->name('*.png')->sort_by_name()->in($masksPath);
$maskChoiches = (count($masks) > 0) ? array_combine($masks, $masks) : array();
We've just defined the full path for the mask folder's images, then we looked for all the images contained inside that folder, using the sfFinder object.
After that, the images, that have been found, are combined into an array where keys and values have the same value. In this way when a user check an option, the value expected by the mb-gallery is passed to the form. Now the same procedure will be made for other folders.
Replace the previous code with this one:
$masksPath = sfConfig::get('sf_web_dir') . sfConfig::get('app_gallery_masks_images_dir');
$loaderPath = sfConfig::get('sf_web_dir') . sfConfig::get('app_gallery_loaders_images_dir');
$controlsPath = sfConfig::get('sf_web_dir') . sfConfig::get('app_gallery_controls_images_dir');
$masks = sfFinder::type('file')->maxdepth(0)->relative()->name('*.png')->sort_by_name()->in($masksPath);
$loaders = sfFinder::type('file')->maxdepth(0)->relative()->name('*.gif')->name('*.png')->name('*.jpg')->name('*.jpeg')->sort_by_name()->in($loaderPath);
$controls = sfFinder::type('file')->maxdepth(0)->relative()->name('*.gif')->name('*.png')->name('*.jpg')->name('*.jpeg')->sort_by_name()->in($controlsPath);
$maskChoiches = (count($masks) > 0) ? array_combine($masks, $masks) : array();
$loaderChoiches = (count($loaders) > 0) ? array_combine($loaders, $loaders) : array();
$controlChoices = (count($controls) > 0) ? array_combine($controls, $controls) : array();
If any image is found, an empty array is passed to the sfWidgetFormSelect.
Change now the form as follows to set the values we've just defined:
$this->setWidgets(array(
'galleryName' => new sfWidgetFormInput(),
'imagesPath' => new sfWidgetFormInput(),
'type' => new sfWidgetFormSelect(array('choices' => array('normal' => 'normal', 'random' => 'random'))),
'galleryMask' => new sfWidgetFormSelect(array('choices' => $maskChoiches)),
'galleryUrl' => new sfWidgetFormInput(),
'galleryColor' => new sfWidgetFormInput(),
'galleryLoader' => new sfWidgetFormSelect(array('choices' => $loaderChoiches)),
'loaderOpacity' => new sfWidgetFormInput(),
'loader' => new sfWidgetFormSelect(array('choices' => $booleans)),
'fadeTime' => new sfWidgetFormInput(),
'slideTimer' => new sfWidgetFormInput(),
'changeOnClick' => new sfWidgetFormSelect(array('choices' => $booleans)),
'nextPath' => new sfWidgetFormSelect(array('choices' => $controlChoices)),
'prevPath' => new sfWidgetFormSelect(array('choices' => $controlChoices)),
'navId' => new sfWidgetFormInput(),
));
Now we have to tell the form to use the values stored in the $values variable. Replace the form with the following code:
$this->setWidgets(array(
'galleryName' => new sfWidgetFormInput(array('default' => $this->values['galleryName'])),
'imagesPath' => new sfWidgetFormInput(array('default' => $this->values['imagesPath'])),
'type' => new sfWidgetFormSelect(array('choices' => array('normal' => 'normal', 'random' => 'random'), 'default' => $this->values['type'])),
'galleryMask' => new sfWidgetFormSelect(array('choices' => $maskChoiches, 'default' => $this->values['galleryMask'])),
'galleryUrl' => new sfWidgetFormInput(array('default' => $this->values['galleryUrl'])),
'galleryColor' => new sfWidgetFormInput(array('default' => $this->values['galleryColor'])),
'galleryLoader' => new sfWidgetFormSelect(array('choices' => $loaderChoiches, 'default' => $this->values['galleryLoader'])),
'loaderOpacity' => new sfWidgetFormInput(array('default' => $this->values['loaderOpacity'])),
'loader' => new sfWidgetFormSelect(array('choices' => $booleans, 'default' => $this->values['loader'])),
'fadeTime' => new sfWidgetFormInput(array('default' => $this->values['fadeTime'])),
'slideTimer' => new sfWidgetFormInput(array('default' => $this->values['slideTimer'])),
'changeOnClick' => new sfWidgetFormSelect(array('choices' => $booleans, 'default' => $this->values['changeOnClick'])),
'nextPath' => new sfWidgetFormSelect(array('choices' => $controlChoices, 'default' => $this->values['nextPath'])),
'prevPath' => new sfWidgetFormSelect(array('choices' => $controlChoices, 'default' => $this->values['prevPath'])),
'navId' => new sfWidgetFormInput(array('default' => $this->values['navId'])),
));
Now every option is filled with the value saved in the database.
The last thing to do is to rearrange the labels for a better display on screen. Add this code under the form initialization:
$this->widgetSchema->setLabels(array(
'galleryName' => 'Gallery name',
'imagesPath' => 'Images path',
'galleryMask' => 'Gallery mask',
'galleryUrl' => 'Gallery url',
'galleryColor' => 'Gallery color',
'galleryLoader' => 'Gallery loader',
'loaderOpacity' => 'Gallery opacity',
'fadeTime' => 'Fade time',
'slideTimer' => 'Slide time',
'changeOnClick' => 'Change OnClick',
'nextPath' => 'Next path',
'prevPath' => 'Prev path',
'navId' => 'Nav id',
));
Now the form is completed and ready to be used.
Build the editor
To manage our content an editor has to be defined. Inside the w3studiocm folder create a new file and call it BaseW3sContentManagerMaskedGallery.class.php. This object will inherit from the BaseW3sEditorContents class, which is the base object to build an editor to manage contents.
Add the following code to the class:
abstract class BaseW3sEditorContentsMaskedGallery extends BaseW3sEditorContents
{
}
The html skeleton is defined by a protected variable called $editorSkeleton which is always required. If you try to define an editor without declaring that variable, you will get an exception.
Here is the code:
protected $editorSkeleton =
'<div id="w3s_masked_gallery_container">
<div class="w3s_toolbar"><table cellspacing="0" cellpadding="0">%s</table></div>
<form id="w3s_masked_gallery_form">
<table border=0>
%s
<tr><th></th><td><input type="submit" value="Save" onclick="activeEditor.edit();return false;" class="button" /></td></tr>
</table>
</form>
</div>';
This editor will have a toolbar to upload all the images for the gallery itself, for the masks, loaders and controls and the form we've just created to let the user to setup the gallery.
Define the editor's toolbar
In W3StudioCMS a toolbar is a serie of buttons which can be placed in an horizontal or in a vertical container. Several parts of the W3StudioCMS' interface are made by toolbars: the Interactive menu's left buttons, the Menu manager, the Actions menu and all the toolbars used by editors.
A toolbar is made by an array of buttons and it is defined in a dedicated configuration file called toolbar.yml. This configuration file is handled by the w3sToolbarConfigHandler which process the configuration file and stores the result into the cache.
To define the toolbar file used by this editor, create a new file under the config folder of the plugin and call it toolbar.yml. Then paste inside this code:
maskedGallery:
buttons:
0:
id: command_1
image: /w3sExtensionMbMaskedGalleryPlugin/images/upload.png
caption: Upload images
action: W3sFileUploader = new w3sFileUploaderGallery('<?php echo sfConfig::get('sf_web_dir') . w3sToolkit::checkLastDirSeparator(sfConfig::get('app_gallery_base_images_dir'))?>' + $('imagesPath').value); W3sFileUploader.show();
actionParams:
id: w3s_file_uploader_btn_1
class: image_button
1:
id: command_2
image: /w3sExtensionMbMaskedGalleryPlugin/images/upload.png
caption: Upload a mask
action: W3sFileUploader = new w3sFileUploaderGallery('<?php echo sfConfig::get('sf_web_dir') . sfConfig::get('app_gallery_masks_images_dir')?>'); W3sFileUploader.show('mask', 'galleryMask');
actionParams:
id: w3s_file_uploader_btn_2
class: image_button
2:
id: command_3
image: /w3sExtensionMbMaskedGalleryPlugin/images/upload.png
caption: Upload a loader
action: W3sFileUploader = new w3sFileUploaderGallery('<?php echo sfConfig::get('sf_web_dir') . sfConfig::get('app_gallery_loaders_images_dir')?>'); W3sFileUploader.show('loader', 'galleryLoader');
actionParams:
id: w3s_file_uploader_btn_3
class: image_button
3:
id: command_4
image: /w3sExtensionMbMaskedGalleryPlugin/images/upload.png
caption: Upload a control
action: W3sFileUploader = new w3sFileUploaderGallery('<?php echo sfConfig::get('sf_web_dir') . sfConfig::get('app_gallery_controls_images_dir')?>'); W3sFileUploader.show('controls', 'galleryControls');
actionParams:
id: w3s_file_uploader_btn_4
class: image_button
maskedGallery is the name of the toolbar and will be used to retrieve the toolbar itself. There are four buttons which will be rendered inside the toolbar. Each button uses some options which are defined in the w3sButton class: give it a look to learn how the parameters you can use to draw your own buttons.
The button's action param could be setted to point a link or a it could be a call to a javascript function, exactly as it is for all these buttons. In fact each button will initialize a new file uploader. We will learn in the last part of this tutorial how to implement it.
In the toolbar it is used a param which has not already be defined and it is the app_gallery_base_images_dir which defines the base path where the images are stored.
Add the following code to the app.yml file:
gallery_base_images_dir: /uploads/assets
gallery_base_images_dir defines the base path for images under the uploads/assets folder.
Complete the editor
At this point we've got all the elementes required to draw the editor. To render the toolbar a new protected function will be defined.
Here is the code:
protected function drawToolbar()
{
include(sfContext::getInstance()->getConfigCache()->checkConfig('config/toolbar.yml'));
$toolbar = new w3sToolbarHorizontal();
$toolbar->setToolbar($toolbars['maskedGallery']);
return $toolbar->renderToolbar();
}
The include call will retrieve from the cache all the defined toolbars, which are stored as an array, and the
$toolbars['maskedGallery']
instruction will retrieve the toolbar you want to get. At last, to render the editor, insert the following code:
public function drawEditor()
{
$options = array();
$params = explode('&', urldecode($this->content->getContent()));
foreach($params as $param)
{
$values = explode('=', $param);
$options[$values[0]] = sprintf('%s', $values[1]);
}
$gallery = new w3sMaskedGalleryForm($options);
return sprintf($this->editorSkeleton, $this->drawToolbar(),
$gallery->render());
}
The first operation made consist to retrieve the saved params from the currents content and to transform then into an array which will be passed to the form, then the editor is drawed rendering the toolbar and the form.
As we made for the content, now we create the real object that manages the editor. Create a new file under the w3studiocms folder, call it w3sEditorContentsMaskedGallery.class.php and paste this code inside:
class w3sEditorContentsMaskedGallery extends BaseW3sEditorContentsMaskedGallery
{
}
Add the editor to Actions menu
To add th editor to the actions menu you have to edit the toolbar file as shown below:
actionMenu:
buttons:
0:
image: /w3sExtensionMbMaskedGalleryPlugin/images/add_gallery.png
caption: Insert a masked gallery
action: W3sContent.addContent('MaskedGallery')
imageTextRelation: 0
imageParams:
size: 32x32
This code adds the new button to the actions menu. It is well explained in this tutorial.

