Monthly Archives: July 2009

Mini curso gratuito y en línea de "MobileProcessing: Primeros pasos"

Mini Curso : Mobile Processing Primeros Pasos

Este 11 de Agosto inicia el mini curso de 8 horas sobre el desarrollo de aplicaciones para dispositivos moviles utilizando la herramienta Mobile Processing que facilita la creacion de este tipo de aplicaciones para dispositivos moviles que soporten Java. Este mini curso se realizara totalmente en linea a traves de video conferencias apoyadas por actividades y tareas en linea que permiten practicar el desarrollo de pequeños ejercicios para conocer el ambiente de desarrollo, no es necesario tener conocimientos previos en lenguajes de programacion y/o herramientas de desarrollo, simplemente tener muchas ganas de aprender, el curso es totalmente gratuito y abierto para cualquier persona interesada en conocer esta tecnologia.

Objetivo del Curso.

Este mini curso provee una introducción al desarrollo de aplicaciones para dispositivos móviles utilizando la herramienta Mobile Processing, esta enfocado para todas las personas interesadas en comenzar a explorar la creación de aplicaciones sencillas y no es necesario tener experiencia en el desarrollo de aplicaciones.

Contenidos.

  • Sesión 01 :
    Presentación del Curso,
    Dispositivos Móviles y Mobile Processing
  • Sesión 02 :
    Dibujando Formas 2D
  • Sesión 03 :
    Interacción con el Usuario
  • Sesión 04 :
    Sonidos e Imágenes
    Cierre del Curso

Horario.

Video conferencia en vivo, los martes de 10 pm, duracion 2 horas. Este horario responde a las necesidades de las personas que laboran, trabajan o estudian y solo tienen disponible las horas de la noche para asistir al curso.

Enlaces.

Experimentando con WiiUseJ en Linux Ubuntu 9.04

Introducción.

WiiUseJ es otra de las prometedoras librerías para manipular la información proveniente del Wiimote desde java que el día de hoy estoy probando después de una primera experiencia con WiiRemoteJ.

Una diferencia importante de esta librería frente a otras probadas anteriormente es que no necesita del complemento de terceras librerías de soporte como Avetana o BlueCove ya que se basa en WiiUse el cual incluye librerías nativas (para Windows y Linux) lo que le permite acceder directamente a la pila de bluetooth del sistema operativo.

Esto es regularmente un punto a favor sin embargo puede llegar a ser una desventaja ya que las librerías al ser nativas deben, especialmente en Linux, corresponder con las versiones correctas de otras librerías a las se enlazan como sucede efectivamente en este momento cuando el release 0.12 fue compilado para libbluetooth 2.0 y esta librería fue actualizada  a la versión 3.0 en Ubuntu 8.10 y posteriores, obligando a que se deban actualizar o compilar nuevamente.

Implementación.

Es necesario importar los siguientes paquetes para la conexión con el wiimote y el manejo de los eventos generados por él.

 import wiiusej.*;
 import wiiusej.wiiusejevents.*;
 import wiiusej.wiiusejevents.utils.*;
 import wiiusej.wiiusejevents.physicalevents.*;
 import wiiusej.wiiusejevents.wiiuseapievents.*;

La clase encargada de manejar los eventos deberá implementar la interfaz WiimoteListener lo que le obligará a implementar los siguientes métodos.

 public void onButtonsEvent(WiimoteButtonsEvent arg0) {}
 public void onClassicControllerInsertedEvent(ClassicControllerInsertedEvent e) {}
 public void onClassicControllerRemovedEvent(ClassicControllerRemovedEvent e) {}
 public void onIrEvent(IREvent arg0) {}
 public void onMotionSensingEvent(MotionSensingEvent arg0) {}
 public void onExpansionEvent(ExpansionEvent arg0) {}
 public void onStatusEvent(StatusEvent arg0) {}
 public void onDisconnectionEvent(DisconnectionEvent arg0) {}
 public void onNunchukInsertedEvent(NunchukInsertedEvent arg0) {}
 public void onNunchukRemovedEvent(NunchukRemovedEvent arg0) {}
 public void onGuitarHeroInsertedEvent(GuitarHeroInsertedEvent e) {}
 public void onGuitarHeroRemovedEvent(GuitarHeroRemovedEvent e) {}

Para la demostración se hizo uso del método onButtonsEvent el cual permite a través del parámetro que recibe de tipo WiimoteButtonsEvent verificar que botón del control fue presionado mediante los métodos isButtonXXXPressed donde XXX puede ser los botones A, B, One, Two, Minus, Plus, Home, Up, Down, Left y Right.

Ejecución.

Se obtuvo la última versión disponible de WiiUseJ, en este caso era la 0.12.

$ wget http://wiiusej.googlecode.com/files/wiiusej%200.12b.zip

$ wget http://wiiusej.googlecode.com/files/WiiuseJ%200.12a%20Javadoc.zip

La segunda descarga es opcional y corresponde con la documentación del API de la librería.

Coo se mencionó en la introducción, las librerías nativas para Linux incluídas en la versión binaria 0.12 se encuentran obsoletas ya que fueron compiladas con libbluetooth 2.0 mientras que las distribuciones actuales ya incluyen la versión 3.0.  Para hacer esto se descarga el siguiente archivo, se descomprime y se reemplaza el archivo libwiiuse.so con la nueva versión.

$ wget http://wiiusej.googlecode.com/files/libwiiuse.zip

Asegúrese de copiar las librerías nativas incluídas con WiiUseJ al directorio raíz de la nueva aplicación.

  • Para Windows: wiiuse.dll y WiiUseJ.dll.
  • Para Linux: libwiiuse.so y libWiiuseJ.so.

De igual manera es necesario contar con el archivo wiiusej.jar en una ubicación conocida.

Para compilar el programa de demostración invocamos el siguiente comando.

$ javac -classpath wiiusej.jar:. WiiUseJTest.java

Para ejecutar el progrmaa de instalación utilizamos el siguiente comando.

$ java  -classpath ../wiiusej.jar:. -Djava.library.path=. WiiUseJTest

Enlaces.

Experimentando con WiiRemoteJ en Linux Ubuntu 9.04

Introducción.

Hace un tiempo (parte 1 y parte 2) hice algunas pruebas acerca de como utilizar el control del Wii (wiimote) desde el PC desarrollando aplicaciones con C# utilizando la librería WiimoteLib de Brian Peek.  Desafortunadamente ni esta librería ni ninguna otra de C# que pudiera encontrar son de plataforma cruzada y por lo tanto no se pueden utilizar desde Linux así que me vi obligado a cambiarme nuevamente a mi lenguaje preferido: Java.

Después de revisar las librerías disponibles obtuve tres candidatos: WiiRemoteJ, WiiUseJ, MoteJ y WiiMote-Simple.

A pesar de que la página de WiiRemoteJ parece haber desaparecido es una de las mas mencionadas y parece ser una de las mas estables.  Requiere de una pila Bluetooth como Avetana o Bluecove para ejecutarse sobre ella.

Implementación.

Básicamente el uso de la librería se basa en la manipulación de un objeto WiiRemote y en la implementación de la interfaz WiiRemoteListener encargada de manejar los eventos provenientes del Wiimote y de sus extensiones.

Las clases se encuentran almacenadas en los siguientes paquetes.

import wiiremotej.*;
import wiiremotej.event.*;

Al implementar la mencionada interfaz es necesario especificar la implementación de los siguientes métodos.

