miércoles, 8 de junio de 2011

jCombo - Combos Anidados con jQuery PHP y MySQL

¿Que es un combo anidado?

Un combo anidado son listas desplegables de opciones en donde una depende de la otra en forma de cascada, lo que quiere decir que al seleccionar un elemento de una lista, la otra lista se debe modificar inmediatamente. Normalmente encontramos estos combos en los formularios donde tenemos que seleccionar un pais, luego una region y finalmente una ciudad o provincia. Y todo esto sin tener que volver a cargar la página gracias a AJAX.

¿Y como se implementa?

Para implementarlo se deben hacer llamadas a través de un objeto ajax en el lado del cliente, recibir la peticion, procesarla y convertirla en un elemento "SELECT" dentro de nuestro formulario, sin embargo aqui les dejo la manera simple de hacerlo, solo tienen que utilizar jquery dentro de su formulario, incluyen el plugin y realizan las llamadas de la siguiente manera:

$("select[name='country']").jCombo("getCountries.php");

o

$('#ciudades').jCombo("getEstados.php?id=", {

parent: "#estados", // select del cual dependemos

initial_text: "-- Favor Seleccione --", // texto inicial

selected_value: "5" // valor por defecto para seleccionar

});


Para ver a mas detalle acerca de su uso, la documentación necesaria y descarga del plugin deben revisar este sitio:
http://www.prodiven.com/jcombo

martes, 15 de marzo de 2011

Tips en Apache para mejorar tu servidor web

Algunos tips para configurar Apache y mejorar el rendimiento. Lo que todo desarrollador debe saber.

1. Prevenir el Hotlinking

¿Estás cansado de que la gente utilice tu ancho de banda poniendo las imágenes albergadas en tu servidor en su sitio web? Añade el siguiente código al final de tu archivo .htaccess para prevenir el hotlinking:


1. Options +FollowSymlinks
2. #Protect against hotlinking
3. RewriteEngine On
4. RewriteCond %{HTTP_REFERER} !^$
5. RewriteCond %{HTTP_REFERER} !^http://(www.)?nombrededominio.com/ [nc]
6. RewriteRule .*.(gif|jpg|png)$ http://nombrededominio.com/img/no_robar_ancho_de_banda.gif[nc]

2. Bloquea todas las peticiones del user-agent

Es posible bloquear a todos los user-agents indeseados que pueden ser potencialmente dañinos o quizá simplemente para mantener la carga del servidor lo más baja posible.


1. #Block bad bots
2. SetEnvIfNoCase user-Agent ^FrontPage [NC,OR]
3. SetEnvIfNoCase user-Agent ^Java.* [NC,OR]
4. SetEnvIfNoCase user-Agent ^Microsoft.URL [NC,OR]
5. SetEnvIfNoCase user-Agent ^MSFrontPage [NC,OR]
6. SetEnvIfNoCase user-Agent ^Offline.Explorer [NC,OR]
7. SetEnvIfNoCase user-Agent ^[Ww]eb[Bb]andit [NC,OR]
8. SetEnvIfNoCase user-Agent ^Zeus [NC]
9.
10. Order Allow,Deny
11. Allow from all
12. Deny from env=bad_bot

3. Redirecciona a todos, excepto a ciertas IPs

Si por alguna razón deseas denegar el acceso a todos los usuarios o sólo permitir a un cierto grupo de direcciones IP para que tengan acceso a tu sitio, añade el siguiente código a tu archivo .htaccess:


1. ErrorDocument 403 http://www.nombrededominio.com
2. Order deny,allow
3. Deny from all
4. Allow from 124.34.48.165
5. Allow from 102.54.68.123

4. Redirecciones 301 amigables con SEO

Si has transferido nombres de dominio o deseas redireccionar a una página específica sin que afecte a los resultados de los motores de búsqueda como Google, utiliza el siguiente código:


1. Redirect 301 /d/file.html http://www.nombrededominio.com/r/file.html

5. Crea una página personalizada para cada tipo de error

Si te cansaste del diseño por defecto de las páginas de error 401, 403, 404, 500, etc. puedes fácilmente crear la tuya propia y referirte hacía ella de esta forma:


1. ErrorDocument 401 /error/401.php
2. ErrorDocument 403 /error/403.php
3. ErrorDocument 404 /error/404.php
4. ErrorDocument 500 /error/500.php


6. Crea una lista de IPs prohibidas

¿Te cansaste de obtener siempre los mismos comentarios de ciertos usuarios una y otra vez? Simplemente prohíbe el acceso a determinadas IPs, añadiendo el siguiente código:


1. allow from all
2. deny from 145.186.14.122
3. deny from 124.15

7. Setea una dirección de e-mail por defecto para el administrador de servidor

Con el siguiente código puedes especificar la dirección de correo electrónico por defecto para el administrador de servidor:


1. ServerSignature EMail
2. SetEnv SERVER_ADMIN default@dominio.com

8. Deshabilita la ventana de aviso de descarga

Usualmente al descargar algo de un sitio web, saltará una ventana que pregunta si deseas abrir el archivo o guardarlo en tu PC. Para prevenir esto, utiliza el siguiente código:


1. AddType application/octet-stream .pdf
2. AddType application/octet-stream .zip
3. AddType application/octet-stream .mov

9. Protege un archivo específico

El código que sigue te permite denegar el acceso a cualquier archivo que desees, mostrando una página de error 403 cuando alguien intenta acceder al mismo:


