viernes, 23 de septiembre de 2011

Yupp CMS: creando modulos

En este artículo veremos como crear nuevos módulos para Yupp CMS.

Lo primero que tenemos es la estructura interna del CMS:
  • controllers
    • apps.cms2.controllers.CmsController.class.php
  • model
    • cms
      • cms2.model.cms.Layout.class.php
      • cms2.model.cms.Module.class.php
      • cms2.model.cms.Page.class.php
      • cms2.model.cms.PageZone.class.php
      • cms2.model.cms.Zone.class.php
  • views
    • cms
      • createModule.view.php
      • createPage.view.php
      • displayPage.view.php
      • displayPageRO.view.php
      • editModule.view.php
      • listModules.view.php
      • listPages.view.php
    • module
      • displayModule.template.php

El objetivo es crear un módulo para poder mostrar HTML y editarlo de forma visual en la web.

Lo primero que hacemos es crear la clase en el modelo, y ubicarla en model/nombreDelModulo:

YuppLoader::load('cms2.model.cms', 'Module');

class HtmlModule extends Module
{
   function __construct($args = array (), $isSimpleInstance = false)
   {
      $this->setWithTable("cms_html_module");
        
      $this->addAttribute("content", Datatypes :: TEXT);

      parent :: __construct($args, $isSimpleInstance);
   }

   public static function listAll(ArrayObject $params)
   {
      self :: $thisClass = __CLASS__;
      return PersistentObject :: listAll($params);
   }

   public static function count()
   {
      self :: $thisClass = __CLASS__;
      return PersistentObject :: count();
   }

   public static function get($id)
   {
      self :: $thisClass = __CLASS__;
      return PersistentObject :: get($id);
   }

   public static function findBy(Condition $condition, ArrayObject $params)
   {
      self :: $thisClass = __CLASS__;
      return PersistentObject :: findBy($condition, $params);
   }

   public static function countBy(Condition $condition)
   {
      self :: $thisClass = __CLASS__;
      return PersistentObject :: countBy($condition);
   }
}


El módulo se llama HtmlModule, y siempre debe extender a Module. La estructura de directorios en model quedará así:
  • model
    • cms
      • ...
    • htmlModule
      • cms2.model.htmlModule.HtmlModule.class.php

Ahora necesitamos crear un controlador que implemente las acciones particulares sobe ese módulo, como editar el contenido. El controlador debe tener el mismo nombre que el módulo. Aquí está el código del controlador:

YuppLoader::load('cms2.model.htmlModule', 'HtmlModule');

class HtmlModuleController extends YuppController {

   /**
    * Edita el contenido del modulo de HTML.
    * 
    * in: id
    * in: pageId
    */
   public function editAction()
   {
      $module = HtmlModule::get($this->params['id']);
      if (isset($this->params['doit']))
      {
         $module->setProperties($this->params);
         if (!$module->save()) print_r($module->getErrors());
         
         return $this->renderString('Modulo salvado correctamente');
      }
      
      return array('module'=>$module);
   }
}


Aquí está la estructura de directorios resultante de controladores:
  • controllers
    • apps.cms2.controllers.CmsController.class.php 
    •  apps.cms2.controllers.HtmlModuleController.class.php

 Por último, se implementan las vistas del módulo. Una vista será para la edición del contenido, y otra para la visualización del contenido del módulo. La estructura de directorios resultante es la siguiente:
  • views
    • cms
      • ....
    • module
      • displayModule.template.php
    • htmlModule
      • edit.view.php
      • displayModule.template.php

Como se puede ver, en realidad la vista de visualización del contenido del módulo es en realidad un template.

displayModule.template.php:

<?php echo $module->getContent(); ?>
<?php if ($mode=='edit') : ?>
  <div class="customModuleActions">
    <a href="'htmlModule', 'action'=>'edit', 'class'=>$module->getClass(), 'id'=>$module->getId())); ?>" alt="editar html" class="edit_html">
      <?php echo h('img', array('app'=>'cms2', 'src'=>'edit.gif')); ?>
    </a>  
  </div>
<?php endif; ?>



Este template muestra el contenido del módulo, y si estamos en modo "edit", muestra el botón para editar el contenido del módulo.


edit.view.php:

<?php

$m = Model::getInstance();

$module = $m->get('module');

YuppLoader::load('core.mvc', 'DisplayHelper');

