domingo, 28 de octubre de 2012

Yii Dialog Box (con jQueryUI y validacion)

Yii Framework y el DialogBox con jQueryUI usando validacion del lado cliente y del lado servidor.

Código en: https://bitbucket.org/christiansalazarh/ejemplodialogbox/src

Ver el ejemplo corriendo en nuestra sección de ejemplos de Yiiframeworkenespanol.org


Conoce jformer, un framework para hacer formularios con jQuery, http://www.jformer.com/


appdemo contiene una aplicación Yii lista, pero para que funcione hay que cambiar la ruta del framework en /appdemo/index.php y poner permisos 777 en appdemo/assets y appdemo/protected/runtime/ .

Este es uno de los primeros casos que me llevo a encontrar a Yii Framework, y que en un principio sugerí mejorar o ampliar, pero no tuve eco, es ciero estaba muy nuevo y recien aprendiendolo pero ahora me animo a crear una solución aunque sea "por encima" no oficial, pronto me gustaría desarrollar un widget que lo haga de una forma mas compacta.

1. Primero, así se verá. El dialogo en pantalla representa a un modelo llamado "models/Persona.php", se presenta en forma de dialogo jquery, el cual hara un POST via ajax, con validación en cliente y servidor.


2.  Si cometemos un error del lado cliente, entonces el componente jquery-validate.js hara lo suyo y nos informará, no permitirá el submit al action ajax hasta que el lado cliente no este como se pide. Para ello se proveen unas reglas de validación en el script.

3. Si cometemos una violación a una regla de modelo, del lado "ServerSide" entonces el modelo de yii framework nos lo advertirá.




Manos a la Obra

Todo el codigo necesario esta en este repositorio:  https://bitbucket.org/christiansalazarh/ejemplodialogbox/src

1. Necesitarás dos recursos javascript:  jquery-validate.js y json2.js (este ultimo es opcional para depurar),
los puedes descargar de:
jquery-validate.js json2.js

Es importante recalcar que los demas recursos jQuery los proveerá Yii, cuando en la "viewPersona.php" (mas abajo) se solicitan los recursos de script. Por esa razón solo publico aqui los scripts que Yii no proveerá.

2. Necesitas un codigo javascript que realiza al dialogo, el cual está en el vinculo a continuación, este codigo es específico para un dialogo de pruebas, solo debes ajustar los campos ahi indicados.
dialogo1.js

3. Necesitas un action en algun controller. A continuación un extracto de codigo, aquel que va en algun controller, en mi ejemplo, en siteController.php
actionPersona.php

4. Necesitas una vista donde alojar el dialogo y su enlace disparador.
viewPersona.php

Principalmente se registran los scripts jquery en la vistaPersona, que será la que se presenta al usuario. Esto se hace aqui:
https://bitbucket.org/christiansalazarh/ejemplodialogbox/src/8608caa29384979982c4710d8a7c865b620000f4/viewPersona.php?at=master#cl-3

Luego en viewPersona.php se crea el layout html que mostrará el cuerpo del dialogo, este dialogo es invisible, por eso el estilo "style='display: none;'".
https://bitbucket.org/christiansalazarh/ejemplodialogbox/src/8608caa29384979982c4710d8a7c865b620000f4/viewPersona.php?at=master#cl-35

En esta misma vista un disparador cualquiera sera el encargado de lanzar el dialogo.
https://bitbucket.org/christiansalazarh/ejemplodialogbox/src/8608caa29384979982c4710d8a7c865b620000f4/viewPersona.php?at=master#cl-18

Para ello el TAG html "id='lanzador'" es pasado al objeto de clase Dialogo1 (es un objeto jquery) el cual va a agregar un metodo "onclick" al tag "lanzador", cuando el usuario haga click ocurrirá esto:
https://bitbucket.org/christiansalazarh/ejemplodialogbox/src/8608caa29384/dialogo1.js?at=master#cl-28

El "action" el cual fue pasado al objeto jQuery en la vistaPersona.php, sera invocado solo cuando la validacion cliente sea exitosa, se lanza aqui:
https://bitbucket.org/christiansalazarh/ejemplodialogbox/src/8608caa29384/dialogo1.js?at=master#cl-70

EL ACTION

En siteController.php, colocamos un action (con el código que esta en actionPersona.php), ese action aparte de presentar la vista principal tiene una segunda funcion, la de procesar el POST enviado via ajax por dialogo1.js.

Cuando este action recibe el POST, lo hace recibiendo algo como: "cedula=123&nombre=aasas&apellido=aaa"Por tanto nos toca convertir esto a un array indexado, segun la documentación de CModel::setAttribute indica que es requerido, para poder finalmente pasar el post a $model->attributes como se haría en cualquier action de yii.  Esta conversión de string a array indexado se hace aqui:
https://bitbucket.org/christiansalazarh/ejemplodialogbox/src/8608caa29384/actionPersona.php?at=master#cl-12