1. #Protect the .htaccess File
2.
3. order allow,deny
4. deny from all


10. Comprime componentes con mod_deflate

Como alternativa a la compresión de archivos mediante Gzip, puedes implementar mod_deflate (que se supone es más rápido). Coloca el siguiente código en la parte superior de tu archivo .htaccess:


1. SetOutputFilter DEFLATE

11. Agrega headers de expiración a los archivos

El siguiente código muestra cómo agregar fechas de expiración en las cabeceras de los archivos:


1. Header set Expires "Wed, 21 May 2010 20:00:00 GMT"

12. Seteando la página por defecto

Puedes hacer que la página a mostrar ni bien un usuario llegue a tu sitio no sea la archiconocida "index". Aquí seteamos "about.html" como página de inicio:


1. #Serve Alternate Default Index Page
2. DirectoryIndex about.html


13. Proteje tus archivos y directorios con una contraseña


1. #proteger un archivo con contraseña
2.
3. AuthType Basic
4. AuthName "Prompt"
5. AuthUserFile /home/path/.htpasswd
6. Require valid-user
7.
8.
# proteger un directorio con contraseña
9. resides
10. AuthType basic
11. AuthName "This directory is protected"
12. AuthUserFile /home/path/.htpasswd
13. AuthGroupFile /dev/null
14. Require valid-user

14. Hacer una redirección 301 de un dominio viejo a uno nuevo

Incluyendo este snippet en el .htaccess de tu viejo dominio, conseguirás redigir a otro nuevo (sin ser penalizado por Google por duplicar contenido, dado que el 301 es un aviso de "redirección permanente"):


1. # Redirigir de un dominio viejo a uno nuevo
2. RewriteEngine On
3. RewriteRule ^(.*)$ http://www.nuevodominio.com/$1 [R=301,L]

15. Forzar el cacheo

El siguiente snippet no va a incrementar directamente la velocidad de carga de tu sitio en general, pero sí va a cargar más rápido cuando el mismo usuario vuelva a visitarlo al enviar un status 304 cuando se pidan elementos que no han sido modificados. Puedes cambiar la frecuencia de cacheo al cambiar el número de segundos (en este ejemplo se da una vez al día):


1. FileETag MTime Size
2. ExpiresActive on
3. ExpiresDefault "access plus 86400 seconds"

16. Comprimir componentes al activar Gzip

Esto hace que sea más rápida la carga de tu sitio:


1. AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml application/xhtml+xml text/javascript text/css application/x-javascript
2. BrowserMatch ^Mozilla/4 gzip-only-text/html
3. BrowserMatch ^Mozilla/4.0[678] no-gzip
4.
5. BrowserMatch bMSIE !no-gzip !gzip-only-text/html

17. Quitar palabras o strings de una URL

En este ejemplo, quitaremos "category" de nuestras URL, es decir: http://tudominio.com/category/blue -> http://tudominio.com/blue

Agrega este código al final de tu .htaccess:


1. RewriteRule ^category/(.+)$ http://www.tudominio.com/$1 [R=301,L]

18. Deshabilitar la navegación por los directorios de tu sitio


1. Options All -Indexes

19. Redirigir los Feeds de WordPress a FeedBurner


1. #Redirect wordpress content feeds to feedburner
2.
3. RewriteEngine on
4. RewriteCond %{HTTP_USER_AGENT} !FeedBurner [NC]
5. RewriteCond %{HTTP_USER_AGENT} !FeedValidator [NC]
6. RewriteRule ^feed/?([_0-9a-z-]+)?/?$ http://feeds.feedburner.com/tufeed [R=302,NC,L]

20. Remover la extensión del archivo de una URL

Para que sea más amigable al usuario y a los buscadores, por ejemplo "http://www.tudominio.com/autos.html" -> "http://www.tudominio.com/autos"


1. RewriteRule ^(([^/]+/)*[^.]+)$ /$1.php [L]

21. Quitar el "www" del dominio


1. #remove www from URI
2. RewriteEngine On
3. RewriteCond %{HTTP_HOST} ^www\.dominio\.com$ [NC]
4. RewriteRule ^(.*)$ http://dominio.com/$1 [L,R=301]

miércoles, 12 de enero de 2011

Lo mejor del Iphone 5


Como es usual Apple tomó la decisión de agregar fascinantes caracteristicas en la nueva generación del IPhone. Ha adquirido a la compañia Polar Rose, conocida por su tecnología de reconocimiento facial FaceLib, el cual predomina sobre otros productos similares.

Con una detección total del rostro y seguimiento de caras en un video, las soluciones Polar Rose podrian cambiar la manera de interactuar con el iPhone. ahora que el iPhone 4 tiene una camara frontal, el bloqueo por contraseña terminará siendo una antiguedad.

Imagina que levantes tu iPhone y automaticamente te aparece la pantalla de inicio. El truco es que tu iPhone no hará esto para nadie mas. Si el software en el iPhone reconoce tu rostro, reemplazara la contraseña. Algunas nuevas patentes reveladas de Apple indican que el iPhone podrá reconocer la forma de tu mano, que podria reemplazar también la contraseña de cuatro digitos.

Otros usos interesantes de esta tecnología incluyen etiquetado automático de personas en fotos y reconocimiento a través de la libreta de contactos. Se podrian utilizar varios algoritmos que analicen y organicen las fotos automaticamente por tipo o grupos de personas.

