# sfDynamicCMS plugin

## Overview

This plugin is not exactly a CMS, it is more a CMF (Content Management Framework).

This plugin allows you to add a Dynamic Content Management System (CMS) to your Symfony project with the following features:

  * Manage different versions of a website (different applications or cultures)
  * For each version : 
    * create and manage a tree structure for navigations (breadcrumb navigation)
    * manage routing & url of each element (fully generate routing.yml)
    * manage displayed menus
    * manage permissions to access content or display element in menu
    * manage pages associated to url (module/action) and page
    * manage page templates (global or specific to version) and template slots)
    * manage content (with slots system)
  * Manage permissions of CMS administration (for edition, administration, advanced administration, developpement).
  * i18n ready (the interface is translated / french translation included)
  * Can be used in any kind of module (by extending it or not), any kind of project.



The aim is to give a backend admin to navigation and content management for all kind of Symfony Project. Then plugin usage is quite more complex and quite different that sfSimpleCMS but permits to be used in any kind of project.[[BR]] This CMS is not l10n, you can't have different versions for a page. But you can maintain different versions for a whole website. Then websites can be totaly different according to their culture and managed separately.[[BR]] This CMS is currently used by all websites (10+) developped by the author : mainly e-commerce and corporate websites.

## Requirements

You should have good basic Symfony knowledge, because the plugin is designed to be very extensible, it might be not easy to grips with it.

