Los botones de Home y Search no funcionan con CyanogenMod en Android

El problema

Después de actualizar el teléfono con la versión nocturna del 26 de agosto este se empezó a comportar de manera extraña: no aparecía el bloqueo del teclado y no funcionaban los botones físicos del teléfono de Home (casita) ni Search (lupa).  Esto aparentemente puede suceder con diferentes versiones de CyanogenMod con cualquier teléfono, esta vez fue mi caso.

La solución

Probé varias opciones desde limpiar los distintos cachés hasta restaurar la configuración de ADW y reinstalar el ROM sin resultados.

La solución llegó con simplemente agregar una opción a la base de datos de configuración de Android de la siguiente manera a través de la terminal (Terminal Emulator).

$ su -

# sqlite3 /data/data/com.android.providers.settings/databases/settings.db "INSERT INTO secure (name,value) VALUES ('device_provisioned','1');"

A continuación se deberá reiniciar el teléfono y listo!

Campos autonuméricos con SQLite3

Introducción

SQLite es un gestor de bases de datos pequeño y simple que se usa ampliamente como apoyo al software (aplicaciones de escritorio, navegadores web, etc.) y en sistemas embebidos como los teléfonos móviles (Android por ejemplo).

A continuación se muestra como crear campos autonuméricos en esta base de datos, es decir, campos cuyo valor es un número serial que es asignado automáticamente por el software.

Procedimiento

Crear la tabla con el campo autonumérico (code en este caso).

CREATE TABLE test (

code     INTEGER        PRIMARY KEY    AUTOINCREMENT,
name     VARCHAR(20)    UNIQUE,
value    FLOAT

);

Agregar datos a la tabla recién creada.

INSERT INTO test VALUES(NULL, 'demo1', 10.10);
INSERT INTO test VALUES(NULL, 'demo2', 20.20);
INSERT INTO test VALUES(NULL, 'demo3', 30.30);

Consultar los datos recién ingresados.

sqlite> select * from test;

    1|demo1|10.1
    2|demo2|20.2
    3|demo3|30.3

Consultar el valor del último campo autonumérico ingresado durante la sesión.

SELECT last_insert_rowid();

    3

Enlaces

Problemas cargando sqlite.so en 20090626+lfs/sqlite.so

Introducción

Instalando Apache+PHP+MySQL en mi equipo con GNU/Linux Mint 12 encuentro el siguiente problema después de instalar el soporte para SQLite (php5-sqlite).

PHP Warning:  PHP Startup: Unable to load dynamic library ‘/usr/lib/php5/20090626+lfs/sqlite.so’ – /usr/lib/php5/20090626+lfs/sqlite.so: cannot open shared object file: No such file or directory in Unknown on line 0

La situación

Aparentemente el uso de SQLite versión 2 ha sido desestimado en pos del uso exclusivo de la versión 3, sin embargo extrañamente la configuración por defecto de PHP sigue intentando cargar su librería.

La solución

Remover la configuración de SQLite2 de PHP y utilizar la versión 3 únicamente.

$ sudo mv /etc/php5/conf.d/sqlite.ini /etc/php5/conf.d/sqlite.ini.old

Una vez evitado que este archivo de configuración sea tenido en cuenta el funcionamiento de PHP vuelve a la normalidad.

$ php -v

PHP 5.3.6-13ubuntu3.3 with Suhosin-Patch (cli) (built: Dec 13 2011 18:37:10)
Copyright (c) 1997-2011 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies
    with Suhosin v0.9.32.1, Copyright (c) 2007-2010, by SektionEins GmbH

Enlaces

Give Me a Tweet, versión 1.0

Introducción.

Preparé el prototipo de esta aplicación web muy simple para experimentar con algunas librerías que tenía por revisar, que a pesar de ser muy sencillas de utilizar es bueno ir conociendo para determinar mas adelante cual de todas las disponibles es la idónea.

Esta es de manera resumida la funcionalidad del prototipo.

  • Obtiene cierta cantidad de tweets de ciertos usuarios predefinidos.
  • Los tweets son alamcenados en caché por una cantidad específica de tiempo.
  • El acceso a la página no requiere de ningún tipo de autenticación por parte del usuario.
  • Cuando el usuario accede al sitio web, el sistema elige un tweet azar y lo muestra.
  • La elección del tweet se realiza sobre los almacenados en el caché.  Si no hay caché o este es demasiado viejo, entonces se renueva automáticamente.
  • Los mensajes que no se encuentran escritos en español son traducidos automáticamente a este idioma.
  • Se prepara un enlace corto a la información del tweet.
  • Se presenta un QRCode con el enlace corto al tweet para ser fácilmente consultado por dispositivos móviles.

Herramientas.