iPhoto actualmente reconoce rostros en fotos para propósitos de etiquetado. Es posible que Apple esté tratando de mejorar y expandir esta funcionalidad. Polar Rose eliminó su servicio de etiquetado para Facebook y Flickr recientemente, enfocando su interés en compañias grandes que requieran su tecnología. Sin embargo Apple no ha confirmado su adquisición de Polar Rose.

martes, 11 de enero de 2011

8 queens (problema de las 8 reinas php)

Aqui les dejo un problema de backtracking para aquellos entusiastas de la programación, se trata de un problema clásico que consiste en ordenar 8 reinas en un tablero de ajedrez de manera que no coincidan lineal o diagonalmente. El algoritmo busca todas las soluciones posibles y se puede parametrizar para diferentes tipos de tablero. Debe ser probado con un N<=25 para no elevear drásticamente el coste computacional.

<>
<>
<>
< ?php

/*************************************************************************
*
* This algorithm solves the 8 queens problem using backtracking.
* Please try with N<=25 * * * *************************************************************************/ class Queens { var $size; var $arr; var $sol; function Queens($n = 8) { $this->size = $n;
$this->arr = array();
$this->sol = 0;
// Inicialiate array;
for($i=0; $i<$n; $i++) { $this->arr[$i] = 0;
}
}

function isSolution($n) {
for ($i = 0; $i < $n; $i++) { if ($this->arr[$i] == $this->arr[$n] ||
($this->arr[$i] - $this->arr[$n]) == ($n - $i) ||
($this->arr[$n] - $this->arr[$i]) == ($n - $i)) return false;
}
return true;
}

function PrintQueens() {
echo("solution #".(++$this->sol)."\n");
for ($i = 0; $i < $this->size; $i++) {
for ($j = 0; $j < $this->size; $j++) {
if ($this->arr[$i] == $j) echo("& ");
else echo(". ");
}
echo("\n");
}
echo("\n");
}


// backtracking Algorithm
function run($n = 0) {
if ($n == $this->size) $this->PrintQueens();
else {
for ($i = 0; $i < $this->size; $i++) {
$this->arr[$n] = $i;
if ($this->isSolution($n)) $this->run($n+1);
}
}
}

}

$myprogram = new Queens(8);
$myprogram->run();

? >
< /pre>
< /body>
< /html>

lunes, 16 de agosto de 2010

Windows Live Mail Offline Instaler

Microsoft no ha ido al CES a anunciar hardware sino principalmente software, y dos han sido las novedades principales que Steve Ballmer ha desvelado en su presentación. Una de ellas es que Windows Live Essentials ya está disponible en su versión final para descarga. Aqui les dejo la versión completa del instalador offline.

Los programas que puedes descargar incluyen:

Todo en un solo sitio
Obtén varias cuentas de correo electrónico en un solo programa: Hotmail, Gmail, Yahoo!*, etc. Y ahora Windows Live Mail también tiene un calendario. Mail combina la facilidad de uso de Outlook Express con la velocidad de Windows Live.


Exprésate en un blog
Writer te facilita el compartir fotos y vídeos en prácticamente en todos los servicios de blog: Windows Live, Wordpress, Blogger, Live Journal, TypePad, etc.


De tu cámara a Internet
Con Galería fotográfica, es muy sencillo llevar tus fotos y vídeos de la cámara al equipo. Busca tus fotos favoritas y compártelas con tus amigos y familiares. Puedes mejorar el aspecto de tus fotos e incluso crear asombrosas panorámicas.


¡Silencio, cámara y acción!
Crea películas y presentaciones a partir de tus fotos y vídeos, y compártelas con tus amigos y familiares.*



Ayuda a proteger a tus hijos en línea
Con Protección familiar, tú decides la experiencia de tus hijos en Internet. Limita las búsquedas, supervisa y bloquea o permite sitios web y decide con quién se pueden comunicar tus hijos en Windows Live Spaces, Messenger o Hotmail.*



Permanece conectado a Windows Live
Con Windows Live Toolbar, siempre obtendrás acceso rápido a Windows Live y Bing desde cualquier lugar de Internet en el que te encuentres.

enlace de descarga:

http://wl.dlservice.microsoft.com/download/D/8/A/D8A4AE91-3CA6-4F37-8988-1C0D91E4A7A7/es/wlsetup-all.exe

martes, 20 de julio de 2010

Combos Anidados con JQuery

Hay muchas maneras de crear combos anidados a través de javascript (ajax), aunque muchas veces necesitamos hacerlo de manera eficiente. Aqui propongo una manera donde los combos se cargan rápidamente a través de un servicio Json. El ejemplo tipico es elegir un Estado geográfico y que al seleccionarlo aparezcan las Ciudades asociadas a este Estado.

Lo primero que hacemos es crear el servicio del lado del servidor, donde el código quedaría mas o menos asi:

archivo: "./servicios/json.getCiudades.php"

// Conectarse a la base de datos

$option='[';
if($_GET['id'] && is_numeric($_GET['id'])) {
$result=mysql_query("select id_ciudad,nombre_ciudad from ciudades where id_estado='".$_GET['id']."'");
if($result && mysql_num_rows($result)>0) {
$error = false;
while ($row = mysql_fetch_array($result))
{
$option.= '{ "valor": "'.$row[0].'", "texto": "'.$row[1].'"}, ';
}
$option = substr( $option , 0, -2);
}
mysql_close();
}
$option=$option.']';
header('Content-Type: text/html; charset=ISO-8859-1');
echo($option);


