sfInstantCMS Plugin
Author
John L. Singleton
WordHustler? LLC
jsingleton@wordhustler.com
http://www.wordhustler.com
Overview
The InstantCMS plugin will enable you to quickly create a custom CMS without writing a single line of database related code. This plugin was inspired by the Zope application server and works by creating a system that can create mutable objects on the fly. You simply define the structure of a page element at runtime and manipulate it with the built in helper functions.
The InstantCMS plugin introduces two primatives:
An Instant Property is a single-valued "one off" value that might appear on a web page. For example, the blurb of text on the "about us" page.
An Instant Object is a mutable type that may contain any number of fields and may contain children. For example, you might use an Instant Object to represent a catalog listing's description, image, and title.
Simply defining either of these primitives creates an editable region in the page that a user can update. You can render the following types of elements with InstantCMS:
- Updatable Text Fields
- Updatable Text Areas
- Updatable Images (with a media browser)
- Updatable File Download Links
- Updatable Repeated Regions (eg, a product catalog)
Note that all regions rendered with an InstantObject? support CRUD via the sfInstantCMS helper functions. InstantProperty? regions only support updating. See below for details.
InstantCMS makes this and many more scenarios a snap. Using InstantCMS alongside an existing application you will be able to completely isolate your content layer from your application, avoiding painful integration with 3rd party CMSs.
Performance Notes
Note that InstantCMS is SELECT-heavy. To allow objects to be mutable, I have opted for a SELECT, rather than JOIN strategy. This is smart for smaller sites with lighter traffic, since CPU time will likely be more available than RAM. InstantCMS is geared toward smaller sites that will not manage thousands of objects. InstantCMS works best for the site that has certain regions that must be updated dynamically. Bio pages, document archives, media managers, newletters, and small product catalogs are examples of applications that InstantCMS was designed for.
InstantCMS in Action
Figure 1 - A screenshot showing an updatable "Staff Bio" region. The controls allow you to delete the object or change its order it the persistent store
Requirements
Instant CMS Requires:
- The wonderful sfMediaLibrary plugin for image/file management.
- A Database. InstantCMS has been tested with MySQL, but should work with any symfony-compatible datasource.
Installation
To install sfInstantCMS:
Listing 1 - Installing sfInstantCMS
symfony plugin-install http://plugins.symfony-project.com/sfInstantCMSPlugin symfony propel-build-model symfony cc
If you are upgrading, you should probably also do a "symfony propel-build-all," which will delete the current schema and reinitialize.
You must now enable the module. Do so by editing your application's settings.yml.
Listing 2 - Activating sfInstantCMS, in myapp/config/settings.yml
all:
.settings:
enabled_modules: [default, sfInstantCMS]
Optionally, have a look at the plugin's security.yml file, located in plugins/sfInstantCMSPlugin/modules/sfInstantCMS/config. After development is complete you will want to secure these actions.
Usage
The sfInstantCMS "PageEdit?" Helper gives you access to two types of helper functions: functions that operate on InstantProperty? objects and functions that operate on InstantObjects?.
Creating Editable Page Properties
The following PageEdit? helper functions will enable you to create editable properties on a page. There are functions for text areas, url fields, images and more.
- function editable_text_tag($name, $defaultValue, $editActive=false, $rows = 50, $cols=60)
Displays an editable region on a page named $name, with a default value of $defaultValue. If the property named $name does not exist it is created and initialized with value $defaultValue. $rows and $cols dictate how large the AJAX edit-in-place editor should be. The $editActive parameter controls the state of the control. If true, the area is enabled for editing. If false, the area renders as text.
- function editable_image_tag($name, $defaultValue, $editActive=false)
Displays an editable image area on the page named $name. When editing is active, you can select an image from the sfMediaLibrary asset picker.
- function editable_filedownload_tag($name, $defaultValue, $editActive=false, $linkText=null)
Displays a "file download" link on the page named $name. The optional parameter $linkText overrides the default text for the link, which is the basename() of the file. In edit mode you can set the file to download using the sfMediaLibrary chooser.
- function editable_url_tag($name, $defaultValue, $editActive=false, $rows = 1, $cols=30)
Creates a clickable URL field on a page. In edit mode you can set the URL.
- function editable_mailto_tag($name, $defaultValue, $editActive=false, $rows = 1, $cols=30)
Creates a clickable MAILTO tag on a page. In edit mode you can configure the email address that the link should point to.
- function editable_page_tag($editBegin, $editEnd, $isEditing=false)
A convenience function that displays a small lock icon on the page. By creating two actions (or one, with different request parameters) you can create a "locked" version of the page or an unlocked version of the page. The $editBegin parameter specifies the URL that directs the user to an editable version of this page. (When the click to "unlock" the page.) The $editEnd parameter specifies a URL to send the user to when they click the lock again, thusly ending the editing process.
- function get_editable_value($name, $defaultValue)
Gets a InstantProperty? by name. This function is rarely used in end user code but is provided for convenience. The return type of this object is a string representing the value stored in $name.
Page Properties Examples
Listing 3 - An updatable Bio Page, in myapp/modules/mymodule/indexSuccess.php
<?php use_helper('PageEdit'); ?>
<?php use_helper('sfMediaLibrary') ?>
<h3> About US </h3>
<div align="center">
<?php editable_image_tag("AboutUS:MainImage", "", ($sf_request->getParameter('edit', 0)==0) ? false : true) ?>
</div>
<?php editable_text_tag("AboutUS:Main", "Default Text", ($sf_request->getParameter('edit', 0)==0) ? false : true) ?>
<?php echo editable_page_tag("/mymodule/index?edit=1", "/mymodule/index", ($sf_request->getParameter('edit', 0)==0) ? false : true) ?>
Note that in the above example we toggle editing on and off by passing an "edit" parameter in the request. Indeed this is the simplest and easiest to manage way to use sfInstantCMS.
Creating Editable Page Objects
The following PageEdit? helper functions will enable you to create editable objects on a page. There are functions for text areas, url fields, images and more. Unlike properties, InstantObjects? are for storing multiple types of the same object. For example, a product in a catalog might have the properties price, name, and description. Your catalog will likely have many of these objects. Objects can have an unlimited number of properties and may even be nested within other objects.
Many of the following functions work identically to their InstantProperty? counterparts. Please read the previous section for a detailed overview of those functions.
- function editable_text_object_tag($object, $name, $editActive=false, $rows=50, $cols=60)
Creates an editable region of text belonging to object $object, bound to the property $name.
- function editable_url_object_tag($object, $name, $editActive=false, $rows=1, $cols=30)
Creates an editable url field that is bound to the $name property of $object.
- function editable_mailto_object_tag($object, $name, $editActive=false, $rows=1, $cols=30)
Creates an editable mailto field that is bound to the $name property of $object.
- function editable_object_image_tag($object, $name, $editActive=false)
Creates an editable image field that is bound to the $name property of $object.
- function editable_object_filedownload_tag($object, $name, $editActive=false, $linkText=null)
Creates an editable file download link that is bound to the $name property of $object.
CRUD Functions
The following functions will assist you in managing collections of InstantObjects?. The interface to the InstantObject? persistent store is presented as a series of familiar CRUD-like functions. The main difference is that InstantCMS does not provide you will functions to operate directly on objects. Rather these functions typically produce controls that allow an end user to manipulate the object visually. Such support is redundant, as InstantObjects? (InstantMutableObjects?) are simply Propel objects that can be manipulated with the standard propel functions like save() and delete(). See get_editable and get_editables, below for more information on how to get a direct reference to an InstantObject?.
- function add_instant_object_tag($name, $text, $defaults, $returnURL, $withParent=null)
Creates a button on the current page that, when clicked, will add a new instance of object $name to the persistent store. The value will be initialized with the values from $defaults. The $text parameter sets the text the button should display. The $returnURL parameter indicates the URL the user should be directed to after the object is added. This is typically just the URL of the current page. The optional $withParent parameter sets the parent id of the newly created object.
InstantCMS expects $defaults to be an associative array, populated with the schema and default values of your object. For example, to describe an item in a catalog, you could initialize your object as follows:
Listing 4 - Initializing an InstantObject? with the add_instant_object_tag function.
<?php add_instant_object_tag("Product",
"Add New Product",
array(
"name" => "New Product",
"description" => "My Great Product",
"image" => "",
));
- function get_editable($id)
Gets the InstantObject? with the numeric id, $id. The return type is InstantMutableObject?. The InstantMutableObject? may be used with the other helper functions in this section but you can also access properties directly. You can get any named property by calling $object->getProperty('PROPERTY_NAME') on an instance of InstantMutableObject?. The function $object->getId() will return the numeric id of the InstantMutableObject?. Note that $id should exist before this function is called.
- function get_editables($type, $defaultValues, $withParent=null)
Gets an array of InstantObjects? of type $type. The $default values parameters array works identically to the description in add_instant_object_tag. Calling this function will implicitly create an InstantObject? of type "$type." If used with $withParent==null, this function will never return an array with less than 1 element of type $type. If used with $withParent!=null, this function will return an empty array if there are no objects that are children of $withParent.
- function move_instant_object_up_tag($object, $returnURL)
Displays a "move up" control that, when clicked, changes the position of $object in the output of the get_editables() function to be one position higher than its current position. Note that you should wrap this function in a block of code to check if editing is active before displaying.
- function move_instant_object_down_tag($object, $returnURL)
Displays a "move down" control that, when clicked, changes the position of $object in the output of the get_editables() function to be one position lower than its current position. Note that you should wrap this function in a block of code to check if editing is active before displaying.
- function delete_instant_object_tag($object, $text, $returnURL)
Displays a "delete" control that, when clicked, deletes $object from the persistent store. The $text parameter is obsolete. Note that you should wrap this function in a block of code to check if editing is active before displaying.
Page Object Examples
The following example demonstrates a typical usage of InstantObjects?. In the example below we create a simple editable product listing.
Listing 5 - An editable, repeating page region for a product catalog in myapp/modules/publications/templates/indexSuccess.php
<?php use_helper('PageEdit'); ?>
<?php use_helper('sfMediaLibrary') ?>
<!-- Displays an "Add Digital Media" button when editing is active. -->
<?php if( ($sf_request->getParameter('edit', 0)!=0)): ?>
<div align="right">
<?php add_instant_object_tag("DigitalMedia", "Add Digital Media »",
array(
"title" => "New Title",
"author" => "New Author",
"price" => "$0.00"
)
, "/publications/index?edit=1") ?>
</div>
<?php endif ?>
<!-- gets all of our books -->
<?php $books = get_editables("DigitalMedia",
array(
"title" => "New Title",
"author" => "New Author",
"price" => "$0.00"
)) ?>
<table width="90%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td colspan="2"><h3>Digital Media</h3></td>
</tr>
<?php foreach($books as $theBook): ?>
<tr>
<td colspan="2">
<?php editable_text_object_tag($theBook, "title", ($sf_request->getParameter('edit', 0)==0) ? false : true, 1, 30); ?><br>
<?php editable_text_object_tag($theBook, "author", ($sf_request->getParameter('edit', 0)==0) ? false : true, 1, 30); ?>
</td>
</tr>
<tr>
<td align="right" colspan="2">
Price: <?php editable_text_object_tag($theBook, "price", ($sf_request->getParameter('edit', 0)==0) ? false : true, 1, 30); ?>
</td>
</tr>
<tr><td colspan="2"> </td></tr>
<!-- if editing is active, display our CRUD controls -->
<?php if( ($sf_request->getParameter('edit', 0)!=0)): ?>
<tr>
<td colspan="2" align="left">
<?php delete_instant_object_tag($theBook, "Delete", "/publications/index?edit=1"); ?>
<?php move_instant_object_up_tag($theBook, "/publications/index?edit=1")?>
<?php move_instant_object_down_tag($theBook, "/publications/index?edit=1")?>
</td>
</tr>
<!-- a visual indicator so the user knows where records start and stop -->
<tr>
<td colspan="2" ><hr width="100%"/></td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</table>
Listing 6 Displaying Nested properties. -- TODO
Securing sfInstantCMS
As a standard Symfony module, sfInstantCMS can easily be secured using standard symfony methods. One approach would be to create two actions for each editable page. For example, for an about page you might create the following actions: about and editAbout. Then in your security.yml file you can restrict access to these actions as your business logic dictates.
You should also have a look at sfInstantCMS's own security.yml file. By default sfInstantCMS performs no security checks. This is left as an exercise for the reader. :)
License
For the full copyright and license information, please view the LICENSE file that was distributed with this source code.
Change Log
5/01/08 -- Initial Release.
Attachments
- sfInstantCMSPlugin-0.0.1.tgz (72.9 kB) - added by jsingleton on 05/02/08 03:25:16.
- InstantCMS1.png (36.9 kB) - added by jsingleton on 05/02/08 04:12:26.
- InstantCMS2.png (47.1 kB) - added by jsingleton on 05/02/08 04:16:41.
