Friday, January 15, 2010

Modificar widgets defaults del módulo sfGuardUser (sfDoctrineGuardPlugin)

Sigo con la onda symfony pues estoy trabajando en un proyecto con el framework.

En este caso estaba usando el plugin sfDoctrineGuardPlugin el cúal supongo, si están leyendo estas líneas, conocen para que funciona.

En este caso tengo un formulario de registro de usuarios y requería que el campo para permisos del usuario, que por default es:

//lib/form/doctrine/sfDoctrineGuardPlugin/BasesfGuardUserForm.class.php
       ...
      'permissions_list' => new sfWidgetFormDoctrineChoiceMany(array('model' => 'sfGuardPermission'))
       ...


y su validator:
      ...
      'permissions_list' => new sfValidatorDoctrineChoiceMany(array('model' => 'sfGuardPermission', 'required' => false)),
      ...


cambiase la validación a un campo requerido, es decir
'required' => true.

Dado que la idea NO es modificar el plugin, de manera que se mantenga el código mantenible y el plugin actualizable (Principios básicos de calidad del software), intenté sobreescribir el validator desde la clase sfGuardUser.class.php, que es creada automáticamente en el directorio /lib/form/doctrine/sfDoctrineGuardPlugin. Procedí a añadir dentro de la función configure, la siguiente línea:

     $this->validatorSchema['permissions_list']->setOption('required', true);

Sin embargo esto no funciona. Ningún tipo de modificación que hiciera sobre la lista de widgets autogenerados (username, group_list, is_active, permissions_list, etc..) funciona.

Solución

Googleando y preguntando en los foros, encontré que la solución es hacer las modificaciones sobre el archivo sfGuardAdminUser.class.php en lugar de sfGuardUser.class.php. Este se encuentra en el directorio del plugin. Pueden copiarlo y agregarlo a la ubicación /lib/form/doctrine. De esta manera cualquier modificación realizada sobre alguno de los widgets SÍ tendrá efecto.

Una segunda opción:

Copiar el generator.yml  de
/plugins/sfDoctrineGuardPlugin/modules/sfGuardUser/config/generator.yml
a
/apps/tu_app/modules/sfGuardUser/config/generator.yml

Luego cambiar el form class en el generator.yml:

     form:
            class: sfGuardUserAdminForm


Crear una nueva clase form así:

//lib/form/doctrine
      class sfGuardMyUserAdminForm extends sfGuardUserAdminForm
     {
        public function configure()
       {
            $this->validatorSchema['permissions_list']->setOption('required', True);
       }
     }

Eso es todo:

Fuente:
-
Modify sfGuardUserForm fields


Monday, January 4, 2010

Symfony 1.2 Forms Autocompletion con Doctrine 1.0 (Parte I)

    El siguiente tutorial fue probado sobre una aplicación con Symfony 1.2.10 (mantenido hasta Febrero del 2010). Aunque no he realizado pruebas con Symfony 1.3 y 1.4 debería funcionar correctamente:

Primer Paso: Instalacion del Plugin sfFormExtraPlugin

     $ php symfony plugin:install sfFormExtraPlugin

     $ php symfony cc

    Con estos paso habremos instalado este plugin que agrega varios widgets útiles.

Segundo Paso: Trabajemos en el Backend con el formulario al cual le agregaremos un campo con autocomplete

    En el ejemplo a continuación tengo un formulario que representa el perfil de un Usuario y deseo agregar un campo de Estado (estados de Venezuela), para su dirección, que sea con autocomplete. En la base de datos existe un campo 'estado' dentro la tabla Usuario, que representa un foreign key al id de la tabla Estado.