Tambien podriamos utilziar una función de PHP llamada json_encode() para convertir un objeto directamente al formato JSON, sin embargo las primeras versiones de PHP no disponen nativamente de esta función. La razón de utilizar JSON es que puede ser convertido directamente en un objeto de javascript a través de una evaluación directa en el código.

Del lado del cliente, crearemos dos combos llamados ciudad y estado, de esta manera:


< html>...
< body>...
< form>...
< select name="estado" id="estado>< /select>
< select name="ciudad" id="estado>< /select>
< /form>
< /body>
< /html>


Luego incrustamos un bloque JQUERY para poder generar las ciudades dependientes del Estado seleccionado. Lo podriamos insertar directamente despues de la etiqueta body o donde sea mas cómodo. El código quedaría de esta manera:


< script type="text/javascript">
$(function() {
$("select#estado").change(function(){
$.ajax({
url: "./servicios/json.getCiudades.php?id=" + $(this).val(),
type: "GET",
dataType: "json",
error: function() {
alert("hay un error en el servicio de datos");
return false;
},
success: function(j) {
var opciones = '';
for (var i = 0; i < j.length; i++)
opciones += '';
$("#ciudad").html(opciones);
}
});
});

});
< /script>


Y listo!, con este código estamos invocando al script en PHP que trae las ciudades según el código de estado y las renderizamos directamente del lado del cliente dentro del combo ciudades. Observarán que la formación del combo de ciudades es casi instantáneo.

jueves, 1 de julio de 2010

Numeros primos en Java

Los números primos a lo largo del tiempo ha sido una de las ramas más complejas de la matemática. Poseen muchas aplicaciones prácticas debido a sus propiedades de números verdaderamente "únicos", asi como es el caso de la criptografía asimétrica por ejemplo. Pero uno de los problemas que ha tenido la humanidad desde sus inicios es determinar una función eficiente que genere estos números con un bajo costo computacional. Aqui escribo 5 versiones de funciones para cálcular números primos, en orden de rapidez y con un algoritmo que se encarga de medir el tiempo de ejecucion para numeros relativamente grandes.


public class Main {
private static long tiempomin=0;
private static int ganador = -1;

public static class Cronometro {
long t0;
public Cronometro() {
t0 = System.currentTimeMillis();
}
public long Parar() {
return (System.currentTimeMillis()-t0);
}
}

public static boolean esPrimo1(long n) {
if(n < 2) return false;
if(n < 4) return true;
for (long i = 2; i <= n; i++) if (n%i == 0) return false;
return true;
}

public static boolean esPrimo2(long n) {
if(n < 2) return false;
if(n < 4) return true;
for (long i = 2; i <= (long) Math.sqrt(n); i++) if (n%i == 0) return false;
return true;
}

public static boolean esPrimo3(long n) {
if(n< 2) return false;
if(n< 4) return true;
for (long i = 2; i*i <= n; i++) if (n%i == 0) return false;
return true;
}

public static boolean esPrimo4(long n) {
long nPrueba;
long nLimite;
if(n%2==0) return false;
nPrueba = 3;
nLimite = n;
while(nLimite > nPrueba) {
if(n%nPrueba==0) return false;
nLimite = n/nPrueba;
nPrueba += 2;
}
return true;
}

public static void Invocar(int nFuncion, long max) {
long enc=0;
if(ganador< 0) System.out.println("Se inicia el conteo para n=" + max + "...");
Cronometro reloj = new Cronometro();
long t2=0;
switch(nFuncion) {
case 1: for(long i=0; i <= max; i++) if(esPrimo1(i)) enc++; break;
case 2: for(long i=0; i <= max; i++) if(esPrimo2(i)) enc++; break;
case 3: for(long i=0; i <= max; i++) if(esPrimo3(i)) enc++; break;
case 4: for(long i=0; i <= max; i++) if(esPrimo4(i)) enc++; break;
}
t2 = reloj.Parar();
if(tiempomin==0 || t2< tiempomin) { tiempomin = t2; ganador = nFuncion; }
System.out.println("esPrimo" + nFuncion + "(1..n) " + t2 + " ms (" + enc + " numeros primos encontrados)");
}

public static void main(String[] args) {
int nfunciones = 4;
long maximo = 6000000;
for(int i=2; i <= nfunciones; i++) Invocar(i,maximo);
System.out.println("La funcion ganadora es esPrimo" + ganador + "() con " + tiempomin + " ms!!");
}

}

lunes, 21 de junio de 2010

Serie de Fibonacci Recursiva

Aqui también dejo la versión de la suceción en forma recursiva, que es mas efectiva pero consume mas memoria.


public class Main {
public static long Fibonacci(long n) {
return (n < 2 ) ? 1 : Fibonacci(n-1) + Fibonacci(n-2);
}
public static void main(String[] args) {
int n=7;
for(int i=0; i < n; i++) System.out.print(Fibonacci(i) + " ");
}
}

Serie de Fibonacci en Java

Aqui dejo un ejemplo que me han pedido en varias oportunidades. Un programa simple que imprime los N números de la sucesión de Fibonacci.


public class Main {
public static void Fibonacci(long n) {
long i,n0=1,n1=0,c;
for (i=1;i<=n;i++) {
c=n0+n1;
System.out.print(c + " ");
n0=n1;
n1=c;
}
}
public static void main(String[] args) {
Fibonacci(7);
}
}

lunes, 14 de junio de 2010

Firmar una aplicacion en Java

