martes, 29 de enero de 2013

OpenProj error “Your Java vendor is Oracle Corporation”

OpenProj es un sistema que funciona con Java Runtime, esta hecho para gestionar Proyectos.

La url para obtenerlo es:
http://sourceforge.net/projects/openproj/?source=dlp

La instalación es bien simple, desde Linux, solo descomprimir el contenido del paquete descargado en el directorio /usr , requiere superusuario, obviamente.

Para ejecutarlo simplemente correr este comando:  openproj

El problema es:

Require Java Runtime del "vendor" SUN, y desde que Oracle tomo el control de la cosa ahora Java ya no es de SUN sino de Oracle y eso no le gusta a OpenProj porque para la fecha de su creación era Sun el vendor y no Oracle como ahora.

Solución:

Se debe editar el archivo de configuracion local localizado en el directorio:
/home/tu-usuario/.openproj/run.conf

y poner lo siguiente en la línea de JAVA_OPTS:

JAVA_OPTS="-Djava.vendor=Sun -Xms128m -Xmx768m"

listo, con eso la cosa funciona.

sábado, 26 de enero de 2013

Problemas con el Apache .htaccess mod_rewrite

Mi .htaccess no funciona

Dilema.

Resumen corto para los entendidos:

1. Que la directiva AllowOverride no este configurada a "None" en httpd.conf.
2. Que el mod_rewrite este habilitado en httpd.conf.
3. Recuerda hacer pruebas con previa limpieza de la cache del navegador.

Primero, qué es y para qué sirve un .htaccess

Un .htaccess es un archivo de configuración en modo texto que solo funciona cuando un servidor web esta siendo atendido por un servicio de Apache.

Tiene muchos usos, dividos basicamente en dos categorías:

1) Para configurar el acceso a los archivos dentro de un directorio de un website. (para que un visitante no pueda visualizar archivos delicados que estan alojados dentro del website, por ejemplo).

2) Para controlar otros servicios de apache entre ellos mod_rewrite, el cual es el objeto de este tema y al cual me enfocaré.

(si..hay mas usos, pero al iniciado no le vamos a lanzar todo de una vez verdad ?)

El principio:  Apache

Apache es un servidor web que funciona en Linux o en Windows, es un sistema que permite que un computador pueda ofrecer servicios web.  Toda la información reelevante esta aqui: http://httpd.apache.org/

El módulo de Apache: mod_rewrite

http://httpd.apache.org/docs/current/mod/mod_rewrite.html

Es un módulo de Apache que presta servicios al website basados en "sobreescribir" (mod_rewrite) la url que nosotros hemos introducido usando una expresion regular:

Con un ejemplo:

Queremos que cuando un usuario de la web ingrese a:

http://www.miwebsite.com/articulos/pelota/azul/

entonces que en realidad acceda a :

http://www.miwebsite.com/index.php?r=/catalogo/buscar&name=pelota&color=azul

Si te has dado cuenta la segunda URL es espantosa pero seguramente es la URL que va a funcionar en el sistema que tenemos hecho.
La primera URL es la que se llama "Friendly Url", es aquella mejor apetecida por los buscadores y demás instrumentos, incluso humanos.

Esta sobreescritura de URL es llevada a cabo por el modulo mod_rewrite del servicio apache mediante la configuración indicada en el archivo .htaccess localizado en la raiz de tu sitio web, siempre y cuando:
La directiva AllowOverride de apache no este configurada en modo "None".


http://httpd.apache.org/docs/current/mod/mod_rewrite.html

Habilitar el .htaccess en Apache

Antes que nada, hay que asegurarse que Apache va a habilitar los .htaccess, de otro modo no importa que basura pongas allí, esta no va a funcionar. Para habilitar el .htaccess sobre Apache primero hay que editar el archivo httpd.conf y establecer allí la directiva AllowOverride a algun valor distinto a None.

Mas información de la directiva aqui:

http://httpd.apache.org/docs/2.2/mod/core.html#allowoverride

<Directory /mi/sitio/web/mi/ruta/>
   ..otras directivas aqui
   AllowOverride All
</Directory>