//lib/form/doctrine/UsuarioForm.php

     <?php

     /**
      * Usuario form.
      *
      * @package    form
      * @subpackage Usuario
      * @author José A.
      */
     class UsuarioForm extends BaseUsuarioForm
     {
           public function configure()
          {
                ...

                $context = sfContext::getInstance();

               // Widget de Estado con Autocomplete
               $this->widgetSchema['estado']->setOption('renderer_class',
                    'sfWidgetFormJQueryAutocompleter');
               $this->widgetSchema['estado']->setOption('renderer_options', array(
                    'url' => $context->getController()->genUrl('@ajax_getstates')
                ));

                ...
           }
      }


    Acá primero se define una variable ($context), para determinar el contexto en donde se encuentra la instancia y que permitirá obtener la URL de la acción que symfony requiere para ejecutar el autocomplete.

    Posteriormente, se modifica el widget 'estado' tal como se indica en el tutorial de symfony make your choice, con la diferencia de que en este caso usaremos un widget distinto del sfFormExtraPlugin, pues el ORM es Doctrine y no Propel.

    En la opción 'renderer_class', asignaremos el widget sfWidgetFormJQueryAutocompleter. Luego en 'renderer_options' debemos especificar el atributo 'url', al cual a través de la variable $context, le asignaremos la URL '@ajax_getstates' de la acción  que crearemos más tarde.

Tercer Paso: Activación del campo en el Generator

    Para que el campo 'estado' se muestre en el formulario, debe ser activado en el archivo generator.yml

//apps/backend/modules/module/config/generator.yml

     ...

     form:
             class: UsuarioForm
             display:
                 Usuario:   [ username, password, password_again, permissions_list, estado]


     ...

Cuarto Paso: Crear la acción en el action.class

//apps/backend/modules/module/actions/action.class.php

    class moduleActions extends autoModuleActions
    {
      ...
      public function executeGetStates(sfWebRequest $request)
      {
           $this->getResponse()->setContentType('application/json');
          // Parametro 'q', contiene lo que fue introducido en el campo por teclado
          $string = $request->getParameter('q');

          // Consulta al modelo Estado
          $rows = Doctrine::getTable('Estado')->getStatesWith($string);

          $states = array();
          foreach ($rows as $row)
         {
             $states[$row->getId()] = $row->getNombre();
          }
   
          return $this->renderText(json_encode($states));
  
       }
      ...
    }


     Se utiliza JSON como formato de codificación para devolver los resultados de la acción. La acción es simple, pues sólo obtiene de la sesión el valor introducido por el usuario en el campo y lo utiliza para la consultar en el modelo. Luego cada uno de esos valores retornados por el modelo, son colocados en un arreglo que será codificado en formato JSON y devuelto como resultado.

Quinto Paso: Consulta en el modelo de la tabla Estado

//lib/model/doctrine/EstadoTable.php
     <?php
     ...
    class EstadoTable extends Doctrine_Table
    { 
         ...
        public function getStatesWith($string)
       {
            $query = Doctrine_Query::create()
               ->from('Estado e')
               ->where('LOWER(e.nombre) LIKE ?', '%'.$string.'%')
               ->orderBy('e.nombre ASC')
               ->execute();
                     
             return $query;
        }
    }


Sexto Paso: Agregar la acción en el routing.yml

//apps/backend/routing.yml
      ...
     ajax_getstates:
       url:   /getStates
       param:  { module: sfGuardUser, action: getStates }

    ...

     Como se aprecia, la etiqueta debe coincidir con el nombre de la URL que se colocó al widget 'Estado', es decir @ajax_getstates

Séptimo Paso
: Incluir los CSS y JS correspondientes al widget de autocomplete de sfFormExtraPlugin

Esto se puede hacer de dos formas diferentes:
a) Activando el CSS y Javascript para toda la aplicación backend.
Agregando esto en el archiv view.yml
//apps/backend/config/view.yml
      ...
     stylesheets:    [main.css, /sfFormExtraPlugin/css/jquery.autocompleter.css]
      javascripts:     [/js/jquery/jquery-1.3.js, /sfFormExtraPlugin/js/jquery.autocompleter.js]

     ...

b) Activando sólo para el módulo en donde es requerido
Agregando estás líneas en _form.php