Cuando creamos una aplicación móvil (o aplet) para cualquier arquitectura, ésta posee permisologia restrictiva para evitar la propagación de códigos maliciosos (virus, aplicaciones no confiables).

En algunas ocasiones necesitamos que nuestra aplicación acceda a recursos que normalmente están restringidos debido al criterio antes mencionado, así que lo mas recomendable es que le hagamos una firma digital.

Existen dos ejecutables que se encuentran en el directorio bin de nuestra instalación de JAVA, llamados keytool, y jarsigner, los cuales podrán ser invocados desde sus respectivas rutas (en caso de windows c:\Program Files\JDK....\bin\).

Solo hacen falta dos pasos para firmar una aplicacion:

1. Generar el Certificado Digital; para eso ejecutamos:

>keytool -genkey -alias MIFIRMA -validity 365 -v

donde MIFIRMA puede ser el nombre de su grupo de claves, o incluso de su compañia. Durante la creación se le pedirá una contraseña para generar la firma, asi como sus datos personales y/o empresariales.

2. Aplicar el certificado digital a un ejecutable o clase:

>jarsigner MiPrograma.jar MIFIRMA -verbose

La aplicación de la firma solo pedirá la contraseña indicada en el paso 1.

Ya con esto tenemos a MiPrograma.jar listo para ser publicado en cualquier sitio o dispositivo!

domingo, 21 de marzo de 2010

Problemas al compartir carpetas con Windows 7

Si tenemos una red local con Windows 7, es probable que les haya pasado que comparten una carpeta y no pueden acceder a ella desde otros equipos de la red. La causa se debe al tipo de red que tienen configurada (Si es red publica, hogar o trabajo). El Windows medirá las restriciones deacuerdo a estas politicas a la hora de conectarse a una red cableada o WiFi disponible. La solución a esto es irnos al Panel de Control, buscar el Centro de redes y recursos compartidos, y cambiar la politica de la conexión a modo Hogar. Deberán hacer esto en todos los equipos que tengan el mismo problema.

martes, 16 de marzo de 2010

Encriptar y Desencriptar con PHP

Aqui les dejo dos funciones que pueden servirle para encriptar y desencriptar cadenas con una llave (parámetro opcional de las funciones).


function my_encrypt($string, $key = "48623468281AX4") {
for($i=0; $i < strlen($string); $i++) {
$char = substr($string, $i, 1);
$keychar = substr($key, ($i % strlen($key))-1, 1);
$char = chr(ord($char)+ord($keychar));
$result.=$char;
}
return base64_encode($result);
}

function my_decrypt($string, $key = "48623468281AX4" ) {
$string = base64_decode($string);
for($i=0; $i < strlen($string); $i++) {
$char = substr($string, $i, 1);
$keychar = substr($key, ($i % strlen($key))-1, 1);
$char = chr(ord($char)-ord($keychar));
$result.=$char;
} return $result;
}

$message = "HELLO MY WORLD!";
echo "Mensaje: ".$message." ";
$enc = my_encrypt($message);
echo "Mensaje encriptado: ".$enc." ";
echo "Mensaje desencriptado: ".my_decrypt($enc)." ";


en donde el navegador mostrará el siguiente resultado:
Mensaje: HELLO MY WORLD!
Mensaje encriptado: fHmEgoFTgY9YiYeDjZxV
Mensaje desencriptado: HELLO MY WORLD!

Evitar advertencia "La página ha caducado" en el navegador (IE - PHP)

Esto suele ser un problema comun a la hora de hacer POST en html y redireccionar a una nueva página a través de un enlace o algún cambio en el header. Hice una solución que me parece práctica cuando se pulsa el boton del navegador "Atrás" no responda con esa molesta advertencia.

La técnica consiste en tomar todas las variables $_POST, procesar las que se consideren adecuadas y redireccionar las mismas variables a través de un $_GET. Esto funciona bien cuando hacemos formularios de búsqueda que generan un listado, al que luego de hacer click en algún item se redirecciona a un nuevo enlace.

El código puede ser escrito como en el siguiente ejemplo:


function EstandarizarQueryString
($excepciones = "") {
if(!is_array($excepciones))
$excepciones = array($excepciones);
$est = array();
$querystring = "";
foreach($_GET as $key => $value) $est[$key] = $value;
foreach($_POST as $key => $value) $est[$key]=$value;
foreach($est as $key => $value) {
if(!in_array($key,$excepciones)) {
if(!empty($value) && $value!="0" && $value!="null") $querystring.="&".$key."=".urlencode($value);
}
}
return substr($querystring,1);
}


Una vez implementada esta funcion, se pueden reescribir los URL y manejar todos (o algunos) de los parametros REQUEST de la página de la siguiente manera:


if($_POST && !$_POST['botonexcepcion']) {
header("location: ".basename($_SERVER['PHP_SELF'])."?".EstandarizarQueryString());
exit();
}
// Aqui se manejan todos los parametros por $_GET ...

Con esto se evita una reconfirmación del POST cuando se ejecuta un evento history.back o cuando se pulsa el botón "Atrás" del navegador (Generalmente pasa en IE).

jueves, 11 de marzo de 2010

Instalar SSH en Windows XP/Vista/7 (Open SSH)

1. bajar el gygwin en http://www.cygwin.com/setup.exe

2. Instalar cygwin con OPENSSH a traves del setup de cygwin (buscar openssh en el cuadro de texto y marcar para instalación)