Lo que aquí ocurre es que AllowOverride All admite que tu puedas "sobreescribir" (override) las directivas de acceso al directorio donde tu estas alojando tu sitio web. De otro modo, cuando tu generes un archivo .htaccess en tu sitio web este será ignorado por completo, no importa si el modulo de apache mod_rewrite este o no habiliado.
Debes comprender que el archivo .htaccess es un archivo de Apache, y que el modulo mod_rewrite es simplemente un subproducto que usa a Apache, por consecuencia usa los servicios de .htaccess que este le provee para leer desde allí lo que mod_rewrite necesite para funcionar, por tanto si no puedes darle valores a .htaccess debido a la negativa de la directiva mencionada entonces mod_rewrite no va a funcionar.
Y qué es httpd.conf ? es el archivo de configuración básico del servidor web de Apache. Muy seguramente en un entorno de hospedaje de la calle esto no será necesario porque el webmaster de esa empresa ya lo habilitó, deberá hacerlo, de otro modo nadie le va a hospedar nada allí porque su servicio no servirá para casi nadie.

Habilitar el modulo mod_rewrite en Apache

El módulo mod_rewrite debe ser habilitado por el webmaster del servicio de hospedaje, casi todos lo admiten, algunos requieren que tu explicitamente lo invoques (caso 1and1), como mencione mas arriba la información detallada del modulo esta aqui:

http://httpd.apache.org/docs/current/mod/mod_rewrite.html

Cómo saber si el modulo mod_write esta instalado en tu hospedaje o servidor web:

1. crea un archivo llamado info.php en alguna parte de tu website, con el siguiente contenido:
<?php echo phpinfo(); ?>

2. accede a el desde el browser: http://localhost/tuwebsite/info.php

3. Buscarás el texto "mod_rewrite" (con el buscador del browser), deberá aparecer en:

Loaded Modulescore mod_log_config mod_logio prefork http_core mod_so mod_alias mod_auth_basic mod_authn_file mod_authz_default mod_authz_groupfile mod_authz_host mod_authz_user mod_autoindex mod_cgi mod_deflate mod_dir mod_env mod_mime mod_negotiation mod_php5 mod_reqtimeout mod_rewrite mod_setenvif mod_status


Si aparece el texto "mod_rewrite" es porque esta instalado en el Apache. Si no aparece, hay que habilitarlo en el archivo de configuración del apache, de esta forma:

Habilitar mod_rewrite en Apache

Caso Linux:

La instalación de Linux comunmente trae el modulo rewrite, pero deshabilitado.  Para habilitarlo hay que ingresar al servidor con permisos de root y crear un enlace simbolico al modulo, luego reiniciar el servicio apache, en resumidas cuentas se hace esto:

# su
# password ****
# ls /etc/apache2/mods-available/rewrite.load

  ...como resultado veran que al listar aparecera el modulo:
# rewrite.load

Hasta aqui confirmamos que el modulo esta disponible, ahora para ver si esta instalado (importante: se busca en "mods-enabled")

ls -l /etc/apache2/mods-enabled/rewrite.load

Si no esta instalado (porque no aparece al listarlo con el comando anterior) entonces hay que crear un enlace simbolico:

cd /etc/apache2/mods-enabled   #bajo root
ln -s /etc/apache2/mods-available/rewrite.load rewrite.load #crear enlace sim
/etc/init.d/apache2 restart   #reiniciar apache



En el caso de Windows:
es mas simple, se edita el archivo:
c:/archivos de programa/apache2/bin/httpd.conf y se habilita la linea que ya viene escrita pero con un # por delante, ese # es indicador de que la linea esta comentada por tanto no va a funcionar, al quitarla va a funcionar.

En ambos casos hay que reiniciar el servicio apache.  Vuelve a cargar el archivo info.php y debería aparecer el texto mod_rewrite indicado en la figura de arriba.

Crear un archivo .htaccess

En un sitio web cualquier que tengas, crea un archivo llamado ".htaccess",  verifica que en el caso de windows no se este creando en cambio un archivo ".htaccess.txt" porque no va a servir. Igualmente es mejor hacerlo por consola debido a que algunos editores de texto consideran a los archivos que empiezan por "." como archivos de sistema y pueden haber problemas.

En el caso de Windows no podrás crear el archivo desde el "explorador de archivos" (file manager), debido al "." que esta al inicio de ".htaccess", hazlo por consola: "cmd.exe".

Probar un archivo .htaccess en el website

Supongamos que tenemos el sitio web: http://localhost/miweb con SOLO UN archivo dentro (solo texto1.html, nada mas, sin ningun index.html )

http://localhost/miweb/texto1.html  (que dira "hola1")

Creamos un .htaccess (en linux):

cd /home/christian/www/miweb/
$ cat > .htaccess
(escribe algo:)  # comentario de prueba
(presiona control+Z)

eso va a crear un archivo .htaccess con una linea comentada que dice: "#comentario de prueba". hazlo por otra via si es necesario, pero debe llamarse ".htaccess".