//apps/backend/modules/module/templates/_form.php
      ...
     <?php use_javascript('jquery-1.3.2.min.js') ?>
     <?php use_javascript('/sfFormExtraPlugin/js/jquery.autocompleter.js') ?>
     <?php use_stylesheet('/sfFormExtraPlugin/css/jquery.autocompleter.css') ?>

      ...

     En este caso agregamos estas líneas al principio del partial _form.php. En ambas opciones lo que se hace es incluir el estilo del campo del campo de autocomplete, y los respectivos JavaScript para hacer el request.

     Con esto debería funcionar el autocomplete para el campo dado.



     Un único problema resta al momento de guardar el formulario. En el campo de Estado se guarda el id de éste en vez del nombre del Estado. Desafortunadamente el sfFormExtraPlugin, no incluye un widget específico de autocomplete para Doctrine como lo hace con Propel. En la segunda parte de este tutorial mostraré una posible solución al problema.

Referencias:
- symfonyguide (Francés)
- particul.es (Francés)
- symfony forum (Inglés)




Monday, December 28, 2009

Gateway Recovery Management error 205 (English/Spanish)

Mi portátil es una Gateway FX 7809u. Esta semana me di cuenta que no había hecho respaldo alguno ni de los Drivers ni de la configuración de fábrica del sistema.

Me dispuse a hacerlo usando la aplicación que el equipo trae para tal tarea. Gateway Recovery Management (Un software hecho por Acer Inc). Cada vez que insertaba un disco y comenzaba el respaldo, la aplicación devolvía un error 205 y pedía que se insertara un CD o DVD en blanco. Luego de googlear un rato y ver que es un problema común con las Gateway, alguien comentaba en un blog que bajando la última versión funcionaría. En efecto, con la nueva versión, el problema queda solucionado.

Se puede descargar desde acá.

-----------------------------------------------------
I have a Gateway FX 7809u. I was trying to make a backup of the factory default system through the Gateway Recovery Management software. However I was getting the same error in every try (Error 205)

After some google search, I finally found a solution to this problem. You must download a new version of the Recovery management software. This is a very common problem reported in many forums by Gateway laptops owners. The default version of the recovery software has a bug.

The new version of the software could be downloaded here. It works like a charm.


Vía: geekyramblings

Saturday, December 12, 2009

Flash Player para Ubuntu 9.10 amd64 y botones que no funcionan

Desempolvando un poco mi blog (que lo he abandonado tal como no quería XD), traigo una solución para un inconveniente con flash y los botones de juegos, reproductores y demás..

Hace un mes que instalé la última versión de Ubuntu, Karmic Koala y había tenido algunos problemas con los botones de acción del reproductor de youtube, facebook y otros widgets con flash. Por ejemplo, para accionar los reproductores de youtube debía usar la barra espaciadora, pues ningún botón funcionaba con un click del mouse.

Googleando un buen rato, encontré en los foros de ubuntu una solución muy sencilla:

1) Ejecutar en un terminal:
$ sudo gedit /usr/lib/nspluginwrapper/i386/linux/npviewer
2) Añadir al final del archivo esta línea:
export GDK_NATIVE_WINDOWS=1
3) Guardar y cerrar

Como ejemplo, este es el contenido de mi archivo npviewer:
#!/bin/sh
TARGET_OS=linux
TARGET_ARCH=i386
# 8 dec 2009. Added to fix flash mouse problem
export GDK_NATIVE_WINDOWS=1
. /usr/lib/nspluginwrapper/noarch/npviewer
Con esto debería bastar. Reiniciar el browser y debería funcionar para Firefox y para Chromium..

Otros intentos:
- Intenté también, antes de encontrar esta solución, instalar la versión de Adobe Flash para 64 bits, bajando el tar.gz de la página y colocando el descomprimido en el directorio /usr/lib/mozilla/plugins y en el directorio /usr/lib/chromium-browser/plugins
Para firefox funcionaba, pero con Chromium (navegador que estoy usando con frecuencia) provocaba errores y hacia crash en algunas páginas con flash.

Decidí al final quedarme con los paquetes de flash que se encuentran en los repositorios y eliminar lo anterior.
Por referencia los paquetes que deberían estar instaldos son:
flashplugin-installer
flashplugin-nonfree

Es todo, suerte!

Fuente:
http://ubuntuforums.org/showthread.php?t=1312123

Saturday, October 3, 2009

Una cita de Andy Rutledge