Finalmente, el action procesa su modelo como ya bien conoces en Yii, usando la funcion validate:
https://bitbucket.org/christiansalazarh/ejemplodialogbox/src/8608caa29384/actionPersona.php?at=master#cl-21












martes, 23 de octubre de 2012

Fechas en Yii Framework usando CJuiDatePicker y CDateTimeParser.

CJuiDatePicker es un componente de Yii que viene de paquete y sirve para elegir fechas. Este componente usa a jQueryUI.datepicker.

1. Supongamos que tienes una tabla cuyo campo fecha debe ser establecido, leido, listado etc.  Ok, primero, por optimizacion no vamos a usar un campo VARCHAR(11) para almacenar una fecha porque no es práctico por muchas razones. Usaremos en campo BIGINT para almacenarla, lo cual es mas eficiente y útil.

2. La tabla en mysql sería como esta:
CREATE TABLE IF NOT EXISTS `prueba` (
  `idprueba` int(11) NOT NULL AUTO_INCREMENT,
  `fecha` bigint(20) NOT NULL,
  PRIMARY KEY (`idprueba`)
);

Fechas en Bigint versus CHAR   Si la fecha esta almacenada como un CHAR quiza para ti sea mas fácil...pero a la hora de usar vistas, comparaciones de fechas y ordenamientos será una pesadilla. Aparte del espacio que gasta.   Por otra parte una fecha en bigInt es una ventaja, porque permite llevar esa fecha de una forma a otra, la fecha en bigint queda almacenada como un numero secuencial, de ese modo la fecha de ayer sera menor que la de hoy por obligación matemática.

3. Ahora, con GII creas un Modelo llamado Prueba, que representa a la tabla "prueba".

4. De nuevo con GII, creas un CRUD para el modelo Prueba.
Hasta aqui el objetivo es que tengas una base de para hacer las pruebas a continuación.

5. Para recibir la fecha en el formulario del CRUD creado por GII, en este caso:
views.prueba._form.php,  quitaras el campo FECHA que GII te puso por defecto y en cambio insertarás el widget:


  1.  <?php
  2.   // widget
  3.   $this->widget('zii.widgets.jui.CJuiDatePicker', array(
  4.    'language'=>'es',
  5.    'model'=>$model,
  6.    'attribute'=>'fechaString',
  7.    'flat'=>false,
  8.    'options'=>array(
  9.     'firstDay'=>1,
  10.     'showOn'=>"both",
  11.     //'buttonImage'=>"images/calendar.gif",
  12.     'buttonImageOnly'=> true,
  13.     //'minDate'=>-31,
  14.     //'maxDate'=>0,
  15.     'constrainInput'=>true,
  16.     'currentText'=>'Now',
  17.     'dateFormat'=>'dd/mm/yy',
  18.    ),
  19.    'htmlOptions'=>array(
  20.    ),
  21.   ));
  22.  ?>


6. Nota que he puesto "model"=>$model y "attribute"=>"fechaString" y no "fecha" como estarías esperando, ya que el modelo "models/Prueba.php" tiene solo el campo fecha, el cual representa a un BIGINT, entonces, por qué "fechaString" ? bueno porque una cosa es el bigint que la base de datos va a almacenar, y otra el string que vamos a recibir de CJuiDatePicker, el cual maneja strings y no enteros.

7. En models/Prueba.php vas a crear un atributo llamado "fechaString", y haras algunos cambios en las reglas:


  1.  <?php
  2.  class Prueba extends CActiveRecord
  3.  {
  4.   public $fechaString;
  5.   public function rules()
  6.   {
  7.    return array(
  8.     array('fechaString','convertir_fecha'),  // <---AQUI
  9.     array('fecha, fechaString', 'required'),  // <--- AQUI
  10.     array('fechaString', 'length', 'max'=>20),  // <--- Y AQUI
  11.     array('idprueba, fecha', 'safe', 'on'=>'search'),
  12.    );
  13.   }
  14.  
  15.    public function convertir_fecha($attr, $params){
  16.    $this->fecha = CDateTimeParser::parse($this->fechaString,'dd/MM/yyyy');
  17.    if($this->fecha == null)
  18.     $this->addError('fecha','La fecha es requerida.');
  19.   }
  20.  }