Ahora navega a: http://localhost/miweb/texto1.html no debe haber ningun fallo, debe decir: "hola 1"

Ok no hay nada malo, podemos seguir con las pruebas.

Edita el archivo .htaccess y agreguemosle una regla de sobreescritura para que al escribir:
http://localhost/miweb/index.html entonces en realidad nos devuelva el contenido de "texto1.html" tal como si hubiesemos navegado directamente sobre "texto1.html", al navegar a "index.html" dirá "hola 1", considerando que "index.html" no existe..!
IMPORTANTE:
index.html "no existe" en estas pruebas, lo virtualizaremos usando una regla usando a mod_rewrite, de modo que el visitante "crea que existe" pero en realidad no y en cambio estará viendo el contenido de otro archivo.

Escribe lo siguiente dentro del ".htaccess":


RewriteEngine On     # esto le dice a apache que active a mod_rewrite para este website
RewriteRule ^index.html$ texto1.html [L]      # es es una regla de sobreescritura

Si esta funcionando, entonces al ingresar a:  http://localhost/miweb/index.html dira "hola 1"


Que sigue

Mod_rewrite es muy complejo y completo, explicarlo aqui se va del alcance del documento, por tanto una vez iniciado estes podrás seguir tu curso y ahondar mas en esta rama.

Debes aprender ahora sobre "expresiones regulares", ya que mod_rewrite esta basado en esta tecnología, cuando puse "^index.html$" en realidad es una "expresion regular" (regexp como se le conoce) la cual le dice a apache que "donde diga -index.html- entonces ahora...".

Lo que basicamente hace mod_rewrite es verificar si una URL hace "match" con alguna de las reglas, para entonces proceder con la acción para esa regla.

acerca de expresiones regulares en yii framework
http://es.wikipedia.org/wiki/RegExp
http://httpd.apache.org/docs/current/mod/mod_rewrite.html
http://httpd.apache.org/docs/2.2/mod/core.html#allowoverride

Suerte, camino largo por delante.

miércoles, 16 de enero de 2013

Usando Ajax

Usando Ajax

Siempre es bueno recordar lo simple en forma simple, por eso aqui pongo un ejemplo de cómo incoporar un simple boton en una pagina html que lea un IDPRODUCTO y traiga en respuesta (usando ajax) una lista de productos relacionados.

Artículos Relacionados:



Ejemplo

Via ajax se pedirá a un action alojado en ProductoController.php que nos de una lista de objetos en formato JSON (por eso en el action veras el "header" y la llamada a CJSON::encode ).   Una vez recibida la lista se procesa item por item creando una lista que mostrara un ID y un nombreProducto en un html tag de tipo "UL" (una simple lista).


Paso 1


Tienes una página index.html con un simple botón y un área para incorporar el listado allí. Omitiré la parafernalia de Yii Framework, dejando solo lo del ejemplo:

  1. <div>
  2.   <input type='button' id='miboton' value='Probar'></input>
  3.   <div id='listado-va-aqui'>...</div>
  4. </div>


Paso 2

A esa página index.html se le adjunta el script:

  1. <script>
  2.    $.fn.imprimeListado = function(listado, selector){
  3.       selector.html('Listado:');
  4.       selector.append('<UL></UL>');
  5.       var ul = selector.find('ul');
  6.       $.each(listado, function(key,item){
  7.          // item dependera de tu modelo Producto.php
  8.          var li = "<li alt='"+item.id+"'>"+item.nombreProducto+"</li>";
  9.          ul.append(li);
  10.          li = ul.find("[alt='"+item.id+"']");
  11.          li.click(function(){
  12.              var _item = $(this).attr('alt');
  13.              alert("hola hiciste click en el item: "._item);
  14.          });
  15.       });
  16.    }
  17.    
  18.    $.fn.leerProductos = function(idproducto, selector){
  19.        selector.html("<img src='images/loading.gif'/>");
  20.        $.ajax({
  21.           url: 'index.php?r=/producto/listadoajax/',
  22.           type: 'post',
  23.           cache: false,
  24.           data: { idproducto: idproducto },
  25.           success: function(resp){
  26.              if(resp.ok == true){
  27.                $.fn.imprimeListado(resp.listado, selector);
  28.              }else{
  29.                selector.html(resp.mensaje);
  30.              }
  31.           },
  32.           error: function(e){
  33.              selector.html(e.responseText);
  34.           }
  35.        });
  36.    }
  37.    $('#miboton').click(function(){
  38.        $.fn.leerProductos(123, $('#listado-va-aqui'));
  39.    });
  40. </script>