MétodoEvento
buttonInputReceivedSe presionó un botón del wiimote.
disconnectedSe desconectó el wiimote.
extensionConnectedSe conectó una extensión al wiimote.
extensionPartiallyInsertedLa extensión recién conectada quedó mal insertada.
extensionUnknownLa extensión recién conectada es desconocida.
extensionInputReceivedLa extensión envía datos al wiimote.
extensionDisconnectedLa extensión ha sido desconectada del wiimote.
accelerationInputReceivedEl wiimote ha generado información de aceleración.
IRInputReceivedEl wiimote ha generado información de su cámara infrarroja.
statusReportedHa sucedido un reporte de estado.
combinedInputReceivedSuceso de eventos combinados.

Evento de presión de botones.

 public void buttonInputReceived(WRButtonEvent evt)
 {
     // boolean     isAnyPressed(int buttonMask)    Returns true if any of the given buttons are pressed; otherwise false.
     // boolean     isOnlyPressed(int buttonMask)   Returns true if all of the given buttons are pressed and no others are; otherwise false.
     // boolean     isPressed(int buttonMask)       Returns true if all of the given buttons are pressed; otherwise false.
     // boolean     wasOnlyPressed(int buttonMask)  Returns true if all of the given buttons were pressed and no others are; otherwise false.
     // boolean     wasPressed(int buttonMask)      Returns true if all of the given buttons were pressed; otherwise false.
     // boolean     wasReleased(int buttonMask)     Returns true if one of the given buttons was released; otherwise false.
     if (evt.wasPressed(WRButtonEvent.TWO))
         this.log("2");
     if (evt.wasPressed(WRButtonEvent.ONE))
         this.log("1");
     if (evt.wasPressed(WRButtonEvent.B))
         this.log("B");
     if (evt.wasPressed(WRButtonEvent.A))
         this.log("A");
     if (evt.wasPressed(WRButtonEvent.MINUS))
         this.log("Minus");
     if (evt.wasPressed(WRButtonEvent.HOME))
         this.log("Home");
     if (evt.wasPressed(WRButtonEvent.LEFT))
         this.log("Left");
     if (evt.wasPressed(WRButtonEvent.RIGHT))
         this.log("Right");
     if (evt.wasPressed(WRButtonEvent.DOWN))
         this.log("Down");
     if (evt.wasPressed(WRButtonEvent.UP))
         this.log("Up");
     if (evt.wasPressed(WRButtonEvent.PLUS))
         this.log("Plus");
 }

Evento de desconexión del control.

 public void disconnected()
 {
     this.log("The remote controller was disconnected.");
     wiimote = null;
 }

Eventos de la extensión.

 public void extensionConnected(WiiRemoteExtension extension)
 {
     this.log("An extension has been connected! (" +
     extension.getCode() + "): " +
     this.getExtensionTypeName(extension));
     try
     {
         wiimote.setExtensionEnabled(true);
     }
     catch(Exception e)
     {
         this.log(e.getMessage());
     }
 }
 public void extensionPartiallyInserted()
 {
     this.log("An extension was partially inserted, push it harder next time");
 }
 public void extensionUnknown()
 {
     this.log("The extension is unknown, what are you putting into me ?");
 }
 public void extensionInputReceived(WRExtensionEvent evt)
 {
     // this.log("The extension is sending me some kind of data ... ");
 }
 public void extensionDisconnected(WiiRemoteExtension extension)
 {
     this.log("An extension has been disconnected! (" +
     extension.getCode() + "): " +
     this.getExtensionTypeName(extension));
 }

Evento de aceleración.

 public void accelerationInputReceived(WRAccelerationEvent evt)
 {
     // double     getPitch()         Returns the pitch of the remote, in radians from -PI to PI.
     // double     getRoll()          Returns the roll of the remote, in radians from 0 to 2PI.
     // double     getXAcceleration() Returns the X-Acceleration of the remote, in terms of G's.
     // double     getYAcceleration() Returns the Y-Acceleration of the remote, in terms of G's.
     // double     getZAcceleration() Returns the Z-Acceleration of the remote, in terms of G's.
     // boolean    isStill()          Returns true if the remote is not being moved; otherwise false.
 }

Evento de la cámara infrarroja.

 public void IRInputReceived(WRIREvent evt)
 {
     // IRLight[] getIRLights()       Returns the array of IR lights seen by the IR sensor.
     // int          getMode()        Returns the mode of the IR sensor.    
 }

Evento de reporte de estado.

 public void statusReported(WRStatusEvent evt)
 {
     // double      getBatteryLevel()       Returns the battery level of the remote.
     // boolean[]   getLEDStatus()          Returns the status of the LED lights.
     // boolean     getLEDStatus(int light) Returns the status of the specified light.
     // boolean     isContinuousEnabled()   Returns the status of continuous reporting.
     // boolean     isExtensionConnected()  Returns the status of the extension port.
     // boolean     isSpeakerEnabled()      Returns the status of the speaker.    
 }

Reporte de recepción combinada de eventos.

 public void combinedInputReceived(WRCombinedEvent evt)
 {
     // WRAccelerationEvent getAccelerationEvent()  Returns the acceleration event of this combined event.
     // WRButtonEvent       getButtonEvent()        Returns the button event of this combined event.
     // WRExtensionEvent    getExtensionEvent()     Returns the extension event of this combined event.
     // WRIREvent           getIREvent()            Returns the IR event of this combined event.
 }

Ejecución.

Para el desarrollo de esta aplicación de demostración se utilizó la librería de BlueCove, en este caso en la versión 2.10.  Es obligatorio obtener el paquete bluecove-*.jar mientras que bluecove-gpl-*.jar sólo es requerido bajo Linux.

$ wget http://bluecove.googlecode.com/files/bluecove-2.1.0.jar

$ wget http://bluecove.googlecode.com/files/bluecove-gpl-2.1.0.jar

Además se requiere de la librería WiiRemoteJ.jar, en este caso en la versión 1.6.

$ wget http://www.world-of-cha0s.hostrocket.com/WiiRemoteJ/WiiRemoteJ%20v1.6.zip.gz

$ cp WiiRemoteJ\ v1.6/WiiRemoteJ.jar .

Se compila el programa de demostración.

$ javac -classpath bluecove-2.1.0.jar:bluecove-gpl-2.1.0.jar:WiiRemoteJ.jar:. WiiRemoteJTest.java

Se ejecuta.

$ java  -classpath bluecove-2.1.0.jar:bluecove-gpl-2.1.0.jar:WiiRemoteJ.jar:. WiiRemoteJTest

Al iniciar la aplicación se debe presionar el botón que dice "Buscar Wiimote" y presionar los botones 1 y 2 del control para ponerlo en modo de descubrimiento.  Después de encontrado los eventos (botón, extensión y desconexión) generados en él deberán ser documentados en la ventana de la aplicación.

WiiRemoteJTest 0.1

WiiRemoteJTest 0.1

Enlaces.

http://www.world-of-cha0s.hostrocket.com/WiiRemoteJ/WiiRemoteJ%20v1.6.zip.gz
http://www.world-of-cha0s.hostrocket.com/WiiRemoteJ/WiiRemoteJ%20v1.6.zip.gz

Experimentando con Yii y DAO: manipulando formularios y registros simples

Introducción.

El día de hoy amanecí con ganas de seguir experimentando con Yii.  Al ya tener documentado el acceso a la base de datos a través de DAO y todo el manejo de los formularios es hora de probar la teoría con algo un poco mas elaborado.