Estas fueron las herramientas utilizadas durante el desarrollo del prototipo.

  1. Netbeans (IDE).
  2. SQLite (persistencia del caché).
  3. Blueprint CSS Framework (framework para la presentación).
  4. PHP (lenguaje de programación).
  5. Yii PHP Framework (framework de desarrollo web).
  6. Extensión de CURL para Yii (acceder al servicio REST fácilmente).
  7. API REST de Twitter (obtener los mensajes).
  8. Google Translate Service (servicio de traducción de textos).
  9. jquery-qrcode para la generación de los códigos QR.
  10. jquery-urlshortener que utiliza el servicio de bit.ly (acortador de URLs).

Prototipo.

 

Prototipo de Give Me a Tweet
Prototipo de Give Me a Tweet

Instalación.

El código fuente del protitpo puede obtenerse desde la siguiente ubicación.

https://github.com/jimezam/Give-Me-a-Tweet/tree/v1.0

Para la ejecución de la aplicación web se requiere que se cuente además de la infraestructura web, con PHP con soporte para SQLite y CURL, y la distribución del Yii PHP Framework (1.1.7 o similar) en una ubicación conocida.

Finalmente se deberán modificar los siguientes archivos para ajustarlos a la infraestructura local.

index.php:

$yii=dirname(__FILE__).’/../../yii-1.1.7.r3135/framework/yii.php’;
$config=dirname(__FILE__).’/protected/config/main.php’;

Ajustar estas rutas a la ubicación real del framework.

protected/views/tweet/show.php:

$.shortenUrl.settings.login  = ‘USUARIO‘;
$.shortenUrl.settings.apiKey = ‘LLAVE DEL API‘;

Modificar estos valores para que correspondan con la información del propietario del servicio.  Esta información se puede obtener de manera gratuita en el sitio web de bit.ly para desarrolladores.

Enlaces.

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);
}

Presentación de Sismos recientes en Colombia

Sismos recientes en Colombia
Sismos recientes en Colombia

Sismos recientes en Colombia es una aplicación de agregación de contenido que permite visualizar de manera gráfica en la geografía Colombiana la ubicación de los sismos que han sucedido en el territorio nacional, tanto los [últimos diez] destacados por su intensidad como la actividad sísmica completa de la semana anterior.  Esta información se actualiza casi en tiempo real, con una diferencia de al rededor de 3o minutos después del suceso, gracias a los servicios de la Red Sismológica Nacional del Instituto Colombiano de Geología y Minería.

La base del proyecto fue desarrollado en una semana de descanso utilizando casi por completo Software Libre.  Se construyó utilizando PHP, HTML/CSS/Javascript/AJAX, jQuery, CodeIgniter, SwiftMailer, SQLiteEclipse PDT y Google Maps.  La aplicación aún se encuentra en progreso, alias beta, así que aún tengo pensando hacerle varias mejoras y complementos.

Actualmente el sitio web permite consultar la información de los sismos destacados y de los sismos diarios de la semana en curso, la información georreferenciada se despliega en el mapa gráficamente desde donde se puede acceder a la información disponible del sismo.  El último sismo destacado sucedido cuenta con un enlace a la RSNC donde se amplía su información.  Las fechas y horas hacen referencia al territorio colombiano, es decir, GMT-5.  Adicionalmente la aplicación cuenta con un sistema de caché que permite agilizar la presentación de información cuando esta se encuentra fresca en el sistema y de refrescarla en ciertos intervalos, disminuyendo la consulta a las fuentes de datos y el tiempo de generación del contenido.

He planeado realizar las siguientes mejoras a la aplicación sin ningún orden necesario.

  • Mejorar los colores de la presentación.  Los actuales son de prueba, estoy buscando a alguien que si sepa del tema para que me asesore.
  • Establecer tooltips informativos para facilitar la utilización del sitio.
  • Verificar la viabilidad de crear un mapa del sitio (sitemap) que facilite las búsquedas de contenido en el sitio.
  • Establecer un procedimiento cron que actualice la inforamción aún sin la consulta de visitantes.
  • Establecer la comunicación con otras aplicaciones como Twitter.
  • Establecer valores finales para la duración de los cachés de información.
  • Implementar un módulo de Contáctenos.
  • Crear una versión móvil que muestre la información resumida.
  • Implementar un indicador que le muestre al usuario en que sección del sistema se encuentra.
  • Verificar el cumplimiento [en lo posible] de XHTML.
  • Establecer la viabilidad de incluír información de sismos de otros lugares del mundo como valor agregado al sitio.

La aplicación, [con optimismo] al igual que las cosas buenas del mundo, se encuentra licenciado bajo CC NC-SA así que su código será liberado tan pronto como termine de implementar las principales mejoras y lo documente para dejarlo apto para el consumo humano.

Si alguien tiene alguna sugerencia acerca de como mejorar esta aplicación me gustaría mucho que lo compartiera conmigo.

Enlaces.

Instalación y primera prueba de Yii

Instalación del framework.

Después de instalados los paquetes de LAMP (Linux, Apache, MySQL y PHP) procedemos con la primera instalación de Yii.  En el equipo de desarrollo, el DocumentRoot se encuentra referenciando a /home/www/public, de esta manera en /home/www se almacenarán los recursos que están relacionados con las aplicaciones web pero que no deben ser publicados, es decir, serán privados.

