miércoles 25 de enero de 2012

Liberamos Yupp CMS v0.2

El proyecto va tomando forma con nuevas funcionalidades, mejoras y código más limpio. Todavía falta mucho camino por recorrer, pero vamos bien. Espero que en poco tiempo más gente se sume al proyecto y puedan aportar módulos y skins.

El CMS puede ser descargado desde:



Les recuerdo algunos posts anteriores sobre el CMS:


Algunos comentarios:
  • Funciona con la versión trunk de Yupp Framework: http://yupp.googlecode.com/svn/trunk/YuppPHPFramework/
  • Para instalar es solo:
    • copiar el directorio en yupp/apps
    • crear la base de datos yupp_cms (corregir datos de conexión a la base si es necesario)
    • correr el bootstrap del cms (creará algunas páginas y módulos de ejemplo)

Solo para fanaticos de PHP

Muy bueno:

miércoles 5 de octubre de 2011

Acelerando la ejecucion de jquery

Ayer leí un artículo interesante, sobre cómo registrar eventos con jQuery en distintos elementos del DOM, y cómo con live() funciona más rápido que haciendo el registro luego de que termine de cargar el DOM.

http://encosia.com/dont-let-jquerys-document-ready-slow-you-down/

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":




lunes 22 de agosto de 2011

Aplicacion para gestion de contenido con tags

Este fin de semana desarrollé un prototipo de un sistema de gestión de contenido, concentrándome en las funcionalidades de agregar y quitar tags del contenido.

Aquí les dejo la aplicación para descargar y probar.

El objetivo fue evaluar que tan costoso era implementarlo en Yupp framework, y el resultado fue el siguiente:
  • Aplicación: Yupp/Ajax/jQuery/MySQL
  • Dedicación: 4 horas
  • Funcionalidades principales: crear tags, agregar tags a varios videos (contenido), quitar una tag de un video
  • Funcionalidades de soporte: obtener todas las tags existentes, obtener las tags para cada video

Lo que logré fue algo así:



A continuación les cuento un poco cómo lo hice:

Estructura de la aplicación:
  • bootstrap
    • apps.resman.bootstrap.Bootstrap.script.php
  • controllers
    • apps.resman.controllers.TagsController.class.php
    • apps.resman.controllers.VideoController.class.php
  • javascript
    • jquery-1.6.2.min.js
    • jquery.form-2.84.js
  • model
    • resman.model.Tag.class.php
    • resources
      • resman.model.resources.Video.class.php
  • views
    • video
      • list.view.php
  • app.xml


Como pueden ver, es una aplicación pequeña, con un par de controllers, un par de clases de modelo persistente, una vista y un par de archivos javascript, uno es el famoso framework javascript jQuery y el otro el plugin para gestionar formularios y poder hacer envíos por ajax, en lugar del clásico submit que nos hace tener que recargar toda la página, y hacerlo por ajax mejora enormemente la experiencia del usuario.

En el archivo de bootstrap se crean algunos videos y la etiqueta que guarda todas las etiquetas del sistema (luego explico como es esto). Como esto es una prueba y no es un gestor de contenidos completo se deben crear los videos en el bootstrap. Si fuera un gestor de contenidos completo, tendría funcionalidades para poder crear, modificar y eliminar videos y otros tipos de contenidos como archivos, links, etc.

En el bootstrap tenemos:

// Incluye clases del modelo persistente
YuppLoader::load('resman.model', 'Tag');
YuppLoader::load('resman.model.resources', 'Video');

// Crea videos de prueba
$videos = array();