El experimento de hoy consiste en crear una tabla de Usuarios (nombre de usuario, contraseña y edad) y un formulario que permita su administración (agregar, editar, listar y remover), utilizando por supuesto formularios basados en CFormModel y el acceso a la base de datos con CDbCommand.

Screenshot_0.1

Vista del formulario de la versión 0.1

Instalación preliminar.

Mi equipo utiliza Linux Ubuntu 9.04 por lo que algunos procedimientos son particulares al estilo *nix, en especial los relacionados con la instalación de paquetes y el manejo de la línea de comando, y será necesario realizar ajustes específicos si se desea utilizarlos en Windows.

  • Realicé la instalación de Apache y PHP (con soporte para SQLite 3.x) de acuerdo con los pasos establecidos anteriormente.
  • Realicé la instalación del framework de Yii versión 1.0.7 sin modificaciones significativas a lo establecido anteriormente.  La estructura inicial de directorios es la siguiente.

/home/web              Directorio base de los archivos relacionados con web.

/yii               Directorio de distribuciones de Yii.

/1.0.7         Distribución 1.0.7 de Yii.

/current       Enlace dinámico a la distribución mas reciente de Yii.

/public Directorio público (Document Root).

/YiiDaoDemo    Directorio base de la aplicación de prueba.

Para facilidad en el acceso de la aplicación yiic se realizó también el siguiente ajuste al final ~/.profile.

PATH=$PATH:/home/web/yii/current/framework
export PATH

Creación de los archivos base de la aplicación.

$ cd /home/web/public

$ yiic webapp YiiDaoDemo

Create a Web application under '/home/web/public/YiiDaoDemo'? [Yes|No] Yes
mkdir /home/web/public/YiiDaoDemo
mkdir /home/web/public/YiiDaoDemo/protected
mkdir /home/web/public/YiiDaoDemo/protected/views
mkdir /home/web/public/YiiDaoDemo/protected/views/system
generate protected/views/system/.yii
mkdir /home/web/public/YiiDaoDemo/protected/views/layouts
generate protected/views/layouts/main.php
mkdir /home/web/public/YiiDaoDemo/protected/views/site
generate protected/views/site/login.php
generate protected/views/site/contact.php
generate protected/views/site/index.php
generate protected/yiic.bat
mkdir /home/web/public/YiiDaoDemo/protected/controllers
generate protected/controllers/SiteController.php
generate protected/yiic.php
mkdir /home/web/public/YiiDaoDemo/protected/components
mkdir /home/web/public/YiiDaoDemo/protected/components/views
generate protected/components/views/mainMenu.php
generate protected/components/UserIdentity.php
generate protected/components/MainMenu.php
mkdir /home/web/public/YiiDaoDemo/protected/runtime
generate protected/runtime/.yii
mkdir /home/web/public/YiiDaoDemo/protected/messages
generate protected/messages/.yii
mkdir /home/web/public/YiiDaoDemo/protected/config
generate protected/config/main.php
generate protected/config/console.php
mkdir /home/web/public/YiiDaoDemo/protected/models
generate protected/models/LoginForm.php
generate protected/models/ContactForm.php
mkdir /home/web/public/YiiDaoDemo/protected/extensions
generate protected/extensions/.yii
generate protected/.htaccess
mkdir /home/web/public/YiiDaoDemo/protected/commands
mkdir /home/web/public/YiiDaoDemo/protected/commands/shell
generate protected/commands/shell/.yii
generate protected/yiic
mkdir /home/web/public/YiiDaoDemo/themes
mkdir /home/web/public/YiiDaoDemo/themes/classic
mkdir /home/web/public/YiiDaoDemo/themes/classic/views
mkdir /home/web/public/YiiDaoDemo/themes/classic/views/system
generate themes/classic/views/system/.yii
mkdir /home/web/public/YiiDaoDemo/themes/classic/views/layouts
generate themes/classic/views/layouts/.yii
generate themes/classic/views/.htaccess
mkdir /home/web/public/YiiDaoDemo/themes/classic/views/site
generate themes/classic/views/site/.yii
mkdir /home/web/public/YiiDaoDemo/css
generate css/bg.gif
generate css/form.css
generate css/main.css
mkdir /home/web/public/YiiDaoDemo/images
generate images/.yii
generate index.php
mkdir /home/web/public/YiiDaoDemo/assets
generate assets/.yii

Your application has been created successfully under /home/web/public/YiiDaoDemo.

Creación y configuración de la base de datos.

$ mkdir -p YiiDaoDemo/protected/data

$ cd YiiDaoDemo/protected

$ vi data/script.sql

CREATE TABLE usuario
(
 username        CHAR(16)        NOT NULL,
 contrasena      CHAR(32)        NOT NULL,
 edad            INT             ,
 PRIMARY KEY(username)
);

$ sqlite3 data/basededatos.db < data/script.sql

$ chown -R www-data data

$ ls -l data

-rw-r–r– 1 www-data jimezam 3072 2009-07-20 15:16 basededatos.db
-rw-r–r– 1 www-data jimezam  126 2009-07-20 15:16 script.sql

$ vi config/main.php