Estaba chequeando mi reader y me encontré con esta cita:

Beautiful accidents can happen, but accident is not the basis for design excellence. Purposeful discovery followed by focused, skillful conceptualization and execution is the basis for design excellence.


Acá está su página que por cierto, está muy bien.

Un post corto lo sé, pero valía la pena colocarlo

Monday, September 28, 2009

Modificación de filtros en Symfony para un modulo de administración. Ejemplo sobre el módulo sfGuardUser del sfDoctrineGuardPlugin(Doctrine)

Dada la poca documentación al respecto, decidí publicar un ejemplo sobre modificación de los filtros para un módulo de administración en una aplicación de symfony. El ejemplo en concreto es sobre el plugin sfDoctrineGuardPlugin y su módulo sf_guard_user.

El detalle importante en este caso es que de los filtros que el módulo genera por default, probablemente sólo los de username, permissions y fechas son útiles, pero dado que el resto de campos que completan el perfil de un usuario van en otra tabla (Customize sfDoctrineGuardPlugin), se hace necesario crear nuevos filtros para los atributos del perfil que sean necesarios.


Ejemplo de schema del perfil:

Perfil:
  columns:
    codigo:
      type: string(10)
      primary: true
      notnull: true
    sf_guard_user_id:
      type: integer
    apellidos:
      type: string(40)
      notnull: true
    nombres:
      type: string(40)
      notnull: true
    sexo:
      type: string(1)
      notnull: true
    email:
      type: string(45)

    #La relación sfGuardUser fue creada manualmente para acoplar esta tabla al
    #plugin de manejo de usuarios sfDoctrineGuardPlugin.
  relations:
    sfGuardUser:
      local: sf_guard_user_id
      foreign: id
      foreignType: one
      onDelete: CASCADE



Agreguemos entonces un filtro para los nombres de los usuarios:

Debemos ubicar la clase sfGuardUserFormFilter.class.php dentro del directorio /lib/filter/doctrine/sfDoctrineGuardPlugin. Ahí agregaremos lo siguiente dentro de la función configure:

public function configure()
{
  //...

  //Widget para el filtro de nombres
  $this->widgetSchema['nombres'] = new sfWidgetFormFilterInput(array(
        'with_empty' => false
  ));
  $this->validatorSchema['nombres'] = new sfValidatorPass(array(
        'required' => false
  ));
}


Con esto hemos creado un widget para el atributo 'nombres' con su respectivo validator. El widget es un simple campo de entrada (sfWidgetFormFilterInput), en donde hemos desactivado con "'with_empty' => false", el checkbox 'is_empty'.

Posteriormente agregaremos la función para consultar sobre la base de datos los atributos que deben ser cargado desde la tabla perfil.

public function addNombresColumnQuery(Doctrine_Query $query, $field, $values)
{
  //Se comprueba que no sea nulo el valor del campo del filtro
  if ($values['text'] != '') {
    $query->from('sfGuardUser u')
      ->innerJoin('u.Perfil us')
      ->andWhere("us.nombres LIKE ?", '%'.$values['text'].'%');
  }

}


El nombre de la función debe ser addCampocualquieraColumnQuery.

En este caso se hace un Join interno entre las tablas sfGuardUser y perfil, agregando un WHERE para filtrar los registros que coincidan con el valor introducido en el campo de input. El valor es $values['text'] y los porcentajes a los lados son sintaxis de doctrine para permitir que la búsqueda no requiera la palabra exacta, y permita devolver resultados con sólo una parte de ella (Documentación doctrine)

Finalmente, se añade el nuevo atributo de filtro a la función getFields, que devuelve todos los campos por los que es posible filtrar:

public function getFields()
{
  return parent::getFields() + array(
      'nombres' => 'Text'

);


Se añade al array ya existente desde parent::getFields() en la clase autogenerada BasesfGuardUserFormFilter.class.php, el nuevo campo de tipo text.


Con esto sólo resta activar el nuevo campo de filtro en el archivo generator.yml del módulo sfGuardUser:

filter:
  display: [username, permissions_list, nombres]


Luego:

$ ./symfony cc

El filtro debería funcionar