$videos[] = new Video(array(
  'name'=>'La Coka Nostra - That\'s coke',
  'comment'=>'excelente!',
  'embedCode'=>'<iframe allowfullscreen="" frameborder="0" height="345" src="http://www.youtube.com/embed/HaVf_yrq6Q0" width="560"></iframe>',
  'link'=>'http://youtu.be/HaVf_yrq6Q0'
));
$videos[] = new Video(array(
  'name'=>'Snoop Dog - Vato',
  'comment'=>'hardcore',
  'embedCode'=>'<iframe allowfullscreen="" frameborder="0" height="345" src="http://www.youtube.com/embed/D_9Dfh2Hf-A" width="560"></iframe>',
  'link'=>'http://youtu.be/D_9Dfh2Hf-A'
));
$videos[] = new Video(array(
  'name'=>'Snoop Dog - Drop it like it\'s hot',
  'comment'=>'chambalala',
  'embedCode'=>'<iframe allowfullscreen="" frameborder="0" height="345" src="http://www.youtube.com/embed/LfgMC6F0FVo" width="420"></iframe>',
  'link'=>'http://youtu.be/LfgMC6F0FVo'
));

// Guarda en la base los videos de prueba
foreach ($videos as $video)
{
   $video->save();
}

// Crea la instancia global con todas las tags
$globalTags = new Tag(array('tags'=>''));
$globalTags->save();


Antes comentaba que se crea una instancia de Tag que va a guardar todas las tags que se crean en el sistema, la idea es que desde esas tags se seleccionen las tags que se le van a poner a los videos.


Las tablas que generan las clases del modelo persistente son las siguientes:

Tabla de videos con los 3 videos creados en el bootstrap:


id name comment embedCode link class deleted
1 La Coka Nostra - That's coke excelente! <iframe allowfullscreen="" frameborder="0" height="345" src="http://www.youtube.com/embed/HaVf_yrq6Q0" width="560"></iframe> http://youtu.be/HaVf_yrq6Q0 Video 0
2 Snoop Dog - Vato hardcore <iframe allowfullscreen="" frameborder="0" height="345" src="http://www.youtube.com/embed/D_9Dfh2Hf-A" width="560"></iframe> http://youtu.be/D_9Dfh2Hf-A Video 0
3 Snoop Dog - Drop it like it's hot chambalala <iframe allowfullscreen="" frameborder="0" height="345" src="http://www.youtube.com/embed/LfgMC6F0FVo" width="420"></iframe> http://youtu.be/LfgMC6F0FVo Video 0


Tabla de tags con la tag que tiene todas las tags creadas en el sistema (la que tiene objClass y objId en NULL), y las tags creadas para cada uno de los videos. Todas las tags de cada video se guardan codificadas en la columna "tags", separadas por coma. Estas son las tags que aparecen en la captura de pantalla que puse más arriba.

id objClass objId tags class deleted
1 NULL NULL papa,pepe,popo,pupu,caradura,sampatrucha Tag 0
2 Video 1 popo,sampatrucha,pepe Tag 0
3 Video 2 sampatrucha,caradura,pepe Tag 0
4 Video 3 popo,sampatrucha,caradura Tag 0


Podría repasar toda las funcionalidades, pero aquí les dejo la aplicación para descargar, donde pueden ver el código y probar de instalarla en Yupp Framework 0.4, además el código está lleno de comentarios.

La idea central era mostrar que con aproximadamente 300 líneas de código, entre PHP y Javascript, y en 4 horas, se pueden lograr funcionalidades muy interesantes, que agilizan la experiencia del usuario, y que tienen gran usabilidad.

lunes 20 de junio de 2011

Google code search una herramienta excelente

Google code search es una herramienta de Google para buscar código fuente dentro de los proyectos alojados en Google code. Por ejemplo, Yupp Framework es un proyecto de código abierto alojado en Google code.

Ingresando al buscador de código, podemos ingresar el siguiente texto: "helper ajax_link package:http://yupp\.googlecode\.com". Al buscar, vamos a obtener una lista de resultados. Haciendo clic en alguno de los resultados de esa lista, podemos acceder al código fuente:

Luego podemos recorrer el código fuente de uno o más archivos donde se dieron resultados para nuestra búsqueda, o también podemos realizar otras búsquedas.

Esta es una excelente herramienta para encontrar en pocos segundos el código fuente exacto que estamos buscando.