Qué acabamos de hacer aquí:
  • Primero, creamos un atributo que no pertenecerá a la base de datos, llamado fechaString, el cual solo servirá para traernos del formulario (usando CJuiDatePicker) a un string con la fecha que le pedimos al usuario.
  • Luego, en rules() hemos indicado que la fechaString es requerido, y que además dispone de un convertidor, para que tras submit podamos pasar lo que hay en "fechaString" a "fecha", claro está validando que no se introdujo basura.  Para esto último en el metodo "convertir_fecha" se hace una conversión usando a CDateTimeParser, quien recibirá el texto de fechaString y lo convertirá.
  • CDateTimeParser necesita saber como es el formato de aquello que le estamos pasando, en la documentacion de CDateTimeParser hallarás los detalles, pero aqui basta con indicar que esperamos un string asi:  "dd/MM/yyyy".  

Si en CDateTimeParse esperamos un string con ese formato específico es lógico esperar que CJuiDatePicker entregue un string con ese formato, asi que por eso ves que mas atras en la configuración del widget de CJuiDatePicker ponemos:  'dateFormat'=>'dd/mm/yy'.  Nota que los formatos son distintos en sus letras, esto es algo que hay que cuidar. Lee bien la doc de cada componente.  Finalmente, si el widget de CJuiDatePicker pide ese formato tambien hay que considerar cual es el formato por defecto del componente 'format' (dateFormat) en config/main.php.  También debe coincidir. Se configura asi:
'format' => array(
'datetimeFormat'=>"d M, Y h:m:s a",
'dateFormat'=>'d/m/Y',   // <-----ESTE ES
),
EL FORMATO DE LA FECHA ES LA CLAVE, POR TANTO VERIFICA QUE:
  * CJuiDatePicker va a emitir un string en un formato indicado en el widget.
  * Prueba.php va a convertir ese string a un bigint usando CDateTimeParser, que tiene su propio formato.
  * Yii::app()->format->formatDate tiene su propio formato.

8. Si abres el form views/prueba/create.php, verás que éste presentara una cajita de texto para el campo "fechaString", cuando le haces clic aparece el CJuiDatePicker.  Cuando haces submity si todo esta bien se invocará a models/Prueba.php::convertir_fecha , esto debido a los rules(), quien pasará lo que hay en fechaString al atributo fecha, que será el que finalmente se guardará en la base de datos.