3. Como ADMINISTRADOR:
Crear una nueva variable de entorno: CYGWIN con valor ntsec tty

Agregar ;c:\cygwin\bin al Path

Abrir el cygwin con el acceso directo (Windows 7 usar run as administrator)

chmod +r /etc/passwd
chmod u+w /etc/passwd
chmod +r /etc/group
chmod u+w /etc/group
chmod 755 /var
chmod 664 /var/log/sshd.log


ssh-host-config, responder todo "yes" excepto cuando dice si desea usar un nombre diferente.

cyglsa-config


Reiniciar el PC. y LISTO!! ya tenemos ssh instalado (para configurar es /etc/sshd-config)

lunes, 8 de marzo de 2010

Clase de abstracción de datos para PHP-MySQL (Entidades/Business Objects)

Cuando trabajamos en PHP muchas veces tenemos tareas repetitivas de para crear inserts, updates y cualquier cantidad consultas a la base de datos. Si mejoramos el método podemos encapsular clases que abstraen las tablas en la base de datos y las llamamos "Entidades" o "Business Objects". Por ejemplo si tenemos una tabla "empleados" podriamos crear la entidad "class Empleado" el cual encapsula métodos Read y Save para guardar registros de empleados en particular, junto a todos los métodos que acompañan la clase.

Para automatizar el proceso de crear entidades en PHP, he creado una clase llamada genericDB, que se encarga de crear métodos set, get, getId, setId para cualquier tabla que tenga un id autonumérico (como primary key).

Podriamos ejemplificar su uso suponiendo que tenemos una tabla llamda "empleados" con un id_empleado autonumerico como clave primaria. El código para utilizar la tabla empleado (como entidad) quedaría asi:

<?php

//* Para leer un registro
$miempleado = new GenericDB("empleados");
$miempleado->setId(5);
if(
$miempleado->Read()) {
echo(
"El empleado existe<br>");
echo(
"El nombre del empleado es ".$miempleado->get("nombre"))
}


//* Para modificar un registro:
$miempleado = new GenericDB("empleados");
$miempleado->setId(5);
if(
$miempleado->Read()) {
echo(
"El empleado existe<br>");
echo(
"El nombre del empleado es ".$miempleado->get("nombre")."<br>")
echo(
"modificamos el nombre<br>");
$miempleado->set("nombre","Pedro");
if(
$miempleado->Save()) {
echo(
"El empleado se ha salvado como ".$miempleado->get("nombre"));
}
}

// Para crear un nuevo registro (nombre, apellido y cedula son campos en la tabla empleados):

$miempleado = new GenericDB("empleados");
$miempleado->set("nombre","Carlos Alberto");
$miempleado->set("apellido","Perez");
$miempleado->set("cedula","12346334");
if(
$miempleado->Save()) {
echo(
"Enhorabuena, se ha creado un nuevo registro <br>");
echo(
"El id del nuevo registro es ".$miempleado->getId()."<br>");
} else {
echo(
"error: ".$miempleado->_message);
}

?>
Puedes bajar el código de la clase genericDb haciendo click aqui.

Autologin en SSH con Putty

Una vez intenté establecer conexiones automáticas mediante SSH a través de putty, generando las llaves, colocandolas en el servidor y de repente: sorpresa!... un mensaje que dice "Server Refuse Our Key". Así que me puse a investigar un poco sobre el tema y encontré la solución. Aqui describo los pasos:

1. Descargar Putty y Puttygen (buscar en google)
2. Abrir Puttygen y generar la llave moviendo el mouse en el recuadro del programa.
3. Salvar la llave publica y la llave privada (.ppk)
4. Colocar la llave publica al servidor donde nos queremos conectar.
5. Cambiamos el formato de la llave pública a openssh de esta manera:

ssh-keygen -if llavepublica > llavepublica_openssh

6. Creamos la carpeta .ssh y le asignamos permisologia adecuada.
mkdir .ssh
chmod 700 .ssh

7. copiamos el contenido de la llave publica en el archivo authorized_keys dentro de la carpeta .ssh
cat llavepublica_openssh >> .ssh/authorized_keys

8. Nos vamos a la seccion "Connection > Data " y en el cuadro autologin colocamos el usuario.

9. Abrimos el putty y nos vamos a la configuracion "Connection > SSH > Auth", alli le damos click al botón browse y seleccionamos la llave privada.

10. Salvamos la sesión con el nombre del servidor y LISTO!


Si queremos que inicie sesión automaticamente podemos crear un acceso directo en windows con el siguiente comando. (incluyendo las comillas)

putty.exe -load "misesion"

domingo, 14 de junio de 2009

Añadir subtitulos en películas Divx, MKV para verlas en el PS3.

Siempre nos preguntamos como poner subtitulos srt en peliculas DivX para verlos en el PS3 a través de un servidor multimedia o en un DVD de mesa con soporte para este tipo de peliculas. El problema radica en que los media servers no multiplexan el tiempo real los subtítulos SRT a menos que hagan Transcoding de los mismos (lo que consume mucho tiempo de procesamiento). Aqui les dejo una solución para pegar subtítulos en sus peliculas de la manera rápida (les tomará como 3 minutos por pelicula) y éstos serán seleccionables a través de las opciones del DVD, PS3 o cualquier hardware con logo de DivX.

1. Pegar subtitulos seleccionables a divX, Xvid