The prerequisites for using the  plugin are:`sfDynamicCMS`

  * The page tree strucure use the [http://www.symfony-project.org/plugins/sfPropelActAsNestedSetBehaviorPlugin sfPropelActAsNestedSetBehaviorPlugin]
  * The page content use the [http://www.symfony-project.org/plugins/sfPropelSlotBehaviorPlugin sfPropelSlotBehaviorPlugin] version 0.1.12 at least
  * [wiki:sfGuardPlugin] to manage permission
  * With Symfony 1.2, you must use propel 1.3 compatoble sfPropelActAsNestedSetBehaviorPlugin version packaged with this Plugin 



These 2 plugins must be installed and behaviors enabled in your .`propel.ini`

It is advised to have the following plugins :

  * Rich text editing uses tinyMCE (or FCKeditor). You must install tinyMCE if you want a wysiwyg interface for text editing.



## Installation

Typical installation : frontend use sfDynamicCMS module & backend use sfDynamicCMSAdmin module

  * To install the plugin for a symfony project, the usual process is to use the symfony command line:
        
        $ php symfony plugin-install http://plugins.symfony-project.com/sfDynamicCMSPlugin
        




Alternatively, if you don't have PEAR installed, you can download the latest package attached to this plugin's wiki page and extract it under your project's  directory. You will also have to copy the contents of the  directory into a  directory.`plugins/``myproject/plugins/sfDynamicCMSPlugin/web/``myproject/web/sfDynamicCMSPlugin/`

  * Rebuild the model, generate the SQL code for the new tables and insert it into your database:
        
        $ php symfony propel-build-all
        

  * Enable  modules in backend, via the  file.`sfDynamicCMSAdmin``settings.yml`
        
        all:
          .settings:
            enabled_modules:        [default, sfDynamicCMSAdmin]

  * Enable  modules in frontend, via the  file.`sfDynamicCMS``settings.yml`
        
        all:
          .settings:
            enabled_modules:        [default, sfDynamicCMS]

  * Clear all routes in routing.yml of frontend. It is possible to add your specific routes (first test without) but it is not advised to have routes which use :module parameter (except if you have well understand sfDynamicCMS routing system).

  * Give write permission to routing.yml (-rw-rw-rw- / 666) of your application managed by sfDynamicCMS (frontend usualy).

  * In frontend, add sfDynamicCMSFilter to build index before actions
        
        rendering: ~
        web_debug: ~
        security:  ~
        # generally, you will want to insert your own filters here
        sfDynamicCMS: 
          class: sfDynamicCMSFilter
        cache:     ~
        common:    ~
        flash:     ~
        execution: ~

  * Clear the cache to enable the autoloading to find the new classes:
        
        $ php symfony cc
        

  * If you use Symfony 1.2, you must enable sfCompat10Plugin in backendConfiguration.class.php
        
        public function setup()
        {
          parent::setup();
          $this->enablePlugins('sfCompat10Plugin');
        }




## sfDynamicCMSAdmin Configuration

sfDynamicCMSAdmin configuration require mainly to describe slots. The aim is to have a syntax which look like "Admin generator" generator.yml files for slots description. Usually configuration is also used to describe global templates which are used by all version. It is possible in admin to define templates that are used only in a specific version.

Templates & nodes system are based on sfPropelSlotBehavior plugin so configuration works in the same way than this plugin.

### `app.yml`
    
    
        [yml]
        all:
          sfDynamicCMS:
            dev_credentials: superadmin
            credentials: admin
            sf_culture: true
            default_template: page1
            templates: 
              intro:
                title: "Intro page"
                slots: []
              home:
                title: "Homepage"
                slots: [text, highlighted_product]
              page1: 
                title: "Page model 1"
                slots: [text, image, doc_file, allow_comments, layout]
              page2: 
                title: "Page model 2"
                slots: [column1, date, products_ads, layout]
              contact: 
                title: "Contact"
                slots: [text_contact, email]
            slots:
              text:
                title: "Text"
                type: Text
                params : size=100x10
              column1:
                title: "Text - 1st column"
                type: Text
                help: Don't use "CTRL+V" keystroke but "paste" icons.
                params: rich=true tinymce_options=height:500,width:300
              date:
                title: "Publication date"
                type: Date
              products_ads:
                title: "Products"
                type: Double_list
                params: related_class=Product retrieve_method=doSelectPublished
              image:
                title: "Image"
                type: Image
                params: include_link=page include_remove=true
                upload_dir: page
                help: Download a JPEG file
              doc_file:
                title: "Documentation"
                type: File
                params: include_link=doc_pdf include_remove=true
                upload_dir: doc_pdf
                help: "Download a pdf file"
              email:
                title: "E-mail address"
                type: Input
                params: size=40
              highlighted_product:
                title: "Producted highlighted at home page"
                type: Select
                params: related_class=Product text_method=getLabel peer_method=doSelectPublished
              layout:
                title: "Layout"
                type: Select
                options:
                  layout: "Default layout"
                  user_area: "User area layout"
              allow_comments:
                title: "Allow user comments ?"
                type: Checkbox
    
        dev:
          sfDynamicCMS:
            front_controllers:
              frontend: frontend_dev.php
    

  * `credentials`: Credential to access sfDynamicCMSAdmin
  * `dev_credentials`: Credential to access developper features (version and routing management). If you use sfGuard and are superadmin, you don't need to define it
  * `sf_culture` : Usage of the plugin in a multilingual context (with :sf_culture routing parameter) ; false by default
  * `templates` : list of page templates, each one must have a title and could have a list of template slots
  * `slots` : definition of all slots used by templates ; each slots reprensents a part of the content (or it can be page options).
  * `default_template`: template set to new pages by default 
  * `front_controllers` : list of front controllers for each application ; empty ( = index.php) by default for all applications



## sfDynamicCMS Configuration

### `app.yml`
    
    
        [yml]
        all:
          sfDynamicCMS:
            title: "My Website rulez da world"
            default_module: page
            default_action: default
            routes_register: true
            sf_culture: true
            index: session #for index to user session, see warnings bellow
            forward404: true
            backend_controller: my_admin.php
    
        dev:
          sfDynamicCMS:
            index: true
            backend_controller: dev_admin.php
    

recommanded :

  * `title` : default title or title prefix/sufix
  * `default_module` module called by default while routing (default : sfDynamicCMS)
  * `default_action` action called by default while routing (default : sfDynamicCMS)



facultative :

  * `routes_register` : Use the plugin's routes ; true by default
  * `sf_culture` : Usage of the plugin in a multilingual context (with :sf_culture routing parameter) ; false by default
  * `index` : Retrieve all nodes and create an index to store them in memory in order to do less database queries ; true by default and recommanded, other options : false, session
  * `forward404` : Redirect to 404 if node is not found ; true by default
  * `backend_controller` : Used by frontend toolbar to make link to sfDynamicCMSAdmin : 'backend.php' by default 



#### Index to user session warnings

Be carefull while define index to session because if administator update elements (navigation, routing, status), it will not be update in its sessions (or user ones). Then administrator should use a special environnement to browse website where index is not stored in session.

For very big website, session could also use a lot of apache memory then you should compare memory usage (with sf debugbar) with index stored in session or not.

## Usage of sfDynamicCMSAdmin : Manage version, navigation and content

Start using the plugin by browsing to the admin module's default page:

  * http://myproject/backend_dev.php/sfDynamicCMSAdmin



Admin is divided in 2 sections :

  * version management
  * navigation and content management



If the plugin is used for the first time (when there is no version), it is needed to start by creating a version.

Then :

  * Create a version (usualy default culture and frontend application)
  * Define slots and page templates : as global (in app.yml) or as defined for this version only (in page template section)
  * Create root navigation element (or homepage)
  * Create navigation elements, create pages, define template used by pages, ...
  * Manage rooting parameters of navigation elements
  * Manage pages linked to navigation elements
  * Manage content of pages
  * Create a new version
  * ...



Next : If you want to use all the power of sfDynamicCMS you should extend one or many modules with sfDynamicCMS module.

### What are slots ?

sfDynamicPlugin's slots not have anything to do with Symfony's slots using in view. Slots are the content logic of pages like fields are the content logic of your database table, so sfDynamicPlugin's slots describe content part of a page like : title, text, picture, link, etc. It can be also, parameters for a page like : show_title, number_column, news_attached_to...

Then each page templates are composed of slots. Then template without slots content only static content. Each slots have a type, it can be : text, rich text (using tiny_mce), date, file, checkbox, selectbox, list selection, etc.

Slots description are attached to templates and slots content are attached to pages.

Templates you design might use method to retrieve the slot content like :
    
    
    echo $current_page->getSlotValue('my_text');

## Usage of sfDynamicCMS in your frontend application

Plugin is used by calling executeSfDynamicCMS method in actions. It petmits to :

  * get current navigation element information to display menu or to know where are the visitor in navigation tree
  * get current page template and define it to current action if needed
  * get current page information (title, meta tags, template) and fill it automaticaly
  * get page content (slots)
  * check credentials
  * ...



Then it is recommanded to use the plugin in all modules and all actions of your projects. Then all actions & modules have to be in the navigation structure defined in sfDynamicCMSAdmin.

Plugin can be used anywhere for any kind of use :

  * in a module dedicated to cms pages : "default", "page", "sfDynamicCMS", ...
  * in a module containing business logic : "news", "product", "shop" ...
  * in a extended module : "sfLucene", ...



There is 2 way to call executeSfDynamicCMS method:

  * by using sfDynamicCMSActionBehavior
  * by extending your module(s) with BasesfDynamicCMSActions



The plugin bring to the action or template, 2 attributes :

  * $this->current_node (action) / $current_node (view)
  * $this->current_page (action) / $current_page (view)



It also check if user has access to this action by checking credentials. It also permits use of sfDynamicCMSHelper and sfDynamicCMS object :
    
    
    use_helper('sfDynamicCMS')
    $node2=sfDynamicCMS::getInstance()->getCurrentNode(2);

### Use the plugin with sfDynamicCMSActionBehavior
    
    
    class myModuleActions extends sfActions
    {
      // Method 1 : call the plugin for every actions of the module
      public function preExecute()
      {
        sfDynamicCMSActionsBehavior::executeSfDynamicCMS($this);
      }
     
      // Method 2 : call the plugin action by action
      public function executeMyAction()
      {
        sfDynamicCMSActionsBehavior::executeSfDynamicCMS($this);
        // ... my article action code (business logic) ... //
      }
    }

### Use the plugin by extending your module(s) with BasesfDynamicCMSActions
    
    
    require_once(sfConfig::get('sf_plugins_dir'). '/sfDynamicCMSPlugin/modules/sfDynamicCMS/lib/BasesfDynamicCMSActions.class.php');
     
    class myModuleActions extends BasesfDynamicCMSActions
    {
     
      // Always retrieve navigation information and page content
      public function preExecute()
      {
        $this->executeSfDynamicCMS();
      }
     
      // OR //
      public function executeMyAction()
      {
        $this->executeSfDynamicCMS();    
        // ... my article action code (business logic) ... //
      }

### Template files

Usually, the template file used is named with the name of the template and the suffix "Success" like . But if a template file named with the name of action and the suffix "Success" exist like , it will overide this. It permits to use page content (structured with a generic template slots) with a specific template defined for a specific action.`my_templateSuccess.php``indexSuccess.php`

Then be carefull,  will overide your template file if you use index action which is usualy the default action (this may change for a future version).`indexSuccess.php`

Templates are not included in sfDynamicCMS, you have to create it.

Example, add in your template 'articleSuccess.php' :
    
    
    use_helper('sfDynamicCMS');
    include_component('default','submenu');
     
    if($current_page->getSlotValue('image')) echo image_tag('/uploads/page/'.$current_page->getImage('image'));
     
    echo $current_page->getRichText('text');
    if($page_oeuvre->getSlotValue('author')) echo '<p>'.link_to($current_page->getSlotValue('author'),$current_page->getSlotValue('link')).'</p>';
     
    $next_node=$current_node->retrieveNextSibling();
    if (next_node) echo '<p'>.link_to_with_path('next article',$next_node->getFirstInternalUri()).</p>;

Be carefull, as you see in example, you need to use link_to_with_path (or button_to_with_path or url_for_with_path) for sfDynamicCMS generated links.

### Partials & components examples

#### Example of usage in Partial template "menu"

Use the 'main_menu' menu defined with sfDynamicCmsAdmin module
    
    
    <?php use_helper('sfDynamicCMS'); ?>
    <div id="menu"><?php 
      $options=array('class'=>'elem','node_template'=>'<li id="menu-%%STRIPPED_TITLE%%">%%LINK%%','title'=>'%%TITLE%%','link_content'=>'<span>%%TITLE%%</span>');
      $options_selected=array('class'=>'elem-sel','node_template'=>'<li id="menu-%%STRIPPED_TITLE%%-sel">%%LINK%%','link_content'=>'<span>%%TITLE%%</span>');
      echo sfDynamicCMSMenu('main_menu',1,false,$options,$options_selected);
      ?></div>

#### Example of usage in component template "submenu"

Use the children node of current node to generate a submenu for current section.

`components.class.php` :
    
    
    public function executeSubmenu()
    {
      //get current first level navigation element (main menu opened)
      $this->current_section = sfDynamicCMS::getInstance()->getCurrentNode(1);
    }

`_submenu.php` :
    
    
    <?php use_helper('sfDynamicCMS'); ?>
    <?php if ($current_section) { ?>
    <div id="submenu">
      <?php echo sfDynamicCMSMenu($current_section,1,false,array('class' => 'subelem'),$array('class'=>'subelem-sel')); ?>
    </div>
    <?php } ?>

You can find more information in sfDynamicCMSMenu function header in sfDynamicCMSHelper.php file

### getFirstInternalUri() method

This method is used in link_to_node helper and every helper using it (like sfDynamicCMSMenu).

''getFirstInternalUri()'' method try to get the first uri to node containing a page by browsing down the tree. If no node containing a page is found, it return uri to the current node.

Example 1 : If you have a node which is only a section's title (no content) named "activities" containing several pages like "e-commerce", "intranet", etc. A call to getFirstInternalUri() on "activities" node will return a symfony uri on "activities/e-commerce" because activities have no content and is only a section title.

Example 2 : If you have a node which use custom routing to use your own module like "product catalog" and then has no page assigned to. This node has no child. A call to getFirstInternalUri() on this node will return a symfony uri to "product".

Then the aim is to have a main menu on your website with different type of link : link to a page, link to a section, link to your own module. You should used getFirstInternalUri() method to retrieve symfony uri on menu elements to retrieve Symfony uri to the web pages associated to.

## sfDynamicCMS Routing & URL

In opposition to Symfony routing system, sfDynamic navigation is organize in a hierarchy. So route rules are organize in a tree and URL can be relative which is impossible with routing.yml rules.

The aim is to have a tree managed navigation which permit :

  * CMS pages with automated route & url management system. The you can give to your client the ability to manage their website easily like in every CMS : they don't care about url or route (and they don't have access to it).
  * Dynamic pages (like a product catalog) using the url, the extension, the module, the action and the parameters you want for a given element (like in Symfony) to not loose Symfony flexibility and had business logic. By this way, your own-design modules can use sfDynamicCMS features.



So it is possible to combine cms advantages and simplicity for your client with your custom-built modules which use all the powerfull features offered by Symfony and sfDynamicCMS.

### URL

Root element url might be "/" or "/my_cms"

Children element url can be relative or absolute (but on first level it is the same because route url is "/"). Absolute url start with a "/" and relative url doesn't. It is also possible to have default url which is a relative url generated automaticaly with menu name (stripped menu name) and hard url (to be used in menu generation) starting with http://.

Then there is 4 kinds of url :

  * default url generated automaticaly (leave the url field empty)
  * relative url : a word (eg: "about-the-company" or "company/team") which will be concateneted with parent url to generate the final url
  * absolute url : an url starting with a "/" (eg: "/my-custom-url") which don't care of parent element, url is what you want.
  * hard url : an url starting with "http://" : usefull when you generate menu which contain external (or e-mail - to come) links.



Url can have like in routing.yml rules : "_" character or parameters (eg: "books/:book_id/_ ") but you might have to use custom routing in this case.

### Route

After defining the way to call the page, "the url", you have to define how it reacts. Then sfDynamicCMS permits 3 ways to define a route :

  * Default routing : route managed automaticaly by sfDynamicCMS
  * Custom routing : route managed by sfDynamicCMS, same routing parameters that routing.yml can be defined, you have the advantage to have a tree routing feature and route recognize by the CMS.
  * Traditionnal routing : routing rules defined in routing.yml of the application. Not recommanded but possible.



#### Custom routing

By using Custom routing, sfDynamicCMSAdmin module generate rules in routing.yml (without erasing your own rules). sfDynamicCMS will recognize this kind of rules. This way is usefull to merge your business logic with cms features offered by sfDynamicCMS.

#### Default routing

By using Default way, there is nothing to define, route is managed automaticaly but not permit to have use a specific module or action. Default routing doesn't appear in routing.yml, this rules are executed after custom and traditionnal routing rules.

#### Traditionnal routing

Traditionnal rules can defined in routing.yml before or after the sfDynamicCMS rules (which are automaticaly updated). Be carefull because sfDynamicCMS might not be able to find where are located this rules in sfDynamicCMS navigation tree.

### Route names

It is not recommanded to use link working with route name. When using custom routing, you can define and use route name.

**_Not recommanded :_**

  * @sfDynamicCMS_custom_route_xx : custom route (xx = Node ID)
  * @sfDynamicCMS?url=... : default route, link by url (might be unusefull, use sfDynamicCMS helper)



**_Deprecated, use helpers :_**

  * @sfDynamicCMS_homepage : main homepage (default culture)
  * @sfDynamicCMS_homepage?sf_culture=xx : homepage of the xx culture (in multilingual context only)



## Dev information

Please note that this plugin is in perpetual development. If you want to help and improve it, please contact Sylvain Papet (my firstname @ com-ocean.com).

### Principles

The plugin has 2 main goals :

  * manage navigation and version
  * manage pages and content



The aim is to have independance between these 2 logics

### Business logic

  * "version" = nav (navigation tree) => SfDynamicCmsNav class & sf_dynamic_cms_nav table
  * "navigation element" = node (navigation node) => SfDynamicCmsNode class & sf_dynamic_cms_node table
  * "Page" = page => SfDynamicCmsPage class & sf_dynamic_cms_page table
  * "Menu" = menu => SfDynamicCmsMenu class & sf_dynamic_cms_menu table



### sfDynamicCMS class

  * sfDynamicCMS : represent current version: the sfDynamicCMS navigation. Use in your module to perform navigation management.
  * sfDynamicCMSTools : static functions to use in extended module and static function used by sfDynamicCMSAdmin
  * model classes : for nav, node, page & slot
  * slotType classes : used in sfDynamicCMSAdmin & model (works look-like sfSimpleCms slots)



## Known limitations

### Custom route overiding default route

Preview could not work with dynamic pages using custom routing

### Custom route overiding default route

Duplication of a version : experimental, not fully implemented

### Custom route overiding default route

#### @sfDynamicCMS_homepage & @sfDynamicCMS_culture_homepage rules & custom routing

@sfDynamicCMS_homepage & @sfDynamicCMS_culture_homepage rules badly work when root nodes use custom routing.

#### default & custom rules conflict

Be carefull, some custom routes can overide default routes in some case.

Example :

  * Parent node "Videos" : url="/video" (1) (default routing) 
    * Node "myVideoPage" : url="myPage" (2) (default routing, cms page)
    * Node "myVideo" : url=":video_param" (3) (custom routing, custom module using a video database)



(absolute url are respectively: "/video", "/video/myPage", "/video/:video_param")

Limitation : Even if "myVideoPage" node is placed before "myVideo", 3rd node rule will overide 2nd node rule. There is 2 solutions to resolve it.

First solution : add a prefix to "myVideo" url :

  * Node "myVideo" : url="vid/:video_param"



Second solution : Define a custom routes for "myVideoPage" and fill module and action with default module and default action.

  * Node "myVideoPage" : url="myPage" => custom routing, module: sfDynamicCMS, action: sfDynamicCMS



An other solution, if "video_param" is a number, add requirement for video_id parameter.

## Todo

### Global

  * sylvio : improve admin interface (especialy for permissions) to be more user friendly



### CMS Features

  * sylvio : check and enhance page title & meta parameter usage (template)
  * sylvio : add new helper functions



### Routing & Navigation

  * sylvio : review @sfDynamicCMS_homepage & @sfDynamicCMS_culture_homepage rules in order that it works with custom routing
  * sylvio : check the feature to create a new version with duplication of content of an other version (improve menu duplication)
  * sylvio : extends sfPropelActAsNestedSetBehaviorPlugin "informational methods" to use Node index
  * sylvio : check nodes which has same url while creating or updating and display warning
  * sylvio : ability to have custom tags for a node (e.g. menu_id, menu_image) or a version (title, ...)



### Doc

  * sylvio : API
  * sylvio : Enhance doc
  * sylvio : Create an open-source sfDynamicCMS Website for sfDynamicCMS Plugin



## Changelog

### 2008-12-16 | 0.4.3 Beta

  * sylvio : fix some bugs



### 2008-12-02 | 0.4.2 Beta

  * sylvio : fix important bugs



### 2008-11-25 | 0.4.1 Beta

  * sylvio : fix an important bug while retrieving version
  * sylvio : fix a bug while deleting page



### 2008-11-24 | 0.4.0 Beta

  * sylvio : a lot of refactor and bugfixes
  * sylvio : sfDynamicCMSMenu: fix bugs & add php documentation
  * sylvio : possibility to use the plugin without extending module : sfDynamicCMSActionsBehavior
  * sylvio : completely refactor & redesign permissions management system
  * sylvio : add "credentials" option for accessing sfDynamicCMSAdmin module
  * sylvio : fix ticket #4362, slot records have always culture defined
  * sylvio : add page author management
  * sylvio : add page preview
  * sylvio : make plugin compatible with Symfony 1.1 (thanks to disturbedHR)
  * sylvio : make plugin compatible with Symfony 1.2 & propel 1.3 (required propel 1.3 compatible version of sfPropelActAsNestedSetBehaviorPlugin : included in data folder of the plugin)
  * sylvio : refactor routing (fix bug with homepage using default routing)
  * sylvio : improve & refactor admin interface
  * sylvio : little toolbar component for frontend to access node admin actions 
  * sylvio : add new helpers 



> **This version must use sfPropelSlotBehavior release 0.1.12**
> 
> #### To upgrade from 0.3.x

  * make a backup of database
  * rebuild model (symfony propel-build-model) and clear cache
  * call http://mydomain.tld/backend_dev.php/sfDynamicCMSAdmin/upgrade (you can call upgrade process several time without incidence)



### 2008-06-13 | 0.3.2 Alpha

  * sylvio : sfDynamicCMSMenu: fix bugs and refactor helper
  * sylvio : add ability to define "default_slot_options:" like sfPropelSlotBehavior Plugin
  * sylvio : fix #3740 ticket "using another module than sfDynamicCMSAdmin as the admin"
  * sylvio : fix minor bugs

Notice : don't forget to upgrade sfPropelSlotBehaviorPlugin




### 2008-06-06 | 0.3.1 Alpha

  * sylvio : fix some bugs (menu creation) and improve helper (sfDynamicCMSMenu) 



### 2008-06-06 | 0.3.0 Alpha

> **This version contains Break Compatibility mainly because slots are outsourced to sfPropelSlotBehavior plugin and schema is updated.**
> 
> #### Upgrade from 0.2.x to 0.3

  * first make a backup of your database and your project
  * install sfPropelSlotBehaviorPlugin 0.1.4 (see requirements section of this doc ) and create sfSlots table in your Database
  * upgrade sfDynamicCMSPlugin to 0.3.x
  * add sfDynamicCMSFilter for frontend (see Installation section) 
  * rebuild your model (symfony propel-build-model)
  * clear cache
  * launch ugrade process http://www.my_domain.tld/backend.php/sfDynamicCMSAdmin/upgrade (MySQL)
  * when you are sure that all work fine, you can delete "sf_dynamic_cms_slot" table



> #### Changes

  * in frontend app.yml, for slots & templates description : you should rename "name" to "title"
  * Be careful Node's isVisible() and getVisibleChildren() methods might be used instead of isPublished() and getPublishedChildren() method for menu(s)

  * sylvio : major change : outsource slots management to sfPropelSlotBehavior plugin, slots system will evolve independantly

  * sylvio : refactor & fix bugs about url generation (when using absolute url)
  * sylvio : fix bug about generation of root node url (which was always "/" whatever url defined)
  * sylvio : Node index system : descrease amount of database requests significantly (sfPropelActAsNestedSetBehaviorPlugin "node retrieval methods" extends to use it)
  * sylvio : some changes on database schema
  * sylvio : new helpers, "link_to_node", "button_to_node", "url_to_node", "sfDynamicCMSMenu" to render customized menu
  * sylvio : new node status (replace is_published)
  * sylvio : new menus management
  * sylvio : call Symfony's forward404 method if node is not found
  * sylvio : sfDynamicCMSAdmin interface improved



### 2008-05-26 | 0.2.5 Alpha

> #### Upgrade note
> 
> You need for each version to regenerate routing config file(s) & database : http://www.my_domain.tld/backend.php/sfDynamicCMSAdmin/generateRouting
> 
> #### Changes

  * sylvio : improve routing : improve ccurrent nav & current node retrieving especially for custom routing rules
  * sylvio : change custom route name generation
  * sylvio : ability to define route name for a node which use custom routing rule 



### 2008-05-13 | 0.2.4 Alpha

  * sylvio : fix some bugs (regeneration of routing.yml and clear cache when moving node, '' markup replacement, routes_register really facultative, sfDynamicCMSNode::getAbsoluteUrl method )
  * sylvio : add "known limitations" section in doc
  * sylvio : trunk available on Symfony svn repositery : http://svn.symfony-project.com/plugins/sfDynamicCMSPlugin/



### 2008-04-25 | 0.2.3 Alpha

  * sylvio : refactor routing.yml generation & fix some minor bugs
  * sylvio : add a features to create a new version with duplication of content of an other version



### 2008-04-24 | 0.2.2 Alpha

  * sylvio : fix several important bugs in sfDynamicCMSAdmin when managing more than one version
  * sylvio : refactor routing & versions (multilingual) - Keep It Sweet & Simple
  * sylvio : development of pages management section in version management (to delete orphan pages)
  * sylvio : localization parameter depracted (it is unusefull to list cultures), "sf_culture" parameters simply have to be set to true if cms is used in a multilingual context (with usage of sf_culture).
  * sylvio : improve doc (configuration, slots, routing, ...)



### 2008-04-10 | 0.2.1 Alpha

  * sylvio : fix usage of sfDynamicCMSAdmin button "before" bug
  * sylvio : fix route bug : order of custom route rules generated in routing.yml
  * sylvio : add Select and CheckBox slot types
  * sylvio : ability to have hard url starting with http:// like : "http://www.symfony-project.org"
  * sylvio : ability to have empty url to have url generated with node menu name
  * sylvio : default sfDynamicCMS action is now sfDynamicCMS (instead of index)
  * sylvio : remove getThumbnail method (unusefull - use directly sfThumbnail helper)



### 2008-03-26 | 0.2.0 Alpha

  * sylvio : downgrade to Alpha (Beta was not correct in precedent version)

  * sylvio : '''BC (Break compatibility)''' in sf_dynamic_node table :

"name" field renamed "url" field and "url" field renamed "absolute_url" field, then use getUrl() instead of getName() in template
        
        ALTER TABLE `sf_dynamic_cms_node` CHANGE `url` `absolute_url` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL
        ALTER TABLE `sf_dynamic_cms_node` CHANGE `name` `url` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL    

  * sylvio : refactor routing : parameters field disapear (parameters are defined in url like in routing.yml rules)

  * sylvio : refactor slots & add "Date", "Double_list", "Custom" slots
  * sylvio : sfDynamicCMSAdmin module is now extensible (in order to permit to add custom slot templates)



### 2008-25-03 | 0.1.2 Beta

  * sylvio : Fix an important bug appearing in 0.1.1 (credential bug during installation)
  * sylvio : make i18n ready "page templates section" & some i18n correction
  * sylvio : add "Date" slot type



### 2008-17-03 | 0.1.1 Beta

  * sylvio : allow flexible model extension by adding an additional level of class inheritance (http://trac.symfony-project.com/wiki/HowToExtendPropelPluginModel)
  * sylvio : add helper to generate link, url & button (in order to correct '/' conversion)
  * sylvio : check write permission to routing.yml files
  * sylvio : add method to check access credentials in sfDynamicCMS module
  * sylvio : add generic action named 'sfDynamicCMS' to sfDynamicCMS module
  * sylvio : security fix (access to 'index' action from sfDynamicCMSAdmin module)
  * sylvio : use of sfThumbnailHelper (in PluginsfDynamicCmsPage)



### 2008-14-03 | 0.1.0 Beta

  * sylvio: initial release