?>
<html>
  <head>
    <style>
      body {
         padding: 5px;
      }
    </style>
    
    <?php echo h('js', array('app'=>'cms2', 'name'=>'jquery/jquery-1.5.1.min')); ?>
    <?php echo h('js', array('app'=>'cms2', 'name'=>'jquery/jquery.form-2.84')); ?>
    <script type="text/javascript">
      
      // Funcion que va a llamar el editor cuando termine de cargar (ver config del TinyMCE)
      var htmlinit = function() {
         
         parent.modalReady(document); // Notifica para que el parent actualice el tamanio del iframe
      }
      
      $(document).ready( function() {
        
        // Para que actualice el textarea con el contenido del TinyMCE, antes de mandarlo por ajax.
        // Ref: http://maestric.com/doc/javascript/tinymce_jquery_ajax_form
        $('#editForm').bind('form-pre-serialize', function(e) {
            
            tinyMCE.triggerSave();
        });
        
        // Submitea el form por ajax
        $('#editForm').ajaxForm({
          
          // Cuando el servidor responde ok, quiero actualizar
          // automaticamente el HTML del modulo sin hacer F5.
          success: function (res, status, response) {
             
            // Este es el modulo que cambia en el dom
            var module = $('#getClass().'__'.$module->getId(); ?>', parent.document);
             
            // Tengo que pedir al servidor el HTML actualizado para este modulo
            // Luego meto el actualizado en el dom, antes del que cambie
            // Luego elimino el modulo viejo del dom para que quede solo el actualizado
            
            $.ajax({
              url: ''cms', 'action'=>'moduleContent', 'params'=>array('class'=>$module->getClass(), 'id'=>$module->getId()))); ?>',
              success: function (newModuleContent, status) {
                
                // Actualiza solo el contenido!
                module.children('.moduleContent').html(newModuleContent);

                // Obtengo ventana modal
                var modal = $('#modal', parent.document); // Selecciona ventana
                
                // Cierra ventana modal
                modal.fadeOut('slow').css('display', 'none').children('iframe').attr('src', '');
              }
            });
          }
        });
      });
      
    </script>
  </head>
  <body>
    <form id="editForm" method="post" action="'edit')); ?>">
      <input type="hidden" name="id" value="getId(); ?>" />
      <input type="hidden" name="pageId" value="get('pageId'); ?>" />
      <?php DisplayHelper::html( 'content', $module->getContent() ); ?>
      <input type="submit" name="doit" value="Guardar" />
    </form>
  </body>
</html>


Eso es todo. Cualquiera puede desarrollar sus propios módulos, por ejemplo un cliente de twitter, un visualizador de videos de youtube, un blog, un albúm de fotos, etc, el límite es la imaginación!

.

viernes, 2 de septiembre de 2011

Yupp CMS: un nuevo comienzo

Hace ya varios años que vengo desarrollando en PHP, y desde el principio comencé a experimentar distintas técnicas para el desarrollo de sitios web dinámicos. La culminación de esa investigación dio lugar a mi primer proyecto de CMS. Luego de seguir avanzando en mi formación universitaria, y conociendo diversas tecnologías, me di cuenta que aquel primer CMS no era suficiente, y tratando de hacerlo más genérico y extensible fue que comencé el proyecto Yupp Framework (si, todo esto comenzó como una librería que sería el core de un CMS).

Ahora Yupp tiene vida propia y superó mis expectativas como proyecto, convirtiéndose en una herramienta muy potente para el desarrollo PHP en general. Ahora estoy volviendo a las raíces, y con todo lo aprendido de los distintos intentos de CMS, y de la experiencia acumulada durante años, me lancé a lograr algo mejor, más genérico, más extensible, más fácil de usar, más dinámico, o sea la herramienta perfecta. Claro está, que la herramienta perfecta para unos puede no serlo para otros, desde mi humilde lugar lo que quiero es bajar a tierra todas esas "grandes ideas" y ponerlas en una herramienta que me facilite en el trabajo diario.

Aquí están algunas capturas de pantalla del prototipo que tengo hasta ahora. Es una aplicación para Yupp Framework v0.4, donde desarrollé varias funcionalidades básicas, y aunque faltan otras tantas, se puede usar.

Todos los que quieran colaborar con el proyecto, por favor contesten este hilo de discusión.

Esta primer captura de pantalla muestra una página del portal en modo "edición", donde hay zonas que se ven de colores (rojo, violeta, azul, etc), y cada zona puede contener varios módulos (las cajitas celestes).
Los módulos pueden ser de 3 tipos: html, menú y google maps. Estos son solo algunos que desarrollé para probar el concepto, la idea es que sea algo con extensibilidad ilimitada y cualquiera pueda crear sus propios módulos fácilmente, extendiendo así la funcionalidad del CMS.



Los módulos pueden arrastrarse y soltarse en distintas zonas, lo que ayuda a que rápidamente un editor pueda ubicar los módulos en las zonas correctas. En la siguiente imagen se ve como el módulo html que contenía el banner en la zona superior, es movido a la zona intermedia.



Esta tercer captura de pantalla muestra la edición del contenido de un módulo de html.



Y esta cuarta captura muestra el resultado luego de la edición del contenido del módulo de html.



Otra funcionalidad que provee este prototipo, es el cambio de layout con un clic. Un layout define la estructura general de las páginas, con las zonas donde se pueden ubicar distintos módulos. Un programador puede generar nuevos layouts, e instalarlo en el CMS. Incluso los diseñadores gráficos no programadores, podrían generar layouts. La siguiente imagen muestra el cambio de layout con respecto a las imágenes anteriores:


Esta imagen muestra otro cambio de layout:



Un editor podría crear nuevos módulos y agregarlos en distintas zonas con un par de clics.



La siguiente imagen muestra el módulo luego de creado:



El editor podría también crear subpáginas para la página actual:



Las subpáginas de la página actual aparecen en el menú en la parte inferior:



Por último, si salimos del modo "edición", podemos ver la página en modo "visualización":