Para esto descargamos el AviAddXSub. Este programa nos permite unir subtitulos .srt a películas de formato DIVX o XVID. Una vez que lo tengamos ponemos la pelicula (peliculaejemplo.avi) junto a los subtitulos con exactamente el mismo nombre (peliculaejemplo.srt). Vamos a la pestaña de Configuracion, seleccionamos letra ARIAL con tamaño 22.

Volvemos a la pestaña "Create DivX with Subtitles" y abrimos la pelicula (File name), seleccionamos una carpeta de salida (output folder), hacemos click en el botón "Create Subtitled DivX File" y listo!.
El par de minutos tendremos un archivo .DIVX con subtitulos seleccionables. Este archivo es compatible con cualquier reproductor de software o hardware con el logo DivX.

2. Pegar subtitulos permanentes a peliculas matroska (.MKV) y hacerlas compatibles con PS3. (x264, bluray)

Primero que todo descargamos e instalamos el programa MKV2VOB.

Como en el caso anterior, juntamos en una misma carpeta la pelicula (peliculaejemplo.mkv) con su respectivo archivo de subtítulos (peliculaejemplo.srt) Abrimos el programa y nos vamos a la pestaña de configuración donde activaremos las siguientes opciones:

Always Encode Subtitles.
Load Subtitles from File.
Large Subtitles.

Volvemos a la pestaña (Add File) y seleccionamos nuestra pelicula con el boton de "Source File", también podemos opcionalmente especificar la carpeta de destino ("Destination Directory"). Una vez que la agregamos se empezará a realizar el transcoding de la película. El proceso tarda un poco mas pero tendremos una pelicula X264 totalmente compatible con consolas PS3 y subtítulos añadidos con la calidad original.

viernes, 24 de octubre de 2008

Detección de rostros en PHP con Facedetect y OpenCV

Con el auge de las tecnologías de detección de rostros de las camaras fotográficas, decidi investigar un poco al respecto y me topé con la biblioteca OpenCV (Open Computer Visión Library). Esta biblioteca permite hacer análisis y transformaciónes a videos e imágenes así como detección de formas usando inteligencia artificial. Logré compilar una extensión para reconocimiento de personas en PHP llamada Facedetect el cual contiene dos funciones disponibles para enlazarlas con PHP5:

face_count(): Devuelve el numero de rostros encontradas en la imagen.
face_detect(): Devuelve un array con las coordenadas de los rostros disponibles.


Imágen sin procesar

Después de procesar
Numero de personas en la foto: 10


Monté la plataforma utilizando un servidor Ubuntu Hardy Heron con Apache2 y php5.

script:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;" />
<title>Detector de Rostros</title>
</head>
<body>
<h1>Open Computer Vision - Contador de personas</h1>
<form action="<?=$_SERVER['PHP_SELF']?>" method="post" ENCTYPE="multipart/form-data">
Archivo<input type="file" name="file">
<input type="submit" value="subir">
<input type="hidden" name="uploadflag" value="1">
</form>
<br>
<?php
if($_POST['uploadflag']==1){
if(
$_FILES['file']['tmp_name'] != ""){
$imagenfinal = "imagenes/foto".date("Ymdhis").".jpg";
$cascade = "/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt.xml";
move_uploaded_file($_FILES['file']['tmp_name'],$imagenfinal);
$format = strtolower(substr(strrchr($imagenfinal,"."),1));
switch(
$format)
{
case
'gif' :
$type ="gif";
$img = imagecreatefromgif($imagenfinal);
break;
case
'png' :
$type ="png";
$img = imagecreatefrompng($imagenfinal);
break;
case
'jpg' :
$type ="jpg";
$img = imagecreatefromjpeg($imagenfinal);
break;
case
'jpeg' :
$type ="jpg";
$img = imagecreatefromjpeg($imagenfinal);
break;
default :
die (
"<br>ERROR: tipo de archivo desconocido: $imagenfinal");
break;
}
$total = face_count($imagenfinal, $cascade);
$faces = face_detect($imagenfinal ,$cascade);

echo
"Numero de personas en la foto: ".$total;
$color = imagecolorallocate($img, 132, 135, 28);
for(
$i=0; $i<$total; $i++) {
$face = $faces[$i];
$x1 = $face['x'];
$y1 = $face['y'];
$x2 = $x1+$face['w'];
$y2 = $y1+$face['h'];
imagerectangle($img, $x1, $y1, $x2, $y2, $color);
echo(
"<br>persona ".($i+1)." : ($x1,$y1) - ($x2,$y2)");
}

if(
$total>0) {
$dest = "imagenes/nuevafoto_".date("Ymdhis").".jpg";
if(
$type=="gif") imagegif($img, $dest);
if(
$type=="jpg") imagejpeg($img, $dest);
if(
$type=="png") imagepng($img, $dest);
if(
$type=="bmp") imagewbmp($img, $dest);
echo
'<br><img src="'.$dest.'"><br>';
}
echo
'<a href="'.$_SERVER['PHP_SELF'].'"></a>';
} else{ echo
"<br>Debe subir una imagen<br>"; }
echo
'<br><br><a href="'.$_SERVER['PHP_SELF'].'">Recargar</a>';
}
?>
</body>
</html>

domingo, 15 de junio de 2008

Cheat para Word Challenge en Facebook

Aqui les dejo un bot generado en php para el famoso juego de facebook "Word Challenge". El programa genera un script en Windows Scripting Tool (vbs) que juega por ti.