Se descarga la última versión disponible del Yii Framework desde la siguiente ubicación.

http://www.yiiframework.com/download/

Para este caso fue el archivo yii-1.0.3.tar.gz el cual fue descargado en /home/www.

$ tar zxvf yii-1.0.3.r780.tar.gz

$ mkdir yii

$ mv yii-1.0.3.r780 yii/1.0.3

$ ln -s /home/www/yii/1.0.3/ /home/www/yii/current

La actualización del PATH como se muestra a continuación sólo modifica el ambiente del shell donde se ejecute; para realizar esta actualización de manera perdurable en el tiempo debe agregarse al final del archivo ~/.profile (para el usuario en sesión) o /etc/profile (para todos los usuarios del equipo).

$ PATH=/home/www/yii/current/framework:$PATH

Creación de la primera aplicación utilizando la herramienta yiic.

$ mkdir /home/www/public/demo1

$ yiic webapp /home/www/public/demo1

Acceder con un navegador web a la siguiente URL para verificar el esqueleto de aplicación generado.

http://localhost/demo1/

Deberá aparecer algo similar a lo mostrado en la siguiente imagen.

Aplicación base generada con 'yiic'
Aplicación base generada con ‘yiic’

La aplicación incluye tres secciones.

  1. Home: una página con texto estático (ver imagen).
  2. Contact: un formulario de contacto con validación de tipos y captcha.
  3. Login: un formulario de registro de usuario para el ingreso a la aplicación con su respectiva autenticación.

Creación y conexión a la base de datos y la tabla User.

$ mkdir -p /home/www/public/demo1/protected/data

$ cd /home/www/public/demo1/protected/data

$ vi script.sql

CREATE TABLE User (

id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
username VARCHAR(128) NOT NULL,
password VARCHAR(128) NOT NULL,
email VARCHAR(128) NOT NULL

);

$ sqlite3 source.db < script.sql

$ ls -l

-rw-r–r– 1 jimezam jimezam  168 2009-03-07 09:56 script.sql
-rw-r–r– 1 jimezam jimezam
3072 2009-03-07 09:57 source.db

$ vi /home/www/public/demo1/protected/config/main.php

return array(
‘basePath’=>dirname(__FILE__).DIRECTORY_SEPARATOR.’..’,
‘name’=>’Mi primera aplicación – Demo1’,
// …
‘db’=>array(
‘connectionString’=>’sqlite:protected/data/source.db’,
),
// …
);

Implementando las operaciones CRUD para User utilizando yiic.

$ cd /home/www/public/demo1

$ yiic shell

Yii Interactive Tool v1.0
Please type ‘help’ for help. Type ‘exit’ to quit.
>> model User
generate User.php

The ‘User’ class has been successfully created in the following file:
/home/www/public/demo1/protected/models/User.php

If you have a ‘db’ database connection, you can test it now with:
$model=User::model()->find();
print_r($model);

>> crud User
generate UserController.php
mkdir /home/www/public/demo1/protected/views/user
generate create.php
generate update.php
generate list.php
generate show.php
generate admin.php
generate _form.php

Crud ‘user’ has been successfully created. You may access it via:
http://hostname/path/to/index.php?r=user

>> exit

Inicie sesión a través del próximo URL y utilizando admin/admin como nombre de usuario y contraseña respectivamente.

http://localhost/demo1/index.php?r=site/login

Acceda al siguiente URL para utilizar el scaffolding de la tabla User recién creado.

http://localhost/demo1/index.php?r=user/admin

Después de agregar algunos datos deberá aparecer algo similar a lo mostrado en la siguiente imagen.

Scaffolding para la tabla User
Scaffolding para la tabla User

Problema con los assets.

Los assets son recursos del proyecto web que son publicados para ser utilizados directamente por los clientes. Para este proyecto se ubican en /home/www/public/demo1/assets y en esta ubicación se comparten las imágenes, las hojas de estilo y las librerías de Javascript.

Extrañamente encontré un problema.  Todas las opciones de la aplicación demo1 funcionaban correctamente exceptuando la eliminación (delete) de usuarios.  En el log de Apache aparecía el siguiente mensaje.

[Sat Mar 07 10:55:56 2009] [error] [client 127.0.0.1] (13)Permission denied: file permissions deny server access: /home/www/public/demo1/assets/d0586e3b/jquery.yii.js, referer: http://localhost/demo1/index.php?r=user/admin

Revisando la carga de los recursos de la página descubrí que tanto jquery.js como jquery-yii.js estaban fallando con un error 403.  Descubrí entonces que era un problema de permisos sobre estos archivos, muy probablemente motivado porque estoy utilizando suPHP (paquetes libapache2-mod-suphp y suphp-common en Ubuntu) que permite ejecutar los scripts bajo los permisos de sus propietarios.  Para resolver el problema fue necesario agregar el permiso de lectura para otros sobre los archivos relacionados con el siguiente comando.

$ chmod -R o+r /home/www/public/demo1/assets

Enlaces.