return array(
    // ...
    // application components
    'components'=>array(
        //...
        'db'=>array(
            'connectionString'=>'sqlite:protected/data/basededatos.db',
        ),
        //...

Creación del modelo para el formulario (UsuarioForm).

  • Al ser un modelo para manejar la información proveniente de informulario, hereda de CFormModel.
    class UsuarioForm extends CFormModel
    {
        // ...
    }
  • Se almacena en el archivo YiiDaoDemo/protected/models/UsuarioForm.php.
  • Incluye los siguientes atributos de acuerdo con su contenido.
    public $username;     /* Nombre de usuario */
    public $contrasena;   /* Contraseña del usuario */
    public $contrasena2;  /* Confirmación de la contraseña */
    public $edad;         /* Edad del usuario */
  • Se definen las siguientes etiquetas para cada uno de los atributos descritos anteriormente.
    public function attributeLabels()
    {
        return array(
            'username'    => 'Nombre de usuario',
            'contrasena'  => 'Contraseña',
            'contrasena2' => 'Contraseña (repetir)',
            'edad'        => 'Edad'
        );
    }
  • Se establecen las restricciones de validación para cada uno de los atributos.
    public function rules()
    {
        return array (
            array('username, contrasena, contrasena2, edad', 'required'),
            array('edad', 'numerical', 'integerOnly' => true),
            array('contrasena', 'compare', 'compareAttribute' => 'contrasena2'),
            array('username', 'length', 'max' => 16),
            array('contrasena', 'length', 'max' => 16),
            array('contrasena2', 'length', 'max' => 16)
        );
    }

Creación de la vista del formulario.

  • Será manejada por el modelo UsuarioForm.
  • Contiene dos partes generales.
    1. Un formulario que permite ingresar la información del Usuario para después agregarlo o editar su contenido de la base de datos.
    2. Un listado de los usuarios registrados en la base de datos desde el cual es posible remover los usuarios marcados o editar la información del usuario elegido.
  • Es la única vista de la aplicación así que será la vista de la acción index del controlador (AdminUsuario) a definirse próximamente.
  • Se almacena en el archivo YiiDaoDemo/protected/views/adminUsuario/index.php.
  • En la parte superior se muestran los mensajes flash provenientes de otras acciones.  Esto es útil para mostrar mensajes de error o éxito como "registro insertado".
    <h1>Administración de usuarios</h1>
    <?php if(Yii::app() -> user -> hasFlash('mensajeEstado')):?>
    <div style="color: #0000FF">
     <?php echo Yii::app() -> user -> getFlash('mensajeEstado'); ?>
    </div>
    <?php endif; ?>
  • La primera parte del formulario permite manipular la información del Usuario.
    <h2>Datos del usuario</h2>
    <!-- Abre el formulario especificando su ACTION y METHOD -->
    <?php echo CHtml::beginForm(CHtml::normalizeUrl(array('adminUsuario/index')), 'post'); ?>  
    <div class='formulario'>  
     <!-- Muestra los mensajes de error asociados al modelo UsuarioForm -->
     <!-- El objeto $usuarioForm es enviado desde el controlador -->
     <?php echo CHtml::errorSummary($usuarioForm); ?>  
     <!-- Se genera el formulario con las etiquetas y valores asociados al modelo UsuarioForm -->
     <table>
     <tr>
         <td><?php echo CHtml::activeLabel($usuarioForm, 'username'); ?></td>
         <td><?php echo CHtml::activeTextField($usuarioForm, 'username'); ?> </td>
     </tr>
     <tr>
         <td><?php echo CHtml::activeLabel($usuarioForm, 'contrasena'); ?></td>
         <td><?php echo CHtml::activePasswordField($usuarioForm, 'contrasena'); ?></td>
     </tr>
     <tr>
         <td><?php echo CHtml::activeLabel($usuarioForm, 'contrasena2'); ?></td>
         <td><?php echo CHtml::activePasswordField($usuarioForm, 'contrasena2'); ?></td>
     </tr>
     <tr>
         <td><?php echo CHtml::activeLabel($usuarioForm, 'edad'); ?></td>
         <td><?php echo CHtml::activeTextField($usuarioForm, 'edad'); ?></td>
     </tr>
     </table>
     <br />
     <!-- Genera el botón de enviar el cual cumplirá funciones de Agregar y Editar -->
     <!-- según exista o no el registro en la base de datos -->
     <div class='acciones'>
         <?php echo CHtml::submitButton('Enviar', array('id' => 'enviar')); ?>
     </div>  
    </div>  
    <!-- Cierra el formulario -->
    <?php echo CHtml::endForm(); ?>
  • La segunda parte muestra un listado de los usuarios registrados en la base de datos con botones para las siguientes acciones.
    • Remover: eliminar de la base de datos a los usuarios cuyos registros se encuentren señalados con la marca.
    • Editar: editar la información del usuario correspondiente a la fila elegida.
      <h2>Listado de usuarios registrados</h2>
      <!-- Abre el formulario especificando su ACTION y METHOD -->
      <?php echo CHtml::beginForm(CHtml::normalizeUrl(array('adminUsuario/editar')), 'post'); ?>
      <!-- Crea un campo oculto para guardar el ID del registro a editarse -->
      <?php echo CHtml::hiddenField('UsuarioForm[elemento]', '', array()); ?>
      <!-- Genera el listado de los usuarios registrados -->
      <table style='border: 1px solid #FF0000;'>
      <tr style='background-color: pink; text-align: center; font-weight: bold;'>
          <td>Marcas</td>
          <td>Username</td>
          <td>Edad</td>
          <td>Opciones</td>
      </tr>
      <!-- El listado de $usuarios es enviado por el controlador a la vista -->
      <?php $i = 0; ?>
      <?php foreach($usuarios as $usuario): ?>
      <tr>
          <!-- Crea el checkbox marca[$i], no seleccionado y cuyo valor es el nombre del usuario asociado -->
          <td style='text-align: center;'>
              <?= CHtml::checkBox("UsuarioForm[marca][{$i}]", false, array('value' => $usuario['username'])); ?>
          </td>
          <!-- Nombre del usuario -->
          <td><?= $usuario['username']; ?></td>
          <!-- Edad del usuario -->
          <td style='text-align: center;'><?= $usuario['edad']; ?></td>
          <!-- Botón de editar del usuario asociado -->
          <!-- Al presionarse actualiza el valor del campo oculto 'elemento' con el -->
          <!-- el nombre del usuario asociado para la edición -->
          <td><?= CHtml::submitButton('Editar', array(
              'onClick' => "document.getElementById('UsuarioForm_elemento').value = '{$usuario['username']}'"
          )); ?></td>
      </tr>
      <?php $i++; ?>
      <?php endforeach; ?>
      </table>
      <br />
      <!-- Botón para remover los usuarios seleccionados -->
      <!-- Al presionarse actualiza el ACTION del formulario para ser manejado por la -->
      <!-- acción correcta del controlador -->
      <?= CHtml::submitButton('Remover', array(
          'onClick' => "document.getElementById('formListado').action = '" . CHtml::normalizeUrl(array('adminUsuario/remover')) . "'"
      )); ?>
      <!-- Cierra el formulario -->
      <?php echo CHtml::endForm(); ?>

Creación del modelo para el usuario.

  • Al ser un modelo para manejar la persistencia de los datos, hereda de CActiveRecord.  Debe tenerse en cuenta que en este experimento en particular se utilizarán los métodos de  DAO en lugar de las facilidades que el ActiveRecord provee directamente.
    class Usuario extends CActiveRecord
    {
        // ...
    }
  • Se almacena en el archivo YiiDaoDemo/protected/models/Usuario.php.
  • Se definen cuales atributos pueden ser asignados de manera automática.
    public function safeAttributes()  
    {  
        return 'username, contrasena, edad';  
    }
  • Se implementa el agregar el usuario a la base de datos mediante un INSERT de SQL.
       	public function agregar()
    	{
    		$sql = "INSERT INTO Usuario(username, contrasena, edad) " .
    		       "VALUES (:username, :contrasena, :edad)";
    		$comando = Yii::app() -> db -> createCommand($sql);
    		$comando -> bindParam(":username", $this -> username, PDO::PARAM_STR);
    		$comando -> bindParam(":contrasena", $this -> contrasena, PDO::PARAM_STR);
    		$comando -> bindParam(":edad", $this -> edad, PDO::PARAM_INT);
    		$control = $comando -> execute();
    		return ($control > 0);
    	}
  • Se implementa el editar el usuario de la base de datos mediante un UPDATE de SQL.
    	public function actualizar()
    	{
    		$sql = "UPDATE Usuario SET contrasena=:contrasena,edad=:edad " .
    		       "WHERE username=:username";
    		$comando = Yii::app() -> db -> createCommand($sql);
    		$comando -> bindParam(":username", $this -> username, PDO::PARAM_STR);
    		$comando -> bindParam(":contrasena", md5($this -> contrasena), PDO::PARAM_STR);
    		$comando -> bindParam(":edad", $this -> edad, PDO::PARAM_INT);
    		$control = $comando -> execute();
    		return ($control > 0);
    	}
  • Se implementa el remover el usuario de la base de datos mediante un DELETE de SQL.
    	public function remover()
    	{
    		$sql = "DELETE FROM Usuario WHERE username=:username";
    		$comando = Yii::app() -> db -> createCommand($sql);
    		$comando -> bindParam(":username", $this -> username, PDO::PARAM_STR);
    		$control = $comando -> execute();
    		return ($control > 0);
    	}
  • Se implementa el recuperar el usuario de la base de datos mediante un SELECT de SQL.
            public function recuperar()
    	{
    		$sql = "SELECT * FROM usuario WHERE username = :username";
    		$comando = Yii::app() -> db -> createCommand($sql);
    		$comando -> bindParam(":username", $this -> username, PDO::PARAM_STR);
    		$fila = $comando -> queryRow();
    		if($fila === false)
    			return false;
    		$this -> username   = $fila['username'];
    		$this -> contrasena = $fila['contrasena'];
    		$this -> edad       = $fila['edad'];
    		return true;
           }
  • De manera similar se implementa el existe que verifica si el usuario, identificado por su nombre de usuario, ya se encuentra registrado en la base de datos o no.
    	public function existe()
    	{
    		$sql = "SELECT username FROM usuario WHERE username = :username";
    		$comando = Yii::app() -> db -> createCommand($sql);
    		$comando -> bindParam(":username", $this -> username, PDO::PARAM_STR);
    		return ($comando -> queryScalar() == $this -> username);
    	}
  • Se implementa el método estático listarTodos que retorna el listado completo de los usuarios registrados en la base de datos.
    	public static function listarTodos()
    	{
    		$sql = "SELECT * FROM usuario";
    		return Yii::app() -> db -> createCommand($sql) ->  query();
    	}

Creación del controlador.

  • Se almacena en el archivo YiiDaoDemo/protected/controllers/AdminUsuarioController.php.
  • Se definen tres acciones diferentes.
    • index: maneja el agregar y editar registros (primer FORM).
    • editar: maneja el presionar el botón de editar para mostrar la información del usuario en el formulario (segundo FORM).
    • remover: maneja el presionar el botón de remover para eliminar de la base de datos a los usuarios seleccionados (segundo FORM).
      class AdminUsuarioController extends CController
      {
          // ...
      }
  • Acción editar: sucede cuando el usuario presiona el botón Editar frente a uno de los registros de los usuarios.  El sistema debe responder mostrando los datos del usuario seleccionado en el primer formulario.
    	public function actionEditar()
    	{
                    /* Verificar que se cuente con información del usuario (request) */
    		if(!isset($_POST['UsuarioForm']))
    		{
    			Yii::app() -> request -> redirect(CHtml::normalizeUrl(array('adminUsuario/index')));
    			exit;
    		}
                    /* Se obtiene el valor del 'elemento' que deberá tener el nombre del usuario a editar */
    		$elemento = $_POST['UsuarioForm']['elemento'];
                    /* Se intenta recuperar el usuario solicitado */
    		$usuario = new Usuario();
    		$usuario -> username = $elemento;
    		$control = $usuario -> recuperar();
    		$usuarioForm = new UsuarioForm();
                    /* Si se tiene éxito, se pasa la información del Usuario al UsuarioForm
                       para ser mostrado en la vista */
    		if($control)
    		{
    			$usuarioForm -> attributes = array(
    				'username' => $usuario -> username,
    				'edad' => $usuario -> edad
    			);
    		}
    		else
    		{
                            /* Si se fracasa, se muestra en la vista el nombre de usuario y un mensaje flash */
    			$usuarioForm -> username = $usuario -> username;
    			$usuarioForm -> addError('username', 'El usuario elegido no existe en la base de datos');
    		}
                    /* Se muestra la vista del formulario con la información recuperada y el listado
                       de usuarios registrados */
    		$this -> render('index',
    			array(
    				'usuarioForm' => $usuarioForm,
    				'usuarios' => Usuario::listarTodos()
    			));
    	}
  • Acción remover: sucede cuando el usuario presiona el botón de remover usuarios en la parte inferior.  El sistema debe responder eliminando de la base de datos a los usuarios seleccionados.
    	public function actionRemover()
    	{
                    /* Verifica que se cuente con información del usuario (request),
                       de lo contrario, redirecciona a la página inicial */
    		if(!isset($_POST['UsuarioForm']))
    		{
    			Yii::app() -> request -> redirect(CHtml::normalizeUrl(array('adminUsuario/index')));
    			exit;
    		}
                    /* Obtiene las marcas del formulario, los valores de las marcas corresponden a los
                       los nombres de los usuarios a remover */
    		$usuarios = array_values($_POST['UsuarioForm']['marca']);
    		$total = 0;
    		$borrados = 0;
    		foreach($usuarios as $username)
    		{
                            /* Crea un nuevo usuario con el nombre de usuario a remover */
    			$usuario = new Usuario();
    			$usuario -> username = $username;
                            /* Ejecuta su remoción */
    			if($usuario -> remover())
    				$borrados ++;
    			$total ++;
    		}
                    /* Prepara un mensaje flash con el estado final de la remoción */
    		Yii::app() -> user -> setFlash('mensajeEstado', "Se removieron {$borrados} de {$total} registros.");
                    /* Redirecciona a la página inicial de la aplicación */
    		Yii::app() -> request -> redirect(CHtml::normalizeUrl(array('adminUsuario/index')));
    		exit;
    	}
  • Acción index: sucede cuando la aplicación es invocada por primera vez, el sistema debe responder mostrando el formulario vacío.  También es invocada cuando el usuario presiona el botón Enviar para editar o agregar un nuevo usuario a la base de datos, el sistema debe responder realizando la acción solicitada.
    	public function actionIndex()
    	{
    		$usuarioForm = new UsuarioForm();
                    /* Si se cuenta con información del usuario (request) entonces
                       se agregará un nuevo usuario o se realizará una edición del mismo */
    		if(isset($_POST['UsuarioForm']))
    		{
                            /* Carga el modelo con la información del formulario */
    			$usuarioForm -> attributes = $_POST['UsuarioForm'];
                            /* Verifica que su información sea válida */
    			if($usuarioForm -> validate())
    			{
                                    /* Crea un Usuario con su información */
    				$usuario = new Usuario();
    				$usuario -> attributes = $_POST['UsuarioForm'];
    				try
    				{
    					$control = false;
                                            /* Si el usuario existe ... */
    					if(!$usuario -> existe())
    					{
                                                    /* Se intenta agregarlo */
    						$control = $usuario -> agregar();
    					}
    					else
    					{
                                                    /* De lo contrario, se intenta actualizar */
    						$control = $usuario -> actualizar();
    					}
                                            /* Si la acción tuvo éxito ... */
    					if($control)
    					{
                                                    /* Muestra un mensaje flash de éxito */
    						Yii::app() -> user -> setFlash('mensajeEstado', 'El registro fue incorporado exitosamente ' .
    						                                                'en la base de datos');
                                                    /* Limpia los valores del formulario */
    						$usuarioForm = new UsuarioForm();
    					}
    					else
    					{
                                                    /* Si falló ... muestra un mensaje flash de fracaso */
    						$usuarioForm -> addError('username', 'El registro no pudo ser incorporado en ' .
    						                                     'la base de datos, inténtelo nuevamente');
    					}
    				}
    				catch (Exception $e)
    				{
                                            /* Si se lanzó una excepción durante el proceso, esta se muestra y
                                               considera como un error */
    					$usuarioForm -> addError('username', $e -> getMessage());
    				}
    			}
    		}
                    /* Se muestra la vista del formulario con la información recuperada y el listado
                       de usuarios registrados */
    		$this -> render('index',
    		                array(
    					'usuarioForm' => $usuarioForm,
    					'usuarios' => Usuario::listarTodos()
    				));
    	}

Conclusiones.

  • A pesar de que Yii parece ser un framework muy interesante para el desarrollo con PHP aún no me siento cómodo con él.
  • Aún quedan varios temas para revisar y experimentar de Yii con los que espero se aclare un poco mas el panorama.
  • El acceso a la base de datos mediante los objetos DAO es muy claro y fácil de implementar.
  • Para la implementación del CRUD es mas conveniente utilizar el ActiveRecord que se estará analizando próximamente y utilizar el DAO para las consultas demasiado complejas.
  • En este, mi tercer programita en Yii, se aclararon varias dudas que tenía, sin embargo persisten otras, en particular esta: cómo limpio/filtro la información proveniente del usuario ?

Enlaces.

public function agregar()
{
$sql = "INSERT INTO Usuario(username, contrasena, edad) " .
"VALUES (:username, :contrasena, :edad)";

$comando = Yii::app() -> db -> createCommand($sql);

$comando -> bindParam(":username", $this -> username, PDO::PARAM_STR);
$comando -> bindParam(":contrasena", $this -> contrasena, PDO::PARAM_STR);
$comando -> bindParam(":edad", $this -> edad, PDO::PARAM_INT);

$control = $comando -> execute();

return ($control > 0);
}

Controlar presentaciones con el wiimote en Linux Ubuntu 9.04 utilizando CWiid

Introducción.

Otra forma de controlar las aplicaciones desde el wiimote al relacionar botones del control con teclas que afectan las aplicaciones es utilizando la librería CWiid.  Esta opción es mucho mas simple aunque puede llegar a ser mas limitada en su funcionalidad que la basada en WiiPresent.

Instalación.

Instalación de CWiid.

$ sudo apt-get install libcwiid1 lswm wmgui wminput

Los paquetes principales son libcwiid1, wminput y wmgui.

Instalación de LibWiimote.

Realice los pasos para tal fin enunciados en el artículo Instalación de wiipresent en Linux Ubuntu 9.04.

Cargar el módulo del kernel.

$ sudo modprobe uinput

Si se desea que el cambio surta efecto para siempre, es decir, perdure después de reiniciar el sistema operativo realice los siguientes pasos.

  1. $ gksudo gedit /etc/modules
  2. Agregue el nombre del módulo uinput en una línea nueva al final de /etc/modules.

Crear un archivo de configuración para OpenOffice Presentation/Microsoft Powerpoint.

$ sudo vi /etc/cwiid/wminput/Presentations

#########################################################

Wiimote.A       = KEY_PAGEDOWN
Wiimote.B       = KEY_PAGEUP

Wiimote.Up      = KEY_UP
Wiimote.Down    = KEY_DOWN
Wiimote.Left    = KEY_LEFT
Wiimote.Right   = KEY_RIGHT

Wiimote.Minus   = KEY_MINUS
Wiimote.Plus    = KEY_KPPLUS
Wiimote.Home    = KEY_HOME
Wiimote.1       = KEY_F5
Wiimote.2       = KEY_ESC

#########################################################

Las constantes de las teclas pueden consultarse en la siguiente ubicación.

http://abstrakraft.org/cwiid/browser/trunk/wminput/action_enum.txt

Con la configuración propuesta en OpenOffice Presentation/Powerpoint se tienen las acciones siguientes.

  1. Flechas (arriba, abajo, izquierda y derecha): Control del cursor/posición de la imagen.
  2. B: retroceder una imagen.
  3. A: avanzar una imagen.
  4. - (menos): disminuye el acercamiento.
  5. + (mas): aumenta el acercamiento.
  6. Home (inicio): va a la primera imagen de la presentación.
  7. 1: inicia el modo presentación.
  8. 2: finaliza el modo presentación/cancelar.

Ejecutar.

Identificar la dirección del wiimote.

$ hcitool scan

Presionar los botones 1 y 2 del wiimote hasta obtener un mensaje similar al siguiente.

00:17:AB:39:DC:B0 Nintendo RVL-CNT-01

Invocar la aplicación.

$ sudo wminput -d -c Presentations <DIRECCIÓN DEL WIIMOTE>

Presionar los botones 1 y 2 del wiimote.

Enlaces.

Instalación de wiipresent en Linux Ubuntu 9.04, un largo camino

Introducción.

Wiipresent es una aplicación muy interesante que nos permite controlar las aplicaciones de Linux desde el Wiimote mediante la conversión entre botones y movimientos del control a combinaciones de teclas.  Es decir, algo análogo a lo que las aplicaciones de Jason Smith e IndyProject le proveen al mundo de Windows.

La aplicación que estoy probando se llama WiiPresent y fue desarrollada por Dag Wieers.  Su instalación fue un tanto truculenta así que amerita ser documentada.

Instalación.

Instalación de libwiimote.

Libwiimote es una librería desarrollada en C que permite el acceso al control del Wiimote desde Linux.  Su código fuente puede descargarse desde la siguiente ubicación.

http://libwiimote.sourceforge.net/

Para la instalación de WiiPresent es necesaria la versión 0.3 o superior de LibWiimote, desafortunadamente la última versión disponible que encontré en Launchpad era la 0.2 por lo cual fue necesario compilar e instalar las fuentes.

Instalar BlueZ si es que no se cuenta con él aún.

$ sudo aptitude install bluez bluez-compat

Descargar la última versión disponible de LibWiimote de la siguiente ubicación.  En este caso se utilizó la 0.4.

http://sourceforge.net/projects/libwiimote/files/

Descomprimir las fuentes.

$ tar zxvf libwiimote-0.4.tgz

$ cd libwiimote-0.4/

Debido a un cambio reciente en el API de HCI es necesario reemplazar la invocación de la función hci_remote_name por su nuevo nombre hci_read_remote_name en las siguientes ubicaciones.

  1. vi src/wiimote_link.c
  2. vi configure.in

Configurar, compilar e instalar la librería.

$ autoconf

$ ./configure

$ make

$ sudo make install

$ sudo ln -s /usr/local/lib/libcwiimote.so.0.4.0 /usr/local/lib/libcwiimote.so

$ sudo ldconfig

Instalación de wiipresent.

Descargar la última versión disponible de la siguiente ubicación.

http://dag.wieers.com/home-made/wiipresent/

Para este caso se utilizó la versión 0.7.2.

$ wget http://dag.wieers.com/home-made/wiipresent/wiipresent-0.7.2.tar.bz2

$ bunzip2 wiipresent-0.7.2.tar.bz2

$ tar xvf wiipresent-0.7.2.tar

$ cd wiipresent-0.7.2

Actualizar el archivo Makefile con la ubicación donde se instaló la librería libwiimote.

$ vi Makefile

Reemplazar la cadena /usr/include/libcwiimote por /usr/local/include/libcwiimote-0.4.0/libcwiimote.

Instalar otras librerías necesarias.

$ sudo aptitude install libxtst-dev

$ sudo apt-get install asciidoc

asciidoc es opcional para la generación de la documentación de la aplicación.  De cualquier manera esta puede accederse desde el sitio web de la librería.

Compilar e instalar la aplicación.

$ make

$ sudo make install

Uso.

Primer paso: conocer la dirección del wiimote.

  1. Abrir una consola.
  2. Ejecutar el comando hcitool scan.
  3. Presionar los botones 1 y 2 del wiimote.
  4. Tomar nota de la dirección del wiimote la cual deberá ser algo similar a la siguiente.

    00:17:AB:39:DC:B0 Nintendo RVL-CNT-01

Segundo paso: ejecutar wiipresent.

  1. Ejecutar la aplicación.

    $ wiipresent -b <DIRECCIÓN DEL WIIMOTE> -r &

  2. Presionar los botones 1 y 2 del wiimote.

Tercer paso: abrir una aplicación como OpenOffice Presentation y controlarla con el wiimote.

  • Izquierda/Derecha: adelantan y retroceden.
  • A: siguiente.
  • Home: va al incio de la presentación.
  • +/-: aumenta y disminuye el volúmen.
  • 1: entra y sale del modo pantalla completa.

Enlaces.

Data Access Objects (DAO) con Yii

Introducción.

Establecer la conexión.

De manera explícita.

  • Se basa en la creación de un objeto CDbConnection, la especificación de un Data Source Name (DSN) y la información de autenticación del usuario de conexión si es necesaria.
    $conexion = CDbConnection($DSN, $username, $password);
    $conexion -> active = true;        // Abrir la conexión, se lanzan excepciones en error.
    // ...
    $conexion -> active = false;       // Cerrar la conexión.
  • El formato del DSN depende del módulo de PDO utilizado.
    1. SQLite: sqlite:/ruta/al/archivo/sqlite
    2. MySQL: mysql:host=<HOST>;dbname=<NAME>
    3. PostgreSQL: pgsql:host=<HOST>;port=<PORT [5432]>;dbname=<NAME>
    4. MSSQL: mssql:host=<HOST>;dbname=<NAME>
    5. Oracle: oci:dbname=//<HOST>:<PORT [1521]>/<NAME>

De manera automática.

  • La clase CDbConnection es un CApplicationComponent por lo que puede ser configurado como un componente de aplicación.
  • Para hacer esto es necesario modificar la configuración de la aplicación (protected/config/main.php) de la siguiente manera para agregar al componente db.
    array (
        // ...
        'components' => array (
            'db' => array (
                'class' => 'CDbConnection',
                'connectionString' => 'mysql:host=database.server.com;dbname=demo_db',
                'username' => 'demo_user',
                'password' => 'demo_pass'
            )
        )
    );
  • De esta manera la conexión se realiza de manera automática y puede accederse mediante Yii::app() -> db.

Ejecutar sentencias.

  • Es necesario crear un CDbCommand a través de CDbConnection::createCommand().
    $comando = $conexion -> createCommand($sql);
    // $comando -> text = $nuevoSQL;              // Actualiza el SQL de un comando
  • La sentencia puede ejecutarse de dos maneras.
    1. execute() para sentencias de modificación como INSERT, UPDATE y DELETE.  En éxito retorna el número de registros afectados.
    2. query() para sentencias de consulta (SELECT).  En éxito retorna un CDbDataReader.
  • Existen otros métodos mas específicos del estilo queryXXX() que facilitan la obtención de los resultados.
    1. $filas = $comando -> queryAll().   Consulta y retorna inmediatamente todas las filas del resultado.
    2. $fila = $comando -> queryRow().   Consulta y retorna inmediatamente la primera fila del resultado.
    3. $columna = $comando -> queryColumn().   Consulta y retorna inmediatamente la primera columna del resultado.
    4. $valor = $comando -> queryScalar().   Consulta y retorna inmediatamente el valor de la primera fila y primera columna del resultado.

Obtener los resultados.

  • Si se utilizó un método del estilo queryXXX() el valor obtenido como resultado es retornado inmediatamente.
  • Si se utilizó el método query(), los resultados pueden obtenerse de tres maneras.
    1. Mediante múltiples llamados a CDataReader::read().
      $dataReader = $comando -> query();
      while(($fila = $dataReader -> read()) !== false)
      {
          // ...
      }
    2. Mediante la construcción foreach la cual extrae una fila por cada iteración.
      foreach($dataReader as $fila)
      {
          // ...
      }
    3. Obtiene inmediatamente un arreglo con todas las filas del resultado.
      $filas = $dataReader -> readAll();

Utilizar transacciones.

  • Se basan en la manipulación de un objeto de clase CDbTransaction.
$transaccion = $conexion -> beginTransaction();           // Inicio de la transacción.
try
{
    $conexion -> createCommand($sql) -> execute();        // Ejecuta las sentencias SQL.
    // ...
    $transaccion -> commit();                             // Almacena los cambios realizados en los datos.
}
catch(Exception $e)                                       // Captura cualquier excepción sucedida.
{
    $transaccion -> rollBack();                           // En error se cancela la transacción y deshacen sus cambios.
}

Parametrizar consultas.

  • Son útiles para evitar la inyección de SQL y para aumentar el rendimiento en la ejecución de consultas recurrentes.
  • Se basa en la ubicación de marcas en el código SQL que después son reemplazadas por valores dinámicamente.
  • Las marcas pueden tener un nombre (etiquetas únicas como :nombre y :contrasena) o ser anónimas (representadas por ?).
  • Para reemplazar las marcas por los valores finales pueden utilizarse los siguientes métodos.
    1. CDbCommand::bindParam().  Relaciona la marca con una referencia a la variable PHP.  Para valores grandes esta opción es preferible en términos de desempeño.
    2. CDbCommand::bindValue().  Relaciona la marca con el valor de una varible PHP.
  • El reemplazo de las marcas debe hacerse antes de que la consulta sea ejecutada.
$sql = "INSERT INTO usuario(nombre, contrasena) VALUES(:username, :password)";
$comando = $conexion -> createCommand($sql);
// Agregar el primer usuario.
$comando -> bindParam(":username", $nombre1, PDO::PARAM_STR);
$comando -> bindParam(":password", $contrasena1, PDO::PARAM_STR);
$comando -> execute();
// Agregar el segundo usuario.
$comando -> bindParam(":username", $nombre2, PDO::PARAM_STR);
$comando -> bindParam(":password", $contrasena2, PDO::PARAM_STR);
$comando -> execute();
// ...

Las constantes que determinan el tipo del valor a reemplazarse por la marca pueden consultarse en la sección de Constantes Predefinidas de PDO en el manual de PHP.

Asociar columnas.

  • Yii provee una facilidad adicional para la manipulación de resultados provenientes de la base de datos.
  • Consiste en relacionar variables con las columnas del resultado así se actualizarán de manera automática en cada iteración que obtenga una nueva fila de datos.
$sql = "SELECT nombre, contrasena FROM usuario";
$dataReader = $conexion -> createCommand($sql) -> query();
// Asociar las variables $username y $password a las columnas uno y dos del resultado.
$dataReader -> bindColumn(1, $username);
$dataReader -> bindColumn(2, $password);
while($dataReader -> read() !== false)
{
    // $username y $password tendrán el valor de cada fila
    // del resultado en cada una de las iteraciones.
}

Enlaces.

Modelos de formularios con Yii (formulario y acción)

Introducción.

Le etapa final de la implementación de la aplicación basada en formularios web corresponde con la implementación del formulario (vista) y de la acción del controlador que la va a procesar.

Definición de las etiquetas del formulario.

  • Permite definir desde el modelo las etiquetas que acompañarán a cada uno de los atributos del mismo.
  • Es preferible este método en lugar de escribirlos manualmente en la vista ya que permite modificarlos según sucedan ciertas situaciones.  Por ejemplo, resaltarlos en rojo (mediante CSS) cuando la validación arroja un valor en un campo específico.
  • Para definirlas es necesario sobreescribir el método attributeLabels() del modelo.  Por defecto se retornan los mismos nombres de los atributos.
public function attributeLabels()
{
    return array(
        'value1'   => 'Operando #1',
        'value2'   => 'Operando #2',
        'operator' => 'Operación'
    );
}

Creación del formulario.

  • Yii provee algunos ayudantes para la creación y estandarización de código HTML mediante la clase CHtml como CHtml::textField, CHtml::dropDownList, CHtml::beginForm y CHtml::endForm.
<div class='formulario'>
    <?php echo CHtml::beginForm(); ?>
    <?php echo CHtml::errorSummary($calc); /* $calc es una referencia al modelo */ ?>
    <div class='operacion'>
        <div class='operando'>
            <?php echo CHtml::activeLabel($calc, 'value1'); ?>
            <?php echo CHtml::activeTextField($calc, 'value1'); ?>
        </div>
        <div class='operador'>
            <?php echo CHtml::activeLabel($calc, 'operator'); ?>
            <?php echo CHtml::activeTextField($calc, 'operator'); ?>
        </div>
        <div class='operando'>
            <?php echo CHtml::activeLabel($calc, 'value2'); ?>
            <?php echo CHtml::activeTextField($calc, 'value2'); ?>
        </div>
</div>
<div class='acciones'>
    <?php echo CHtml::submitButton('Ejecutar'); ?>
</div>
<?php echo CHtml::endForm(); ?>
</div>

Creación de la acción.

  • Contiene la lógica del controlador que manipula los modelos de acuerdo al requerimiento recibido por parte del usuario.
public function actionCalculate()
{
    $calc = new CalculatorForm();
    if(isset($_POST['CalculatorForm']))                         /* If the form was submitted */
    {
        $calc -> attributes = $_POST['CalculatorForm'];         /* Populate the form with POST data */
        if($calc -> validate())                                 /* Validate the input data */
            // ...                                              /* On success do something */
        $this -> render('result', array('calc' => $calc));      /* Render a view */
    }
}

Enlaces.

Actualización de portales web basados en Drupal a la versión 6.13

Introducción.

Después de actualizada la versión 5 del portal a la 6.12 o la instalación de una versión 6.x fresca, se hace necesario actualizar el portal a la nueva versión disponible, la 6.13.

Procedimiento.

  1. Realizar la copia de seguridad de la base de datos del portal.
  2. Ingresar al portal con el usuario cuyo id = 1.
  3. Modificar  la configuración del sitio poniéndolo en modo administración: ?q=admin/settings/site-maintenance.
  4. Actualizar los módulos disponibles según el módulo de update-status: ?q=admin/reports/updates.
  5. Desactivar los módulos (?q=admin/build/modules) y temas (?q=admin/build/themes) de terceros.
  6. Realizar la actualización de los archivos.
    1. $ wget http://ftp.drupal.org/files/projects/drupal-6.13.tar.gz
    2. $ rm site.old
    3. $ mv site site.old
    4. $ tar zxvf drupal-6.13.tar.gz
    5. $ mv drupal-6.13/ site
    6. $ cp -rf site.old/files site    (si no se utiliza la convención bajo sites/default).
    7. $ cp -rf site.old/sites site
    8. (web) $URL/apps/site/update.php
    9. (web) $URL/?q=admin/reports/updates
    10. rm site/install.php site/CHANGELOG.txt site/INSTALL.txt site/INSTALL.mysql.txt site/INSTALL.pgsql.txt site/LICENSE.txt site/MAINTAINERS.txt site/UPGRADE.txt
    11. $ rm drupal-6.13.tar.gz
  7. Activar los módulos (?q=admin/build/modules) y temas (?q=admin/build/themes) de terceros.
  8. Modificar  la configuración del sitio poniéndolo en línea nuevamente: ?q=admin/settings/site-maintenance.

Con esta actualización se introducen las siguientes modificaciones al esquema de actualizaciones de Drupal que se había estado siguiendo hasta la fecha.

  • Los archivos del usuario, diferentes a los de la distribución de Drupal, y a los cuales se les debe realizar copia de seguridad se encuentran en las siguientes ubicaciones.
    • sites/all/libraries.  Librerías que aplican a todos los sitios.
    • sites/all/modules.  Módulos de terceros que aplican a todos los sitios.
    • sites/all/themes.  Temas de terceros que aplican a todos los sitios.
    • sites/default/files.  Archivos de usuario de un sitio específico (default).
    • sites/default/settings.php.  Configuración de un sitio específico (default).
  • Si no se utiliza la convención bajo sites los directorios libraries, modules, themes y files se ubicarán en el directorio raíz de la distribución de Drupal, esto debe tenerse muy en cuenta ya que deberán agregarse como pasos durante la copia de seguridad y deberá tenerse extremo cuidado para evitar conflictos con los archivos del núcleo de la distribución que utilizan la mayoría de estos directorios.

Enlaces.

Funciones generales para el manejo de temas en Drupal 6

Variables disponibles por defecto para los temas.

  • $db_is_activeTrue si la base de datos se encuentra activa.
  • $directory. Ruta del tema la cual es relativa al directorio de Drupal.
  • $id.  Identificador del tema.
  • $is_adminTrue si el usuario activo es un administrador del sitio.
  • $is_frontTrue si la página actual es la página frontal.
  • $logged_inTrue si el usuario activo se encuentra autenticado.
  • $user.  Contiene la información del usuario activo.
  • $zebra.  Es un diferenciador par/impar para facilitar la presentación de algunos elementos de la interfaz.

Funciones mas comunes del sistema de temas.

  • Sus nombres empiezan siempre por el prefijo theme_.
  • Se ubican en includes/theme.inc e includes/theme.maintenance.inc.

Estas son las funciones de uso mas común para todos los módulos.

  • theme_box (includes/theme.inc).  Crea un contenedor (box) basado en el tema.
  • theme_breadcrumb (includes/theme.inc).  Maneja el rastro de la ubicación (breadcrumb).
  • theme_closure (includes/theme.inc).  Define el formato de hook_footer() al final de la página.
  • theme_feed_icon (includes/theme.inc).  Activa el ícono del alimentador RSS.
  • theme_image (includes/theme.inc).  Aplica el tema a una imagen.
  • theme_indentation (includes/theme.inc).  Provee un DIV para la estandarización de la identación.
  • theme_item_list (includes/theme.inc).  Aplica el tema a una lista de elementos.
  • theme_links (includes/theme.inc).  Aplica el tema a una lista de enlaces (como los enlaces primarios y secundarios).
  • theme_mark (includes/theme.inc).  Aplica el tema a un marcador para el contenido.
  • theme_more_help_link (includes/theme.inc).  Genera el enlace para "mas ayuda".
  • theme_more_link (includes/theme.inc).  Genera el enlace de "mas" que aparece en los bloques.
  • theme_placeholder (includes/theme.inc).  Modifica el formato del texto que se despliega en las marcas (placeholders).
  • theme_progress_bar (includes/theme.inc).  Muestra el porcentaje del estado de una barra de progreso.
  • theme_status_messages (includes/theme.inc).  Modifica el formato de los mensajes de estado y de error.
  • theme_table (includes/theme.inc).  Aplica el tema a una tabla.
  • theme_table_select_header_cell (includes/theme.inc).  Controla la celda de la cabecera de las tablas que tienen la funcionalidad de seleccionar-todo.
  • theme_tablesort_indicator (includes/theme.inc).  Genera el ícono de ordenar.
  • theme_task_list (includes/theme.maintenance.inc).  Aplica el tema a la lista de tareas de mantenimiento.
  • theme_username (includes/theme.inc).  Aplica el tema al nombre de usuario.
  • theme_xml_icon (includes/theme.inc).  Genera el ícono de XML.

Enlaces.