"miscript.vbs"; // archivo de salida
$retardoinicial = 1800; // tiempo inicial de espera (en milisegundos) para hacer foco en la ventana del juego
$retardotecla = 0; //milisegundos
$diccionario = "xxxxxxxxxx"; // aqui va la pagina donde se obtienen las palabras
// esta pagina del diccionario deberan solicitármela por correo

function OutputFile($file) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; '
.'filename="'.$file.'"');
echo
file_get_contents($file);
}

function
SaveContent($content, $file)
{
if (
file_exists($file)) @unlink($file);
if (!
$handle = fopen($file, 'w')) die("Error in $file");
fwrite($handle,$content);
fclose($handle);
}

function ParseContent($string){
preg_match_all("(word=[a-zA-Z]{3}[a-zA-Z]*)", $string, $matches);
$rightwords = array();
foreach(
$matches[0] as $valor) $rightwords[] = substr($valor,5);
$rightwords = array_reverse(array_unique($rightwords));
return $rightwords;
}

if (
$_POST) {
srand((float)microtime() * 1000000);
$url = "http://$diccionario?letters=".substr($_POST['letras'],0,6).
"&dict=sowpods&Cheat=Cheat"
;
$input = @file_get_contents($url) or die('Cant access $url');
$rightwords = ParseContent($input);
$content = 'Dim Wsh'."\r\n";
$content.= 'Set Wsh = Wscript.CreateObject("Wscript.Shell")'."\r\n";
$content.= 'Wsh.AppActivate "Exploring"'."\r\n";
$content.= 'Wscript.sleep('.$retardoinicial.')'."\r\n";
foreach(
$rightwords as $word) {
$scriptline = '';
for(
$i=0; $i<strlen($word); $i++)
{
$letter = substr($word,$i,1);
$scriptline.='Wsh.SendKeys "{'.$letter.'}"'."\r\n";
}
$scriptline.='Wsh.SendKeys "{ENTER}"'."\r\n";
$content.=$scriptline;
}
SaveContent($content,"./".$scriptname);
OutputFile($scriptname);
exit();
}

?>
<html>
<body>
<h3>Generador de palabras para Word Challenge</h3>
<form name="miforma" method="post" action="<?=$_SERVER['PHP_SELF']?>">
<label>letras</label>
<input type="text" name="letras" />
<input type="submit" name="submit" value="submit">
<br>
</form>
</body>
</html>


miércoles, 5 de septiembre de 2007

Progress Bar en ASP.NET 2.0

Buscando Barras de progreso en Internet me topé con uno de esos controles dll que funcionaba a la perfección, pero pedia un donativo (practicamente shareware con alerta y todo) para el control. Asi que decidí ver como funcionaba y publicar mi propio control para uso público. Aqui les dejo el código fuente (ProgressBar.ascx y ProgressBar.ascx.vb)

ProgressBar.ascx

<% Control Language="VB" AutoEventWireup="false" CodeFile="ProgressBar.ascx.vb" Inherits="componentes_ProgressBar" %>
<table cellspacing="1" cellpadding="0" border="0" style="border-color:Blue;border-width:1px;border-style:solid;height:20px;width:200px;"><tr><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#0000ff;"></td><td style="background-color:#d3d3d3;"></td><td style="background-color:#d3d3d3;"></td><td style="background-color:#d3d3d3;"></td><td style="background-color:#d3d3d3;"></td><td style="background-color:#d3d3d3;"></td><td style="background-color:#d3d3d3;"></td><td style="background-color:#d3d3d3;"></td></tr></table>



ProgressBar.ascx.vb

Partial Class componentes_ProgressBar : Inherits System.Web.UI.UserControl
Private _forecolor As Color = Color.Blue
Private _backgroundcolor As Color = Color.LightGray
Private _width As String = "200px"
Private _valor As Integer = 0
Private _maxCuadros As Integer = 30

Public Property ForeColor() As Color
Get
Return
_forecolor
End Get
Set
(ByVal value As Color)
Me._forecolor = value
End Set
End Property

Public Property
BackgroundColor() As Color
Get
Return
_backgroundcolor
End Get
Set
(ByVal value As Color)
Me._backgroundcolor = value
End Set
End Property

Public Property
MaxCuadros() As Integer
Get
Return
_maxCuadros
End Get
Set
(ByVal value As Integer)
_maxCuadros
= value
End Set
End Property

Public Property
Width() As String
Get
Return
_width
End Get
Set
(ByVal value As String)
_width
= value
End Set
End Property

Function
ToHexColor(ByVal c As Color) As String
Return
"#" + c.ToArgb().ToString("x").Substring(2)
End Function

Public Property
Value() As Integer
Get
Return
_valor
End Get
Set
(ByVal value As Integer)
If value > 100 Then value = 100
If value < 0 Then value = 0
_valor = value
End Set
End Property

Protected Overrides Sub
Render(ByVal Output As HtmlTextWriter)
Dim i As Integer
Dim
vreal As Integer = CInt((_valor * _maxCuadros / 100) + 0.5)
Dim fcolor As String = ToHexColor(_forecolor)
Dim bcolor As String = ToHexColor(_backgroundcolor)
Dim c As String
Output.Write("<table cellspacing=""1"" cellpadding=""0"" border=""0"" style=""border-color:Blue;border-width:1px;border-style:solid;height:20px;width:" & _width & ";""><tr>")
For i = 1 To _maxCuadros
c
= IIf(vreal < i, bcolor, fcolor)
Output.Write(
"<td style=""background-color:" & c & ";""></td>")
Next
Output.Write("</tr></table>")
End Sub
End Class