9. Si abres el form views/pruebas/admin.php verás que la fecha se muestra en el formato NUMERICO, así 1298928922, esto es porque la vista admin.php por defecto presenta al atributo fecha asi mismo como viene sin preprocesarlo.  Para que el CGridView de admin.php muestre la fecha en el formato correcto, se hace lo siguiente:


  • <?php $this->widget('zii.widgets.grid.CGridView', array(
  •  'id'=>'prueba-grid',
  •  'dataProvider'=>$model->search(),
  •  'filter'=>$model,
  •  'columns'=>array(
  •   'idprueba',
  •   array('name'=>'fecha','type'=>'date'),
  •   array(
  •    'class'=>'CButtonColumn',
  •   ),
  •  ),
  • )); ?>


  •   Si ves, puse 'type'=>'date'. Eso hace que CGridView invoque a: Yii::app()->format-  >formatDate(1298981222), lo cual formateará el valor ingresado (la fecha en bigint de la tabla "prueba") a una representación indicada por la configuracion hecha en config/main bajo:
    'format' => array(
    'datetimeFormat'=>"d M, Y h:m:s a",
    'dateFormat'=>'d/m/Y',   // <-----ESTE ES
    ),

    10. FINALMENTE, para actualizar el valor de un registro de la tabla prueba hay algo extra que hacer: Convertir la fecha desde un bigint a un string antes de mostarla en el formulario. Esto se hace en el controller, asi:


    1.  public function actionUpdate($id)
    2.  {
    3.   $model=$this->loadModel($id);
    4.  
    5. // AQUI...pasar a fechaString lo que tiene el bigint fecha usando:
    6.   $model->fechaString = Yii::app()->format->formatDate($model->fecha);
    7.  
    8.    // Uncomment the following line if AJAX validation is needed
    9.   // $this->performAjaxValidation($model);
    10.  
    11.    if(isset($_POST['Prueba']))
    12.   {
    13.    $model->attributes=$_POST['Prueba'];
    14.    if($model->save())
    15.     $this->redirect(array('view','id'=>$model->idprueba));
    16.   }
    17.  
    18.    $this->render('update',array(
    19.    'model'=>$model,
    20.   ));
    21.  }


    lunes, 22 de octubre de 2012

    IA - El dilema de la consciencia

    Un autor cuyo nombre no recuerdo pero que si bien  dedicó una copia de su libro a mi papá, resumía que:


    "Aquello que es estático, que no razona y que solo se rige de reglas fijas, cómo puede derivar 
    en razonamiento ?". 

    Se refería al hecho de que máquinas estáticas, físicas cómo nuestro cerebro y nuestras máquinas puedan razonar. Es una incógnita de aquello filosófico del estudio del razonamiento basado en máquinas (biológicas o mecánicas). Pues bien, yo lo pongo en la misma línea de razonamiento cuando nos enfrentamos a una simpe reglita escolar (de aquellas que llevamos al colegio para trazar líneas rectas):

    Si ponemos una gran lupa en la rayita de "1cm", casi llegando a 2cm, cómo sabemos cuando realmente pasamos a "2cm"...y no simplemente es 1.9999..9 ? Nunca lo sabemos, nunca pasamos a 2...siempre es 1.9. Pero, obviamente en algún momento llegamos a 2cm...sino para que una reglita tan larga ? Al igual que ese simple ejemplo de la regla, puedo decir que una maquina física, tosca y bruta que solo sabe de estados
    fijos en algún momento al igual que la reglita ya no es tosca y bruta y pasa a razonar. Si esto no fuese cierto, usted y yo no estaríamos compartiendo un texto, no existiríamos.

    Si usted y yo tuvieramos en nuestro cerebro un aparato que capturara y digitalizara las ondas de radio y de ellas hiciera una punzación mecanica en el oido izquierdo, muy probablemente en 5 años usted y yo podríamos comunicarnos por ese mecanismo, eso quiere decir que la capacidad de adaptación a eventos externos que nuestra maquina dispone (el cerebro) es la responsable de que podamos interactuar como lo hacemos con los demas sentidos.

    Y si hicieramos una maquina bruta que solo se dedicara a recibir eventos externos sin tener la menor capacidad de comprenderlo, podría esta en 10 millones de años llegar a razonar ? Obviamente si se puede, nosotros lo hicimos, pero podriamos nosotros ? Obviamente si se podrá.

    Java vs PHP - La mentalidad de aquel.

    Java y PHP, ambos son buenos, ambos son malos, depende de para qué lo vas a usar.  Primero SACATE DE LA CABEZA LA RETROGRADA IDEA de "el mejor lenguaje".  Arranquemos por aqui.

    En lo personal, tengo años de experiencia en C, C++, Java, PHP y otros mas que no vienen al caso. Porqué tantos lenguajes...para impresionar ? NO.  Para cubrir necesidades y lograr objetivos ? SI.

    Hay gente con problemas personales muy severos que los hacen creese seres superiores hijos de la nasa solo porque aprendieron a programar en un lenguaje, quizá en dos...ahí dándole...Conozco mucha gente que te presenta el gran software que han hecho en java como si eso les garantizara su perfección.

    Si ellos supieran que dirían todos mis colegas que como yo se ganan su dinero haciendo software serio día a día en proyectos que incluyen piezas de software hechas a veces en casi todos los lenguajes conocidos.  Será que somos idiotas ? Yo en realidad creo que si, porque por ahí hay gente que se dice que los idiotas no programan en java porque para ellos es muy difícil.

    El mal software:

    * Solo él y dios comprenden el código hecho ahí. (tu no entiendes su trabajo porque es para genios de la nasa, y obviamente tu no debes ser uno de ellos).

    * No tiene la menor planificación de software, y la arquitectura..pues bien, según su creador es obvia porque él es un genio y su software esta hecho para gente inteligente, no para brutos. Según él. (Debe ser verdad, digo yo).

    * Su cliente (el pobre ser inferior que lo contrató para hacerle el sistema) debe  amoldarse a las genialidades del creador. (No vamos a molestar al genio para que nos ayude a automatizar un proceso de negocio...debemos amoldarnos a lo que él nos hizo, sino se va..y que será de nosotros sin él.)

    A veces Java, A veces PHP, a Veces C++... y a veces un pizarrón...

    Todo depende de cual es el que mejor beneficia a la solución que el cliente necesita. Ni mas allá, ni mas acá.

    Ejemplo complejo:

    Supon que te tocase hacer un reporte de ventas de todas las sucursales de una empresa internacional.

    Que harías en PHP ? conectarte a cada una ? pasando por internet asi a rin pelao..? conectandose y desconectandose...yendo de aqui para allá ejecutando scripts por todas partes ? y si un server falla ? y si las sucursales tienen distintos modelos de datos en cada una ? que haras ? si se puede en PHP...pero el camino es terrible...por tanto NO ES LA SOLUCION, y ademñas por razones de que hay muchos desarrollos involucrados en cada sucursal no será factible, será un código complicado y seguramente inestable e inseguro..no por PHP sino por TI. He aqui la diferencia.

    Has oido acerca de Glassfish ?  Pues bien, con esta herramienta resuelves un problema de software complejo, en Java principalmente, para que procesos de negocio compartan distintas fuentes de datos de forma unificada.  En este caso de ejemplo de sucursales que tienen muchas bases de datos, y peor aun, distribuidas en servidores distintos de distintos paises con distintas interfaces y configuraciones JAVA es la solución, porque su madurez como lenguaje, la arquitectura de sus paquetes y la fuerza que provee permite solucionar.  Quiza en PHP también lo hagas, y muy bien, pero lo liviano como PHP trata a los errores de tipeado te jugará una muy mala pasada por tanto no será la mejor solución.

    Y C++, que harás en C++ en ese cliente con sucursales de ejemplo ?  Quiza drivers, quiza servicios, quiza recolectores de datos en bajo nivel, o procesos de actualización o lo que sea que necesite ejecutarse para una máquina específica, digamos una maquina corriendo en Linux y otras en Windows. No vas a hacer la interfaz de usuario web en C++ aunque podrías, porque gastarás demasiado tiempo reinventando la rueda. Por tanto, C++ serviría para un propósito bien definido.

    Y el pizarrón ?

    Aunque no creas, a veces un proceso de usuario se vuelve mas facil de manejar en una pizarra con cintas y cuadrículas que luego se llevan a un equipo de cómputo.  Sino pregúntale a los que fabrican software para barcos de guerra...ellos tienen mucho software que automatiza casi todo, pero hay cosas que solo los humanos saben hacer bien mediante un dibujo.

    CADA LENGUAJE ES UNA HERRAMIENTA PARA RESOLVER UNA DETERMINADA CONDICIÓN DE UN PROCESO DE NEGOCIO DEL CLIENTE.

    (no existe el mejor lenguaje, ni el lenguaje de los "bravos", ni el de los "idiotas". Solo existen "idiotas" que creen lo contrario y lo gritan a toda voz solo porque ese día aprendieron a programar en un lenguaje)





    viernes, 19 de octubre de 2012

    File Uploads en Yii Framework (con "drag and drop")



    A continuación presentaré tres alternativas para subir archivos a tu aplicación web: Basica,  Coco y YiiFileManagerFilePicker , ambas tienen sus propios beneficios y sus diferencias. 

    Adicionalmente me gustaría presentar el artículo en donde muestro cómo recibir archivos por una vía remota (desde otra aplicación), está relacionado con la subida de archivos porque las personas siempre piensan que la única vía de subir archivos es mediante un widget y la realidad muestra que tu aplicación pudiera proveer un mecanismo para recibir archivos por un canal http.


    La manera estandar de subir archivos en Yii Framework

    Necesitas leer acerca de estas dos clase: CUploadedFile  y CFileValidator,  la primera es para manejar el archivo subido, y la segunda es para validar el archivo que esta siendo subido.  CUploadedFile se encarga de lidiar con el estandar $_FILES de php.

    Paso 1, El modelo para subir un archivo:


    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    <?php
    // protected/models/ImportEmployeesForm.php
    class ImportEmployeesForm extends CFormModel {
        public $the_file;
        public function rules()
        {
            return array(
                array('the_file', 'file', 'allowEmpty'=>false,
                    'types'=>'csv',
                    'maxSize'=>array(1024 * 2000),
                    'message'=>'Solo se admiten archivos de texto con extensión CSV'),
            );
        }
        public function attributeLabels(){
            return array(
                "the_file"=>"Archivo a Importar",
            );
        }
    }
    



    Paso 2, La vista que aloja al formulario:




    <?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm', array(
        'id'=>'importemployees-form',
        'enableAjaxValidation'=>false,
        'type'=>'horizontal',
        'htmlOptions'=>array("enctype"=>"multipart/form-data"),
    )); ?>
        <div class='file-uploader'>
            <?php echo $form->fileFieldRow($model,'the_file'); ?>
        </div>
        <div class='buttons'>
            <?php
            $this->widget('bootstrap.widgets.TbButton', array(
                'buttonType'=>'submit',
                'label'=>'Subir Archivo',
                'type'=>'primary', // null, 'primary', 'info', 'success', 'warning', 'danger' or 'inverse'
                'size'=>'large', // null, 'large', 'small' or 'mini'
                'htmlOptions'=>array(
                ),
            ));
            ?>
        </div>
    <?php $this->endWidget(); ?>

    Paso 3, La controladora que usa al modelo y la vista para descargar y procesar el archivo subido.

    public function actionImportEmployees(){
       $model = new ImportEmployeesForm();
       if(isset($_POST["ImportEmployeesForm"])){
           $model->attributes = $_POST["ImportEmployeesForm"];
           $model->the_file = CUploadedFile::getInstance($model, "the_file");
           if($model->validate()){ // aqui entra CFileValidator
              $filename = $model->the_file->tempName;
              //hacer algo con el archivo recien subido...
           }
        }
        $this->render("importemployees",array('model'=>$model));
    }

    A continuación otras opciones mas avanzadas:

    Coco
    Es un widget para implementar en tu formulario que mostrará un multi-file-uploader ajax-based, para recibir los archivos subidos deberás indicarle al widget la ubicación de un método en alguna clase el cual recibirá los archivos. En el caso particular de coco la funcionalidad es heredada de un componente preexistente fabricado por valums/fileuploader.



    el widget coco ofrece el boton "find & upload", la lista de archivos bajo el botón aparece cuando se han ido subiendo archivos. permite drag&drop.



    YiiFileManagerFilePicker
    Es mas avanzado que Coco, también es un multi-file-uploader, también ofrece un widget al igual que Coco, pero agrega mas funcionalidad y extensibilidad, por ejemplo:

    1. El usuario tiene un explorador de sus archivos subidos con capacidad de renombrar archivos, eliminarlos, seleccionar varios.


    2. La presentación del widget depende de tu diseño, tu fabricas el layout del widget incrustándole las piezas html que el widget pide pero con el estilo y ubicación que tu necesitas.

    3. El control del widget se hace desde una sub clase que alojas en tus componentes, donde harás los cambios de código necesarios sin alterar al core del widget.

    4. Gracias al subclassing (lee el punto 3) puedes oir eventos, controlar mejor qué ha subido el usuario, qué quiere subir, tipos de archivos admitidos, tamaños, mimetypes y muy importante: control basado en el estado del usuario.

    5. Permite aplicar visores para los distintos tipos de archivo, pudiendo tu exportar una URL de algún archivo de usuario para ser visto desde fuera de tu aplicación.

     
    Aquí el widget es presentado en modo embedido (como parte de la página) pero puede presentarse también en modo "dialog box" (como una ventana flotante que aparece cuando se requiera)



    USANDO COCO FILE UPLOADER

    Ir al sitio web de Coco


    Se hace en dos partes:

    Primero: Pones el widget en la vista o formulario en donde lo requieras.
    Segundo: en algun controller pones un action fijo en cualquier controller.

    1. DESCARGA O CLONA COCO.

      Si no usas GIT simplemente copia el contenido de la extension directamente dentro de 'extensions', si usas GIT haz lo siguiente:

      cd /home/blabla/myapp/protected
      mkdir extensions
      cd extensions
      git clone https://bitbucket.org/christiansalazarh/coco.git
    2. SETUP EN CONFIG/MAIN

      Edita tu archivo /protected/config/main.php
      'import'=>array(
              'application.models.*',
              'application.components.*',
              'application.extensions.coco.*',            // <------agrega esto
          ),

    3. CONECTA A "COCO" A TU APLICACION CON UN ACTION ESTATICO

      Edita protected/controllers/siteController.php (aunque puedes usar otra).
      Este action solo es requerido una vez para todo el proyecto !!
      y agregale lo siguiente (coloreado) :

      public function actions()
          {
              return array(
                  'captcha'=>array(
                      'class'=>'CCaptchaAction',
                      'backColor'=>0xFFFFFF,
                  ),
                  'page'=>array(
                      'class'=>'CViewAction',
                  ),
                  'coco'=>array(
                      'class'=>'CocoAction',
                  ),
              );
          }
    4. INSERTA EL WIDGET EN UNA VISTA

      widget('ext.coco.CocoWidget'
              ,array(
                  'id'=>'cocowidget1',
                  'onCompleted'=>'function(id,filename,jsoninfo){  }',
                  'onCancelled'=>'function(id,filename){ alert("cancelled"); }',
                  'onMessage'=>'function(m){ alert(m); }',
                  'allowedExtensions'=>array('jpeg','jpg','gif','png'),
                  'sizeLimit'=>2000000,
                  'uploadDir' => 'assets/',
                  // para recibir el archivo subido:
                  'receptorClassName'=>'application.models.MyModel',
                  'methodName'=>'onFileUploaded',
                  'userdata'=>$model->primaryKey,
              ));
         ?>
      

    ¿ CÓMO FUNCIONA COCO ?
    1. Cuando alguien visite la vista en donde insertaste el Widget verás que aparece un botón con el texto que pusiste en el argumento "buttonText", el cual por defecto dice: "Find & Upload".
    2. Alguien podrá arrastrar un archivo a ese botón o podrá darle clic y éste le presentará una caja de selección de archivo a subir.
    3. El usuario envia el archivo, y coco internamente invoca a tu action: index.php?r=site/coco con algunos argumentos. Va a transferir a ese action el archivo a subir mediante llamadas ajax.  Tu no haces nada en este punto, solo mirar como coco lo hace, incluso te presentará una caja de progreso cancelable.
    4. Cuando el action determina que el archivo fue subido (lo sube en la carpeta que tu indicas en el atributo "uploadDir") entonces, hara una de estas dos cosas:

      a) Si no has configurado los argumentos: receptorClassName y methodName entonces Coco dejará el archivo en el directorio assets (o donde tu digas en "uploadDir") y solo recibiras una notificacion via ajax en el metodo javascript que has definido en "onCompleted".

      b) Si has configurado los argumentos: receptorClassName y methodName, entonces coco desde el lado del servidor creara una instancia de la clase que tu configures en "receptorClassName" y luego invocará un método: aquel que pusiste en "methodName".  En ese método podrás recibir la notificacion del archivo subido.

      Por tanto, para que esta opción B funcione deberás crear una clase asi:

      // creas la clase en: protected/models/MyModel.php  (o donde quieras)
      class MyModel {

          public function onFileUploaded($fullFileName,$userdata) {
              // userdata es el mismo valor que pusiste en config/main
              // fullFileName es la ruta del archivo listo para leer.
          }
      }

      y los argumentos a pasarle al widget serían:
                  'receptorClassName'=>'application.models.MyModel',
                  'methodName'=>'onFileUploaded',


    OTRAS OPCIONES DEL WIDGET:

    'buttonText'=>'Find & Upload',
    'dropFilesText'=>'Drop Files Here !',
    'htmlOptions'=>array('style'=>'width: 300px;'),
    'defaultControllerName'=>'site',
    'defaultActionName'=>'coco',


    USANDO YIIFILEMANAGERFILEPICKER FILE UPLOADER & FILE EXPLORER

    Ir al sitio web de YiiFileManagerFilePicker

    dependencia:  la extensión YiiFileManager  (es muy simple de instalar)

    1. puedes descargar la extensión para Yii Framework o clonarla de aquí:

    cd yourapp/protected/extensions/
    git clone https://bitbucket.org/christiansalazarh/yiifilemanagerfilepicker.git

    2. En tu archivo protected/config/main.php agregarás:

    'import'=>array(
        'application.models.*',
        'application.components.*',
        'application.extensions.yiifilemanager.*',
        'application.extensions.yiifilemanagerfilepicker.*', // <<--THIS
    ), 
    
    
    3. En algún controller (por defecto siteController) debes crear un action que implemente la clase YiiFileManagerFilePickerAction como indico aca, es para que el widget pueda comunicarse con la aplicación.

    class SiteController extends Controller {
        public function actions()
        {
        return array(
            'captcha'=>array(
            'class'=>'CCaptchaAction','backColor'=>0xFFFFFF,),
            'page'=>array('class'=>'CViewAction',),
            'yiifilemanagerfilepicker'=>array(
        'class'=>
            'ext.yiifilemanagerfilepicker.YiiFileManagerFilePickerAction'),
        );
        }
    ...
    }
     
    4. Debes crear la clase que dará vida al widget. Creala dentro de tus componentes, para facilitar las cosas ya hay una clase lista para ser copiada y usada como patrón, por favor no uses la clase directamente desde la extensión, haz una copia en tus componentes. Los detalles de la clase los puedes conseguir en el instructivo README.md dentro de la extensión.


    #copiala desde aqui:
    'protected/extensions/yiifilemanagerfilepicker/demo-component/MyYiiFileManViewer.php'
    
    #a tu propia aplicacion:
    'protected/components/MyYiiFileManViewer.php'



    5. El widget requiere algunos iconos, la extensión trae algunos predefinidos:

    # copiar los iconos a tu aplicacion desde aqui:
    'yourapp/protected/extensions/yiifilemanagerfilepicker/demo-images'  
    
    # a esta ubicacion:
    'tuaplicacion/images/'

    6. En alguna vista insertas el codigo HTML que el widget usará para presentar en el la información, aqui puedes dar el formato html requerido, clases CSS etc, pero respetando lo que aquí se presenta:

    <div>Select a Background image: <a href='#' id='file-picker'>click here</a>
        <img src='' width='50%' id='selected-image' />
    </div>
    <div id='file-picker-viewer'>
        <div class='body'></div>
        <hr/>
        <div id='myuploader'>
            <label rel='pin'><b>Upload Files
                <img style='float: left;' src='images/pin.png'></b></label>
            <br/>
            <div class='files'></div>
            <div class='progressbar'>
                <div style='float: left;'>
                    Uploading your file(s), please wait...</div>
                <img style='float: left;' src='images/progressbar.gif' />
                <div style=
                    'float: left; margin-right:10px;'class='progress'>
                </div>
                <img style='float: left;' class='canceljob' 
                    src='images/delete.png' title='cancel the upload'/>
            </div>
        </div>
        <hr/>
        <button id='select_file' class='ok_button'>Select File(s)</button>
        <button id='delete_file' class='delete_button'>
            Delete Selected File(s)</button>
        <button id='close_window' class='cancel_button'>Close Window
            </button>
    </div>
    <hr/>Logger:<br/><div id='logger'></div>

    7. En la misma vista anterior (paso 6) insertas el widget. 
    Cuando el usuario hace click en el elemento html #file-picker (en el paso6) entonces aparecerá un file-chooser











    Este file-chooser (imagen arriba), aparecera cuando se haga click en el DIV insertado en el paso 6. Para que eso ocurra debes insertar este widget en la misma vista donde pusiste el código del paso6.

    Por defecto en este caso el file-chooser aparece en forma embedida, es decir, sin mostrar un dialogbox, para hacerlo con un dialogbox se requiere un minimo de cambios expuestos en el README.md de la extensión (en ingles).



    <?php
        // the widget
        //
        $this->widget('application.components.MyYiiFileManViewer'
        ,array(
            // layout selectors:
            'launch_selector'=>'#file-picker',
            'list_selector'=>'#file-picker-viewer',
            'uploader_selector' => '#myuploader',
            // messages:
            'delete_confirm_message' => 'Confirm deletion ?',
            'select_confirm_message' => 'Confirm selected items ?',
            'no_selection_message' => 'You are required to select some file',
            // events:
            'onBeforeAction'=>
                "function(viewer,action,file_ids) { return true; }",
            'onAfterAction'=>
                "function(viewer,action,file_ids, ok, response) { 
                    if(action == 'select'){ 
                      // actions: select | delete
                      $.each(file_ids, function(i, item){ 
                      $('#logger').append('file_id='+item.file_id 
                      + ', <img src=\''+item.url+'&size=full\'><br/>');
                    });
                }
            }",
            // 'onBeforeLaunch'=>"function(_viewer){ }",
            'onClientSideUploaderError'=>
                "function(messages){ 
                    $(messages).each(function(i,m){  alert(m); }); 
                }
            ",
            'onClientUploaderProgress'=>"function(status, progress){
                $('#logger').append(
                    'progress: '+status+' '+progress+'%<br/>');
                }",
            ));
    ?>

    Basicamente el widget solo te pide los nombres de los componentes html donde se presentará al file-chooser además de las funciones de escucha de eventos.
     
    Lo que hace este widget es insertar eventos jQUERY para que cuando hagas click en el lanzador del paso6 (#file-picker) entonces aparezca un selector de archivos (imagen arriba de esta nota).  

    La manera en cómo tu quieres presentar el lanzador de archivos es tu asunto, el widget se enfoca en proveer funcionalidad y tu responsabilidad es la de decir cómo se presenta.

    Hay algunos eventos javascript que te gustaría oir, a los cuales puedes adosar funciones JS:

    onBeforeAction  #lanzado antes que se seleccionen los archivos
    onAfterAction  #lanzado cuando se ha hecho click en seleccionar archivos 
    onClientSideUploaderError #cuando en el browser ha ocurrido un problema
    onClientUploaderProgress  #para ver el progreso de la subida
    ¿ Dónde controlas los tipos de archivo requerido y demás asuntos ? en tu clase 'protected/components/MyYiiFileManViewer.php', la cual hiciste en el paso4.

    8. Y cómo funciona ?

    Recuerdas en el paso6 este extracto de código ?
    <div>Select a Background image: <a href='#' id='file-picker'>click here</a>
        <img src='' width='50%' id='selected-image' />
    </div>
    Eso presentará un link asi: "Select a background image: click here"

    Cuando el usuario hace click entonces via jQUERY aparecerá el file-chooser apuntado por el selector jQUERY: "#file-picker" en el cual tu tienes definido el layout html a su vez renderizado por el widget del paso7.

    Cuando el usuario hace selección de archivos en el file-chooser tu recibes eventos jquery, cuando ha seleccionado archivos y estos son subidos al servidor tu recibes eventos en la clase del paso4 ('protected/components/MyYiiFileManViewer.php') en donde tu aceptas y controlas.

    El usuario puede guardar archivos en su propio espacio web, esto es controlado por otro componente que no he nombrado pero que forma parte de las dependencias de YiiFileManagerFilePicker: 
    la extensión YiiFileManager  esta es muy simple de instalar y se encarga de darle al widget los archivos del usuario.

    De nuevo, en la clase 'protected/components/MyYiiFileManViewer.php'
    es donde tu manejas los archivos seleccionados o subidos.  Recuerda que el usuario puede seleccionar un archivo que previamente habia subido en otra sesión.


    Links usados en este post

    Cómo recibir archivos via http 

    YiiFileManager (low level file manager yiiframework)

    YiiFileManagerFilePicker (ajax based file uploader & file explorer yiiframework)

    YiiFileManagerRemote

    Coco (ajax based file uploader yiiframework)