Paso 3

Se crea el action en el lado de Yii Framework, en el controlador obviamente:

  1. public function actionListadoAjax(){
  2.   // funciona via POST a proposito para saber.
  3.   $idproducto = $_POST['idproducto'];
  4.   $listado = Producto::model()->findAllByAttributes(
  5.      array('idproducto'=>$idproducto));
  6.   if($listado == null){
  7.     header("Content-type: application/json");
  8.     echo CJSON::encode(array('ok'=>false,
  9.       'mensaje'=>'idproducto no tiene items',
  10.          'listado'=>null));
  11.   }
  12.   else {
  13.     header("Content-type: application/json");
  14.     echo CJSON::encode(array('ok'=>true,
  15.       'mensaje'=>'', 'listado'=>$listado));
  16.   }
  17.   // si ocurre una excepcion o si se emite una
  18.   // entonces caera en la seccion "error" del
  19.   // cuerpo de las opciones del elemento $.ajax:
  20.   // throw new Exception('pruebame');
  21. }

viernes, 11 de enero de 2013

Usando JOIN con CDbCriteria (Yii Framework)


Ejemplo de modelo de persistencia tradicional Many-Many en YiiFramework


En el siguiente ejemplo tenemos Correa y Perrito, varios de cada una, a veces necesitamos saber (a) que correas son de un perrito, y a veces al revés, preguntando (b) que perritos usan una correa específica. Aunque este último caso (b) no esta en el código ejemplo puedes inferirlo fácilmente invirtiendo la lógica del caso (a).

Diagrama de Datos

1. Supongamos el siguiente "Diagrama de Clases UML" que muestra cómo estan relacionados los objetos:
















2. Del diagrama UML anterior, se puede deducir un "Diagrama de Datos" (que no es lo mismo aunque se parezcan):



3. Los datos de ejemplo del modelo son:


4.  Tenemos las siguientes Clases modeladas en sistema con Yii Framework y CActiveRecord, obviamente simplificadas para no perder el foco del ejemplo:


  1. class Perrito extends CActiveRecord {
  2.   // $id, $name  
  3. }
  4. class PerritoCorrea extends CActiveRecord {
  5.   // $perrito_id, $correa_id  
  6. }
  7. class Correa extends CActiveRecord {
  8. // $id, $color



5. El requerimiento de negocio es:
 "Listar las correas de un perrito, buscando por el nombre del perrito".

Considera que al buscar por correa, primero hay que ir a la relacion correa-perrito para saber que perritos usan la correa a ser considerada, pero antes, hay que ir desde correa-perrito hacia el perrito para saber si su nombre coincide con la búsqueda.

Esto ultimo se hará con una busqueda basada en el objeto de clase CDbCriteria aplicado en la clase Correa,  y por qué en Correa ? porque el concepto dice que son: "las correas de un perrito"...y no al revés.

Por tanto, en Correa.php, agregamos un método: listarPorPerrito($nombre), que recibe como argumento el nombre del perrito al que queremos sacar a pasear con su correa.


  1. class Correa extends CActiveRecord {
  2. // $id, $color
  3.     public function listarPorPerrito($nombre_perrito){
  4.         $c = new CDbCriteria();
  5.         // queremos buscar las correas del "Perrito.name" igual a "KIM",
  6.         // pero pasando por toda la relacion:
  7.         //
  8.         // select * from Correa 't'  <-- CDbCriteria pone la 't' automaticamente
  9.         //  left join PerritoCorrea PXC on PXC.correa_id = t.id
  10.         //  left join Perrito PX on PX.id = PXC.perrito_id
  11.         // where
  12.         //   PX.name like '%KIM%'
  13.        
  14.         $px  = Perrito::model()->tablename();
  15.         $pxc = PerritoCorrea::model()->tablename();
  16.         $c->join =
  17.            'left join '.$pxc.' PXC on PXC.correa_id = t.id'
  18.           .'left join '.$px.' PX on PXC.perrito_id = PX.id'
  19.         ;
  20.         $c->compare('px.name',$nombre_perrito,true); // <-- 'true' indica "Like"
  21.         return new CActiveDataProvider($this,array(
  22.            'criteria'=>$c,
  23.            'pagination'=>array('pageSize'=>5),
  24.             // el sort no lo pongo para no sacar de foco el ejemplo, pero considera
  25.             // que tu criteria $c dispone de PX.id, PX.name (PX es Perrito), por
  26.             // tanto puedes usar esos nombres para ordernar. experimentar con eso.
  27.         ));
  28.     }
  29. }



El equivalente SQL de este "Criteria" sería:

  1. select * from Correa t
  2.   left join PerritoCorrea PXC on PXC.correa_id = t.id
  3.   left join Perrito PX on PX.id = PXC.perrito_id
  4.   where
  5.   PX.name like '%KIM%'



6. Esta listo, y ahora cómo se usa ?

Como de 50 maneras diferentes..., aqui verás varias:

6.1-primero creas un action en algun controller para poder acceder a la información:

  1. // protected/controller/xxxController.php
  2. public function actionListarCorreas($perrito){
  3.   $dp = Correa::model()->listarPorPerrito($perrito);
  4.   $this->render('correas',array('dataProvider'=>$dp));
  5. }



6.2-ahora, creas una vista llamada "protected/views/xxx/correas.php", o como quieras, y la renderizas así:

  1. // protected/views/xxx/correas.php
  2. $this->widget('zii.widgets.grid.CGridView', array(
  3.    'dataProvider'=>$dataProvider,
  4. ));


6.3-listo, ya dispones de una URL para visualizarla:

index.php?r=/xxx/correas&perrito=kim

a la cual podrias acceder asi:

CHtml::link("ver correas de perrito",array("/xxx/correas","perrito"=>"kim"));

o con ajax desde cualquier parte usando javascript con jQuery:


  1. $.ajax({
  2.   url: 'index.php?r=/xxx/listarcorreas&perrito=kim',
  3.   cache: false, type: 'get',
  4.   success: function(html){
  5.     // $.fancybox(html);  <- si usas fancybox
  6.     // o a rin pelao usando un <div id='ventanita'></div>
  7.     $('#ventanita').html(html);
  8.   }
  9. });


Advertencia en Alto Volúmen, cómo evitar el colapso al usar las relaciones que GII contruye ?:

Si por ejemplo tuviésemos un modelo de datos "Marca->Factura", entonces pudiésemos tener 100 marcas y 500000 de facturas del otro lado, entonces, que sucede si una de esas marcas tiene 100.000 facturas ? sucederá que al usar relations() (foreach($marca->facturas as $f) echo $f->numero; ) esto colapsará estrepitosamente, debido a que relations() usa arrays.  Este ejemplo que pongo acá en el blog está basado en CDbCriteria, justamente para evitar el colapso, entregando DataProviders en vez de arrays.

Espero te haya servido.
el código de ejemplo esta aquí:  http://pastebin.com/ghxQ8GGn


OTRO EJEMPLO
/*
 relaciones:
 
 Paciente {id,  nombre}
 Centro {id, nombre}
 PacienteCentro {paciente_id,centro_id}
 
 USO:
 
 $centro = Centro::model()->findByAttributes(array("nombre"=>"villa portales block2"));
 $dataProvider = $centro->pacientes;
 // o con argumentos:
        $dataProvider = $centro->getPacientes("salazar");  // todos los salazar del centro seleccionado
*/
class Centro extends CActiveRecord {
public function getPacientes($filtra_por_nombre_paciente=null, $pagesize=5){
 $c = new CDbCriteria();
 $px  = Paciente::model()->tablename();
 $pxc = PacienteCentro::model()->tablename();
 $c->join =
  'left join '.$pxc.' PXC on PXC.centro_id = t.id'
  .'left join '.$px.' PX on PXC.paciente_id = PX.id'
 ;
        if(null != $filtra_por_nombre_paciente)
  $c->compare('px.nombre',$filtra_por_nombre_paciente,true);
 return new CActiveDataProvider($this,array(
  'criteria'=>$c,
  'pagination'=>array('pageSize'=>$pagesize),
 ));
}
}//eof

class Paciente extends CActiveRecord {
public function getCentros($filtra_por_nombre_centro=null, $pagesize=5){
 $c = new CDbCriteria();
 $cx  = Centro::model()->tablename();
 $pxc = PacienteCentro::model()->tablename();
 $c->join =
  'left join '.$pxc.' PXC on PXC.paciente_id = t.id'
  .'left join '.$cx.' CX on PXC.centro_id = CX.id'
 ;
        if(null != $filtra_por_nombre_centro)
  $c->compare('cx.nombre',$filtra_por_nombre_centro,true);
 return new CActiveDataProvider($this,array(
  'criteria'=>$c,
  'pagination'=>array('pageSize'=>$pagesize),
 ));
}
}//eof