<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Tema: Laboratorio | Sentido Web</title>
<link>http://sentidoweb.com/</link>
<description>Actualidad y maneras de hacer en Internet (programación, diseño, tendencias, negocios...)</description>
<language>es</language>
<copyright>Copyright 2008</copyright>
<lastBuildDate>Mon, 22 Oct 2007 10:00:00 +0000</lastBuildDate>
<generator>http://www.movabletype.org/?v=3.2</generator>
<docs>http://blogs.law.harvard.edu/tech/rss</docs> 

<item>
<title>Laboratorio: modificar urls para CodeIgniter con mod_rewrite</title>
<description><![CDATA[<p><img alt="codeigniter.png" src="http://sentidoweb.com/img/2007/04/codeigniter.png" class="right" height="43" width="163" /><a href="http://CodeIgniter.com">CodeIgniter</a> es uno de los frameworks PHP de los que más se habla últimamente, a mí personalmente me gusta bastante, aunque hay algunas cosas que no son como me gustaría.</p>
<p>Una de ellas es la estructura de las URLs, las URLs siguen el formato:</p> <pre>http://dominio/controlador/metodo/parametro/parametro/...</pre>
<p>El controlador es la clase que se encarga de hacer las operaciones y el método es el método de la clase que realiza una función concreta.</p> 
<p>Por ejemplo si tenemos una tienda online, podemos tener un controlador para productos y un método que sea editar, con el que se podrá modificar las características del producto. La url sería la siguiente:</p>
<pre>http://tienda.com/articulo/editar/cafetera</pre>
<p>Con esta url podemos editar un artículo llamado cafetera y además es bastante entendible. Pero, ¿qué pasa si queremos mostrar el artículo cafetera?, pues que la url debería ser la siguiente:</p>
<pre>http://tienda.com/articulo/ver/cafetera</pre>
<p>Pero lo de <em>ver</em> no queda demasiado bien y queda mejor si es  directamente:</p>
<pre>http://tienda.com/articulo/cafetera</pre>
<p>El problema nos lo encontramos cuando queremos usar una URL que no indique el método y si un parámetro. Si no usamos ni método ni parámetro, CodeIgniter toma por defecto el método <em>index</em>, pero si no usamos método y si parámetro, CodeIgniter no es capaz de saber que lo que mandamos es un parámetro, por lo que hay que usar el Apache para que siga la estructura de CodeIgniter.</p>
<pre>RewriteEngine on

<p>RewriteCond %{REQUEST_FILENAME} !-f<br />
RewriteCond %{REQUEST_FILENAME} !-d<br />
RewriteRule ^articulo/(.+)$ index.php/articulo/ver/$1 [L]</p>

<p>RewriteCond $1 !^(index\.php|favicon\.ico)<br />
RewriteRule ^(.*)$ index.php/$1 [L]</pre><br />
<p>Hay que tener en cuenta que este ejemplo es válido únicamente si solo se va a usar siempre dos segmentos en la url, uno para el controlador y otro para el parámetro.</p><br />
<p>La segunda parte y el uso de <em>index.php</em> es debido a que CodeIgniter usa este script para gestionar toda la aplicación, y para que no aparezca, debemos redireccionarlo todo a index.php, menos los ficheros que existen como el favicon, el robot.txt, etc.</p></p>]]></description>
<link>http://sentidoweb.com/2007/10/22/laboratorio-modificar-urls-para-codeigniter-con-mod_rewrite.php</link>
<guid>http://sentidoweb.com/2007/10/22/laboratorio-modificar-urls-para-codeigniter-con-mod_rewrite.php</guid>
<category>Laboratorio</category>
<pubDate>Mon, 22 Oct 2007 10:00:00 +0000</pubDate>
</item>
<item>
<title>Laboratorio: indicador de calidad de password</title>
<description><![CDATA[<p>Cuando un usuario se registra en una aplicación web, suele haber casos que cuando se está introduciendo la contraseña, el sistema indica si la password cumple unos mínimos de seguridad o no. En algunas ocasiones suele darnos solo un aviso informativo, pero en otros o cumple todos los requisitos o no admite la contraseña.</p>
<p>En este caso vamos a crear un script que modificará los <em>input password</em> para añadirle una funcionalidad que indique la calidad de la contraseña que se va introduciendo.</p>
<p><img alt="password.png" src="http://sentidoweb.com/img/2007/07/password.png" width="308" height="28" class="center" /></p>
<p>El script añadirá el evento de control de tecla pulsada (onkeyup) y <strong>realizará una serie de comprobaciones para conocer la calidad de la contraseña</strong>, una contraseña válida será la que cumpla todas las condiciones. Las condiciones que incluimos en el ejemplo son las típicas que suelen pedir: <strong>que existan mayúsculas y minúsculas, algún número, caracteres especiales y una longitud mayor de 6</strong>.</p>
<p>Para chequear cada condición usaremos expresiones regulares y llevaremos un contador para saber cuántas se cumplen. Cuando hayamos finalizado las comprobaciones calcularemos el porcentaje de calidad y lo indicaremos con una barra de progreso. Esta barra se crecerá según aumente la calidad de la contraseña e irá cambiando de color desde un color rojo para la poca calidad hasta un color verde que indique mucha calidad.</p>
<p>El efecto de barra de progreso lo vamos a realizar de la siguiente manera, tenemos <strong>una imagen que va a funcionar como máscara</strong>, tendrá dos partes, de igual tamaño y cada parte del mismo tamaño que el ancho del input. Cada mitad tendrá una funcionalidad, la mitad de la derecha servirá para ocultar y la de la izquierda para mostrar, según queramos que se vaya mostrando la barra de progreso, iremos desplazando el fondo hacia la derecha para que se vaya viendo la barra de progreso. Inicialmente la línea discontinua de la imagen que mostramos como ejemplo estará en el lado izquierdo del input.</p>
<p><img alt="mascara.gif" src="http://sentidoweb.com/img/2007/07/mascara.gif" width="200" height="100" class="center"/></p>
<p>El código sería el siguiente:</p>
<pre><code>var __PASSWORD__ = {
  colorKO: [255, 0, 0], // Color de contraseña no válida
  color50: [127, 127, 0], // Color para el 50% para que no haya un cambio tan brusco
  colorOK: [0, 255, 0], // Color de contraseña válida
  width: 150,
  
  // Añade el evento onkeyup a la caja input:password y le añade el evento checkpassword
  init: function() {
    var inputs = document.getElementsByTagName("INPUT");
    for (var i=0; i&lt;inputs.length; i++) {
      if (inputs[i].type == 'password') {
        inputs[i].addEventListener("keyup", __PASSWORD__.checkPassword, false);
        inputs[i].className = 'password';
      }
    }
  },
  
  // Obtiene el color porcentual entre un color inicial y otro final, teniendo en cuenta un color intermedio
  getColor: function(porc) {
    var color = new Array();
    var color1 = porc &lt; 50? __PASSWORD__.colorKO:__PASSWORD__.color50;
    var color2 = porc &lt; 50? __PASSWORD__.color50:__PASSWORD__.colorOK;
    color[0] = parseInt(color1[0]-((color1[0] -color2[0])*porc/100));
    color[1] = parseInt(color1[1]-((color1[1] -color2[1])*porc/100));
    color[2] = parseInt(color1[2]-((color1[2] -color2[2])*porc/100));
    
    return color;
  },
    
  // Comprueba la calidad de la contraseña
  checkPassword: function() {
    var valor = this.value;
    
    var cont = 0;
    
    var comprobaciones = new Array(
      /[A-Z]/, // mayúsculas
      /[a-z]/, // minúsculas
      /\d/,    // números 
      /.{6}/,  // más de 6 caracteres
      /(\s|\\|\/|!|"|·|\$|%|&amp;|\(|\)|=|\?|¿|\||@|#|¬|€|\^|`|\[|\]|\+|\*|¨|´|\{|\}|\-|_|\.|:|,|;|&gt;|&lt;)/ // Caracteres especiales
      );
    // Miro todas las condiciones
    for (var i=0; i&lt;comprobaciones.length; i++) {
      if (valor.match(comprobaciones[i])) {
        cont++;
      }
    }
    
    // Valores posibles en caso de 5 condiciones: 0, 20, 40, 60, 80, 100, pero el valor mínimo tiene que ser 20 para que coincida con el colorKO, ya que si no, al pulsar una tecla ya cambia de color a un paso colorKO+1
    var porc = parseInt((cont*100/(comprobaciones.length-1))-(1/(comprobaciones.length-1)*100));
    // Desplazo el background tratándolo como una máscara
    this.style.backgroundPosition = (parseInt(cont*__PASSWORD__.width/comprobaciones.length)-(__PASSWORD__.width))+"px 0px";
    // Cambio el color de fondo
    var color = __PASSWORD__.getColor(porc);
    if (porc &lt; 0) {
      this.style.backgroundColor = '';
    } else {
      this.style.backgroundColor = 'rgb('+color[0]+', '+color[1]+', '+color[2]+')';
    } 
  }
  
  
};

window.addEventListener("load", function() {__PASSWORD__.init();}, false);</code></pre>
<p><a href="http://sentidoweb.com/lab/codes/js/password/password.html">Demo</a></p>]]></description>
<link>http://sentidoweb.com/2007/07/16/laboratorio-indicador-de-calidad-de-password.php</link>
<guid>http://sentidoweb.com/2007/07/16/laboratorio-indicador-de-calidad-de-password.php</guid>
<category>Laboratorio</category>
<pubDate>Mon, 16 Jul 2007 15:30:00 +0000</pubDate>
</item>
<item>
<title>Laboratorio: triggers y procedimientos almacenados en MySQL</title>
<description><![CDATA[<p>Unas opciones no muy usadas cuando se trabaja con MySQL son los triggers y los procedimientos almacenados (<em>stored procedures</em>), supongo que a que es algo nuevo en MySQL y la gente no está acostumbrada a estas dos funcionalidades.</p>
<p>Vamos a mostrar un ejemplo de uso de triggers y procedimientos almacenados paso a paso. Quizás el ejemplo no sea el más correcto, o simplemente sea poco útil, pero lo importante es el uso en sí, no el ejemplo.</p>
<p>Nuestro sistema tendrá dos tablas, una con ventas y la otra con comisiones. En la primera se almacenarán cada venta que se realiza en un comercio, y en la segunda las comisiones que le corresponden a cada vendedor en el momento.</p>
<pre>CREATE TABLE `ventas` (
  `vendedor` int(11),
  `producto` int(11),
  `importe` float
)

CREATE TABLE `comisiones` (
  `vendedor` int(11),
  `comision` float
)</pre>
<p>Las comisiones se calcula de una forma especial, le corresponde un porcentaje de las ventas según el tipo de producto, y es importante para los vendedores el que se sepa en cada momento qué comisiones lleva ganadas (esto es una justificación para no usar un <em>cron</em> o algo parecido).</p>
<p>Para calcular qué comisión le corresponde a un vendedor, calcularemos los porcentajes para cada tipo de producto vendido y luego lo añadiremos/actualizaremos en la tabla de comisiones. Todo se realizará en un procedimiento almacenado.</p>
<pre>DELIMITER $$

DROP PROCEDURE IF EXISTS `test`.`sp_comisiones`$$

CREATE PROCEDURE `test`.`sp_comisiones` (IN mivendedor INT)
BEGIN
	DECLARE micomision INT DEFAULT 0;
	DECLARE suma INT;
	DECLARE existe BOOL;

	select IFNULL(sum(importe),0) into suma from ventas where producto = 1 and vendedor = mivendedor;
	SET micomision = micomision + (suma * 0.15);

	select IFNULL(sum(importe),0) into suma from ventas where producto = 2 and vendedor = mivendedor;
	SET micomision = micomision + (suma * 0.1);

	select IFNULL(sum(importe),0) into suma from ventas where producto = 3 and vendedor = mivendedor;
	SET micomision = micomision + (suma * 0.2);

	select count(1)>0 into existe from comisiones where vendedor = mivendedor;
	if existe then
		UPDATE comisiones set comision = comision+micomision where vendedor = mivendedor;
	else
		insert into comisiones (vendedor, comision) values (mivendedor, micomision);
	end if;
END$$

DELIMITER ;</pre>
<p>Ahora, para actualizar los datos de las comisiones usaremos un trigguer, así cuando se haga una venta (insert en la tabla ventas), se llamará al procedimiento almacenado (<em>sp_comisiones</em>), que recalculará la comisión para ese vendedor.</p>
<pre>DELIMITER $$

DROP TRIGGER `test`.`tr_ventas_insert`$$

CREATE TRIGGER `test`.`tr_ventas_insert` AFTER INSERT on `test`.`ventas`
FOR EACH ROW BEGIN
	call sp_comisiones(new.vendedor);
END$$

DELIMITER ;</pre>
]]></description>
<link>http://sentidoweb.com/2007/06/20/laboratorio-triggers-y-procedimientos-almacenados-en-mysql.php</link>
<guid>http://sentidoweb.com/2007/06/20/laboratorio-triggers-y-procedimientos-almacenados-en-mysql.php</guid>
<category>Laboratorio</category>
<pubDate>Wed, 20 Jun 2007 16:00:00 +0000</pubDate>
</item>
<item>
<title>Laboratorio: navegación por teclado</title>
<description><![CDATA[<p>Ya en varios sitios se ha mostrado como realizar la navegación mediante el teclado, esto significa usar las teclas para acceder a contenidos o para realizar acciones.</p>
<p>Nosotros queremos darle un toque de automatización, que <strong>tan solo sea necesario añadir un enlace interno en el documento</strong> para acceder a la zona del documento.</p>
<p>Crearemos un script que obtenga todos los enlaces internos del documento y si tienen el atributo <em>rel</em> y su valor tiene el formato "<em>formato:[letra]</em>", se considerará un objetivo para la navegación por teclado. La letra será la que se usará para acceder y pueden repetirse, por lo que si se repiten las teclas para el shortcut, se irán alternando uno a uno los distintos enlaces.</p>
<p>El script consta de dos partes, uno que se ejecuta después de la carga del documento y que recupera todos los enlaces internos y los agrupa por shortcut, y otro que captura el evento <em>onkeydown</em> del <em>document</em> para acceder a la zona del documento (que será modificando el <em>location</em>).</p>
<pre><code>var __SHORTCUTS__ = {
  // Guarda los shortcuts con los enlaces
  shortcuts: new Array(),
  // Índice del enlace actual organizado por shortcuts 
  idx_shortcuts: new Array(),
  
  // Obtiene todos los enlaces internos y los que corresponden a un shortcut los almacena
  leer_shortcuts: function(e) {
    var objs = document.getElementsByTagName("A");
    for (var i=0; i&lt;objs.length; i++) {
      var rel = objs[i].rel;
      if (rel &amp;&amp; rel.match(/shortcut:[a-z]/i) &amp;&amp; objs[i].name) {
        var tipo = (rel.substring(rel.indexOf(":")+1)+"").toUpperCase().substring(0, 1);
        if (!__SHORTCUTS__.shortcuts[tipo]) {
          __SHORTCUTS__.shortcuts[tipo] = new Array();
          __SHORTCUTS__.idx_shortcuts[tipo] = 0;
        }
        __SHORTCUTS__.shortcuts[tipo][__SHORTCUTS__.shortcuts[tipo].length] = objs[i];
      }
    }
    // Capturo el evento de pulsado de teclado en el document
    document.onkeydown = __SHORTCUTS__.controlar_keypress; 
  },
  
  // Captura una tecla pulsada y accede al shortcut que tenga asociado
  controlar_keypress: function(e) {
    try {
      if (!e) e = event;
      var key = e.keyCode;
      // Obtengo el caracter correspondiente, quizás esto falle, no he hecho pruebas suficientes
      var c = String.fromCharCode(key);
      // Si es una letra
      if (c.match(/^[a-z]$/i)) {
        // Obtengo el enlace para el shortcut
        var obj = __SHORTCUTS__.shortcuts[c.toUpperCase()][__SHORTCUTS__.idx_shortcuts[c.toUpperCase()]];
        // Acceso a esa parte del documento
        document.location = "#"+obj.name;
        // Incremento el indice para que acceda al siguiente
        __SHORTCUTS__.idx_shortcuts[c.toUpperCase()] = (__SHORTCUTS__.idx_shortcuts[c.toUpperCase()]+1)%__SHORTCUTS__.shortcuts[c.toUpperCase()].length;
      }
    } catch (e) {} // En IE me da un error que no llego a comprender, ¡cómo no!
  }
}

// Cargo el proceso en el onload
if (window.addEventListener) {
  window.addEventListener("load", function(event) {__SHORTCUTS__.leer_shortcuts(event);}, false);
} else if (window.attachEvent) {
  window.attachEvent("onload", function(event) {__SHORTCUTS__.leer_shortcuts(event);});
} else {
  document.onload = function(event) {__SHORTCUTS__.leer_shortcuts(event);}
}</code></pre>
<p><strong>Para indicar qué elementos tienen shortcut, modificamos los estilos para mostrar subrayada la primera letra del elemento</strong>, para lo cual el elemento debe tener el atributo <em>title</em> con valor "<em>SHORTCUT: [letra]</em>". <strong>Mejorando así la accesibilidad ya que no añadimos ninguna etiqueta para la primera letra</strong>. Por supuesto en IE6 no funciona y con la etiqueta <em>legend</em> tampoco me funciona.</p>
<pre>*[title~="SHORTCUT:"]:first-letter {
  padding-bottom: 2px;
  border-bottom: 1px dotted #000000;  
}</pre>
<p>Para este ejemplo tenemos dos tipos de shortcuts: 'P' para ciertos párrafos y 'C' para acceder al formulario de comentarios.</p>
<p><a href="http://sentidoweb.com/lab/js/navegacion-teclado/shortcuts.html">Ejemplo</a></p>
]]></description>
<link>http://sentidoweb.com/2007/03/30/laboratorio-navegacion-por-teclado.php</link>
<guid>http://sentidoweb.com/2007/03/30/laboratorio-navegacion-por-teclado.php</guid>
<category>Laboratorio</category>
<pubDate>Fri, 30 Mar 2007 16:00:00 +0000</pubDate>
</item>
<item>
<title>Laboratorio: no permitir que accedan directamente a las urls empleadas en Ajax</title>
<description><![CDATA[<p>Cuando usamos Ajax en nuestras aplicaciones solemos usar un script específico que devuelva los datos preparados para que el cliente los interprete y los muestre.</p>
<p>En algunas ocasiones no suele ser de nuestro agrado que usuarios accedan directamente a las URLs empleadas para Ajax.</p>
<p>Para evitar esta situación se puede usar un script sencillo. Se tomará en cuenta el valor de la variable de entorno <strong><em>HTTP_REFERER</em>, la cual nos devuelve desde qué página se accede a nuestro script</strong>.</p>
<pre><code>$ref = getenv('HTTP_REFERER');</code></pre>
<p>Si el contenido de esta variable es <em>false</em> entonces es que se ha accedido directamente desde el navegador, si no, es que se accede desde una página, también habrá que comprobar que la página es la que nosotros queremos.</p>
<pre><code>&lt;?php
  $ref = getenv('HTTP_REFERER');
 
  if (!$ref || $ref != '<em>[nuestra url]</em>') {
?&gt;
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE html 
     PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "xhtml1-transitional.dtd"&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Sentido Web - Redireccionamiento Ajax&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt; 
&lt;h1&gt;Sentido Web - Redireccionamiento Ajax&lt;/h1&gt;
&lt;p&gt;&lt;?php echo "Hola, son las ".date("H:i:s"); ?&gt;&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
&lt;?php
  } else {    
    echo "Hola, son las ".date("H:i:s");
	}
?&gt;</code></pre>
<p>En este caso mostraremos una página cuando no haya <em>referer</em> o no sea el que nosotros esperamos. También se podría hacer para que el <em>referer</em> estuviera en el servidor, pero eso lo dejo para que lo haga el que lo necesita.</p>
<p>Una funcionalidad parecida se podría usar por temas de accesibilidad, dependiendo de un parámetro que indique si se trata de una llamada Ajax o no, se devuelva la información para el script del cliente o se muestre la información en una página.</p>
<pre><code>&lt;a href="url-ajax.php" onclick="ajax('url-ajax.php?ajax=1')"&gt;Enlace&lt;/a&gt;</code></pre>
<p>En el caso de haber problemas de accesibilidad, el enlace accede a la página con la información. En caso contrario, ejecuta la funciona <em>ajax()</em>, indicándole que se trata de una llamada Ajax para que nos devuelva la información formateada, ya sea en XML o JSON o como queramos.</p>]]></description>
<link>http://sentidoweb.com/2007/03/28/laboratorio-no-permitir-que-accedan-directamente-a-las-urls-empleadas-en-ajax.php</link>
<guid>http://sentidoweb.com/2007/03/28/laboratorio-no-permitir-que-accedan-directamente-a-las-urls-empleadas-en-ajax.php</guid>
<category>Laboratorio</category>
<pubDate>Wed, 28 Mar 2007 16:20:00 +0000</pubDate>
</item>
<item>
<title>Laboratorio: mostrar que thumbnails has visitado</title>
<description><![CDATA[<p>Una de las cosas que más me ha gustado de <a href="http://www.designmeltdown.com/">Design Meltdown</a> es que te indica que thumbnails has visitado.</p>
<p>Se trata de lo siguiente, tienes un enlace que contiene una imagen en miniatura que accede a la imagen con el tamaño original (thumbnail). Cuando ya has visitado la imagen el thumbnail cambia y te dice que ya lo has visitado.</p>
<p><img alt="imagenes-visitadas.png" src="http://sentidoweb.com/img/2007/03/imagenes-visitadas.png" width="167" height="96" class="center"/></p>
<p>Para hacerlo es sencillo, creas una capa con un tamaño específico (el del thumbnail), le indicas mediante estilo dentro de la propia etiqueta HTML la imagen de fondo, que será el thumbnail en sí.</p>
<p>Dentro de la capa incluyes un enlace que se mostrará como un bloque (<em>display: block</em>) y que no mostrará nada, bueno, en este caso si mostrará un texto que referencie a lo que enlaza, pero mediante estilos el texto no se verá, esto lo hacemos así para que los dispositivos que no admitan estilos puedan ver algo.</p>
<p>Modificaremos el estilo de enlace visitado para que muestre como fondo una imagen parcialmente transparente y que muestre el texto "visitada".</p>
<pre><code>.contenedor-imagen {
  width: 150px;
  height: 85px;
}

.contenedor-imagen a {
  text-indent: -100000px;
  display: block;
  width: 150px;
  height: 85px;
  border: 2px solid #FFFFFF;
}

.contenedor-imagen a:hover {
  border: 2px solid #FF0000;
}

.contenedor-imagen a:visited {
  background: url(visitada.gif);
}</code></pre>
<pre><code>&lt;div class="contenedor-imagen" style="background: url(miniatura.png)"&gt;
  &lt;a href="http://sentidoweb.com?ejemplo1"&gt;Sentido Web&lt;/a&gt;
&lt;/div&gt;</code></pre>
<p><a href="http://sentidoweb.com/lab/css/imagenes-visitadas/imagenes-visitadas.html">Ejemplo</a></p>]]></description>
<link>http://sentidoweb.com/2007/03/23/laboratorio-mostrar-que-thumbnails-has-visitado.php</link>
<guid>http://sentidoweb.com/2007/03/23/laboratorio-mostrar-que-thumbnails-has-visitado.php</guid>
<category>Laboratorio</category>
<pubDate>Fri, 23 Mar 2007 16:00:00 +0000</pubDate>
</item>
<item>
<title>FireMarker: marca los textos mediante Firefox</title>
<description><![CDATA[<p><img alt="firemarker.png" src="http://sentidoweb.com/img/2007/02/firemarker.png" width="150" height="48" class="right"/><strong>FireMarker</strong> es una extensión para Firefox que hemos desarrollado en <strong>Sentido Web</strong> que permite marcar partes del texto de la página que estamos visitando.</p>
<p>Tan solo es necesario seleccionar el texto y pinchar en el icono que aparecerá en la barra de estado de nuestro navegador.</p>
<p><img alt="firemarker3.png" src="http://sentidoweb.com/img/2007/02/firemarker3.png" width="162" height="46" class="center"/></p>
<p>Una vez realizada esta función, el texto se marcará según los colores que sea hayan seleccionado, que por defecto serán amarillo para el fondo y negro para el color de letra.</p>
<p><img alt="firemarker2.png" src="http://sentidoweb.com/img/2007/02/firemarker2.png" width="350" height="252" class="center"/></p>
<p>Los textos marcados seguirán en este estado cuando se refresque la página o se reinicie el navegador, ya que la información marcada se almacena en las preferencias del navegador.</p>
<p>Entre las opciones que nos ofrece esta extensión de Firefox nos encontramos con:</p>
<ul><li>Marcar texto de distintos elementos HTML a la vez.</li>
<li>Borrar el texto marcado de todas las páginas (también de las que no se están visualizando en ese momento).</li>
<li>Borrar el texto marcado de la página actual.</li>
<li>Copiar al portapapeles el texto seleccionado de la página actual. En este caso he añadido un retorno de carro después de cada bloque de texto seleccionado.</li>
<li>Cambiar el color de fondo y de letra del texto seleccionado.</li></ul>
<p>Si alguien encuentra algún problema o error sería de gran ayuda que nos lo comunicara.</p>
<p><a href="http://sentidoweb.com/lab/firefox/FireMarker.1.0.xpi">Descargar FireMarker 1.0</a></p>]]></description>
<link>http://sentidoweb.com/2007/02/26/firemarker-marca-los-textos-mediante-firefox.php</link>
<guid>http://sentidoweb.com/2007/02/26/firemarker-marca-los-textos-mediante-firefox.php</guid>
<category>Laboratorio</category>
<pubDate>Mon, 26 Feb 2007 15:40:00 +0000</pubDate>
</item>
<item>
<title>Post2PDF 1.4: error XSS corregido</title>
<description><![CDATA[<p><img alt="Post2PDF" src="http://sentidoweb.com/img/2006/10/post2pdf.png" class="right" height="127" width="112">Nueva versión de <strong>Post2PDF</strong>, plugin de WordPress para exportar los plugins a formato PDF.</p>

<p>Esta versión corre a cargo de <a href="http://www.buayacorp.com/">Alexander Concha</a>, quien detectó un error de XSS en el plugin (¡ya me vale!) y además de solucionarlo, le ha dado un buen repaso a todo el plugin mejorándolo en muchos aspectos, por lo cual le estoy muy agradecido, ya que a parte de solucionar un error importante, he podido aprender bastante de su experiencia en WordPress.</p>

<p>Como novedad importante, hay que decir que el plugin <strong>solo es compatible con WordPress 2.x</strong>, las pruebas realizadas con versiones anteriores no han sido satisfactorias.</p>

<p><a href="http://sentidoweb.com/temas/desarrollo-web/laboratorio/plugins-wp/index.php#post2pdf">Post2PDF</a></p>]]></description>
<link>http://sentidoweb.com/2007/02/22/post2pdf-14-error-xss-corregido.php</link>
<guid>http://sentidoweb.com/2007/02/22/post2pdf-14-error-xss-corregido.php</guid>
<category>Laboratorio</category>
<pubDate>Thu, 22 Feb 2007 16:30:00 +0000</pubDate>
</item>
<item>
<title>Post2PDF 1.3: compatibilidad con WordPress 2.0</title>
<description><![CDATA[<p><img alt="Post2PDF" src="http://sentidoweb.com/img/2006/10/post2pdf.png" class="right" height="127" width="112" />Nueva versión de <strong>Post2PDF</strong>, plugin de WordPress para exportar los plugins a formato PDF.</p>

<p>En esta ocasión no era compatible con WordPress 2.x ya que las librerías han sido modificadas y fallaba a la hora de acceder a una de ellas que ya no existe.</p>

<p>Aquellos que quieran usarlo en su WordPress 2.x deben bajarse la última versión, mientras que aquellos que no hayan actualizado su versión de WordPress no es necesario que lo actualicen.</p>

<p><a href="http://sentidoweb.com/temas/desarrollo-web/laboratorio/plugins-wp/index.php#post2pdf">Post2PDF</a></p>]]></description>
<link>http://sentidoweb.com/2007/01/29/post2pdf-13-compatibilidad-con-wordpress-20.php</link>
<guid>http://sentidoweb.com/2007/01/29/post2pdf-13-compatibilidad-con-wordpress-20.php</guid>
<category>Laboratorio</category>
<pubDate>Mon, 29 Jan 2007 10:59:54 +0000</pubDate>
</item>
<item>
<title>Laboratorio: ejemplo de CAPTCHA</title>
<description><![CDATA[<p>El otro día ví en Rapidshare un ejemplo de <a href="http://es.wikipedia.org/wiki/Captcha">CAPTCHA</a> que parecía fácil de desarrollar. Se trata de las letras (tres en este caso), giradas ángulos aleatorios, pero no demasiado para que se pueda leer con facilidad, sobre un fondo de letras de tonalidad parecida, de menor tamaño y también giradas aleatoriamente.</p>
<p>Un ejemplo dinámico es el siguiente:</p>
<p><img src="http://displaynone.awardspace.com/captcha/captcha.php" alt="CAPTCHA" class="center"/></p>
<p>El método es muy sencillo, usamos una cadena de texto con todas las letras que queremos que aparezcan, para poder acceder aleatoriamente a cada una de ellas. Después nos crearemos el fondo de la imagen, siendo un gris claro y unas letras pequeñas, de un gris un poco más oscuro sobre ellas, que llenan toda la imagen. Y por último, creamos las tres letras del CAPTCHA para mostrarlas en el centro de la imagen y de un tamaño considerable.</p>
<p>En este caso he cogido la fuente de <a href="http://daFont.net">daFont</a>, pero se puede usar la que se quiera. Yo recomiendo usar alguna que esté distorsionada para que la lectura para nosotros no presente complicaciones, pero si para algún proceso automático.</p>
<p>El script sería el siguiente, teniendo en cuenta que no se trata el almacenamiento en sesión de las letras del CAPTCHA para luego poder compararlas.</p>
<pre><code>&lt;?php
// Juego de letras para usar
$letras = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

// Configuración tamaño imagen y tamaño fuente
$ancho_caja = 200;
$alto_caja = 100;
$tam_letra = 16;
$tam_letra_grande = 60;
// angulo máximo que rota (izq y der) cada letra
$angmax = 20;
// Establecer el tipo de contenido
header("Content-type: image/png");

// Creamos una imagen
$im = imagecreate($ancho_caja, $alto_caja);

// Creo el color del texto, del texto del fondo y del fondo de la imagen
$gris = ImageColorAllocate($im, 200, 200, 200);
$colorLetra = ImageColorAllocate($im, 175, 175, 175);
$colorLetraFondo = ImageColorAllocate($im, 180, 180, 180);


// tipo de letra obtenido en dafont.net
$fuente = './Hotel Coral Essex.ttf';

// Calculo el número de líneas que entran
$caja_texto = imagettfbbox($tam_letra, 0, $fuente , $letras);
$alto_linea = abs($caja_texto[7]-$caja_texto[1]);
$num_lineas = intval($alto_caja / $alto_linea)+1;

// Dibujo las letras del fondo
// Cada letra de escribe de una en una para poder
// darle una rotación independiente al resto
$pos = 0;
for ($i = 0; $i&lt;$num_lineas; $i++) {
    $x = 0;
    for ($j = 0; $j&lt;30; $j++) {
        $texto_linea = $letras[rand(0, strlen($letras)-1)].' ';
        $caja_texto = imagettfbbox($tam_letra, 0, $fuente , $texto_linea);
      imagettftext($im, $tam_letra, rand(-$angmax, $angmax), $x, $alto_linea*$i, $colorLetraFondo, $fuente , $texto_linea);
        // Posicion x de la siguiente letra
        $x += $caja_texto[2] - $caja_texto[0];
    }
}


// Escribo las tres letras del CAPTCHA
$res = $letras[rand(0, strlen($letras)-1)];
$ang1 = rand(-$angmax, $angmax);
$caja_texto = imagettfbbox($tam_letra_grande, $ang1, $fuente , $res);
$y1 = abs($caja_texto[7]-$caja_texto[1]);
$x1 = abs($caja_texto[2]-$caja_texto[0]);

$res .= $letras[rand(0, strlen($letras)-1)]; 
$ang2 = rand(-$angmax, $angmax);
$caja_texto = imagettfbbox($tam_letra_grande, $ang2, $fuente , $res[1]);
$y2 = abs($caja_texto[7]-$caja_texto[1]);
$x2 = abs($caja_texto[2]-$caja_texto[0]);

$res .= $letras[rand(0, strlen($letras)-1)]; 
$ang3 = rand(-$angmax, $angmax); 
$caja_texto = imagettfbbox($tam_letra_grande, $ang3, $fuente , $res[2]);
$y3 = abs($caja_texto[7]-$caja_texto[1]);
$x3 = abs($caja_texto[2]-$caja_texto[0]);

imagettftext($im, $tam_letra_grande, $ang1, ($ancho_caja/2)-(($x1+$x2+$x3)/2), $y1+($alto_caja-$y1)/2, $colorLetra, $fuente , $res[0]);
imagettftext($im, $tam_letra_grande, $ang2, ($ancho_caja/2)-(($x1+$x2+$x3)/2)+($x1), $y2+($alto_caja-$y2)/2, $colorLetra, $fuente , $res[1]);
imagettftext($im, $tam_letra_grande, $ang3, ($ancho_caja/2)-(($x1+$x2+$x3)/2)+($x1+$x2), $y3+($alto_caja-$y3)/2, $colorLetra, $fuente , $res[2]);

imagepng($im);
imagedestroy($im);
imagedestroy($im2);
?&gt; </code></pre>
<p>Un ejemplo lo podéis ver en la misma imagen que aparece arriba, ya que es dinámica y se modifica al recargar la página.</p>
<p><a href="http://sentidoweb.com/lab/codes/php/captcha/captcha.zip">Código</a></p>]]></description>
<link>http://sentidoweb.com/2007/01/03/laboratorio-ejemplo-de-captcha.php</link>
<guid>http://sentidoweb.com/2007/01/03/laboratorio-ejemplo-de-captcha.php</guid>
<category>Laboratorio</category>
<pubDate>Wed, 03 Jan 2007 15:00:00 +0000</pubDate>
</item>
<item>
<title>Post2PDF 1.2: 3 correcciones y 1 novedad</title>
<description><![CDATA[<p><img alt="Post2PDF" src="http://sentidoweb.com/img/2006/10/post2pdf.png" class="right" height="127" width="112"/>Nueva versión <strong>Post2PDF</strong>, el plugin para Wordpress que exporta a PDF los contenidos de nuestros posts. En esta ocasión corregimos tres pequeños defectos e incluimos una pequeña novedad.</p>

<p>Aunque los errores no eran críticos, dos de ellos si hacía que algúnos amigos de <strong>Sentido Web</strong> tuvieran problemas para poder usarlo. </p>

<p>El primero de los casos se daba cuando en la administración de WordPress se tenía como codificación de los caracteres de salida un formato UTF-8 (gracias <a href="http://sentidoweb.com/2006/11/08/post2pdf-11-correccian-de-dos-pequeaos-errores.php#comentario_4003">Víctor</a>). Para solucionarlo ha sido necesario incluir la función <em>mb_convert_encoding</em> para convertir el texto obtenido de la BD en UTF-8 y así luego poder pasárselo al plugin. Aunque <em>TCPDF</em> (la librería usada para crear PDFs) admite codificación distinta a UTF-8, el problema no se solucionaba mediante esta libería.</p>

<p>El segundo problema se daba cuando se usaba una imagen jpeg en la cabecera del documento PDF (gracias <a href="http://blog.evolutionibus.info">Evolutionibus</a>). Aquí debo reconocer que no era capaz de reproducir el error que le daba a un visitante de <strong>Sentido Web</strong>, hasta que cosas de la buena suerte, di con ello.</p>

<p>La novedad que menciono se trata de la inclusión de multilenguaje en la pantalla de administración del plugin. Por ahora solo existe la versión en español, inglés y francés (gracias <a href="http://labuenaletra.blogspot.com/">Belcha</a>), pero me gustaría que existiera traducción para distintos idiomas (sobre todos para los existentes en la Península Ibérica: catalán, euskera, gallego, portugués, ...), pero mientras que la programación no es lo mío, los idiomas tampoco ;).</p>

<p><a href="http://sentidoweb.com/temas/desarrollo-web/laboratorio/plugins-wp/index.php#post2pdf">Post2PDF</a></p>]]></description>
<link>http://sentidoweb.com/2006/12/19/post2pdf-12-3-correcciones-y-1-novedad.php</link>
<guid>http://sentidoweb.com/2006/12/19/post2pdf-12-3-correcciones-y-1-novedad.php</guid>
<category>Laboratorio</category>
<pubDate>Tue, 19 Dec 2006 15:46:18 +0000</pubDate>
</item>
<item>
<title>Laboratorio: crea tu propio CSS Evolution</title>
<description><![CDATA[<p>El otro día <a href="http://sentidoweb.com/2006/11/28/links-for-20061128.php">hablábamos</a> sobre <a href="http://ajaxian.com/archives/css-evolution">CSS Evolution</a>, una forma de ver paso a paso el efecto de las CSS sobre nuestra página. Algo que supongo que a la mayoría nos ha gustado y que puede que queramos verlo en nuestra propia web.</p>
<p><img alt="cssevolution.png" src="http://sentidoweb.com/img/2006/12/cssevolution.png" width="300" height="204" class="center"/></p>
<p>Os paso el javascript necesario para hacerlo, el cual es posible que no sea del todo eficiente, pero bueno, la falta de tiempo no me ha permitido hacer más, entre otras cosas solo funciona para Firefox.</p>
<p>Lo primero es tener los estilos en un <em>link</em>:</p>
<pre>&lt;link rel="stylesheet" type="text/css" media="screen" href="sw-large.css" /&gt;</pre>
<p>Una vez cargada la página, mostrándose con estilos, se desactivará la etiqueta <em>link</em> y luego se recuperará el texto de todos lo estilos, para volver a activar la etiqueta e ir añadiendo los estilos uno a uno cada cierto tiempo.</p>
<pre><code>// Donde almaceno los estilos para luego ir incluyéndolos uno a uno
var antiguas;

function ini() {
  // Se podría hacer para que cogiera todos los links
  var link = document.getElementsByTagName("link")[0];
  link.disabled = true;
  // Avisa para que no te pille despistado
  alert("COMENZAR");
  // Recupera los textos de todos los estilos
  var reglas = document.styleSheets[0].cssRules;
  antiguas = new Array();
  for (var i=0; i&lt;reglas.length; i++) {
    antiguas[i] = reglas[i].cssText;
  }

  // Quita todos los estilos
  for (var i=0; i&lt;reglas.length; i++) {
    document.styleSheets[0].deleteRule(0);
  }

  // Inserta todos los estilos uno a uno
  setTimeout("insertaCSS(0)", 500);
  link.disabled = false;
}

// Añade los estilos
function insertaCSS(i) {
  document.styleSheets[0].insertRule(antiguas[i], i);
  if (i+1&lt;antiguas.length) {
    setTimeout("insertaCSS("+(i+1)+")", 500);
  } else {
    alert("FIN");
  }
}

window.onload = function() {
ini();
};</code></pre>
<p>Es posible que estéis algún rato sin ver cambios, depende de si vuestros estilos son generales o algo concretos, por lo que no afectarán a ciertas partes de la página.</p>
<p>Por cierto, he encontrado un fallo a la hora de recuperar el texto de los estilos, que cuando el texto es algo así:</p>
<pre>p {

color: blue;
}</pre>
<p>Debido a la línea vacía, devuelve un estilo <em>undefined</em>. Bueno, tampoco lo he mirado con detenimiento, pero creo que si se da ese error.</p>
<p><a href="http://sentidoweb.com/lab/codes/js/cssevolution/cssevolution.html">Ejemplo</a>
</p>
]]></description>
<link>http://sentidoweb.com/2006/12/01/laboratorio-crea-tu-propio-css-evolution.php</link>
<guid>http://sentidoweb.com/2006/12/01/laboratorio-crea-tu-propio-css-evolution.php</guid>
<category>Laboratorio</category>
<pubDate>Fri, 01 Dec 2006 13:00:00 +0000</pubDate>
</item>
<item>
<title>Post2PDF 1.1: corrección de dos pequeños errores</title>
<description><![CDATA[<p><img alt="Post2PDF" src="http://sentidoweb.com/img/2006/10/post2pdf.png" class="right" height="127" width="112"/>Nos hubiera gustado tardar tan poco como la gente de Mozilla, pero problemas con la conexión y demás, no hemos podido subirlo antes.</p>
<p>Hemos creado una nueva versión de <strong>Post2PDF</strong> en la que se solucionan un par de errores:</p>
<ul><li>Error: problemas en la selección del título cuando el prefijo de la BD de WordPress no es el que viene por defecto.</li>
<li>Error: se mostraba incorrectamente el icono de PDF en ciertos casos.</li></ul>
<p>Hubiera estado bien haber podido añadir algúna novedad que tenemos pensada, pero era necesario subir la corrección del error.</p>
<p>Gracias <a href="http://francisco.hernandezmarcos.net">Francisco</a> por encontrar uno de los errores y avisarnos.</p>
<p><a href="http://sentidoweb.com/temas/desarrollo-web/laboratorio/plugins-wp/index.php#post2pdf">Post2PDF</a></p>]]></description>
<link>http://sentidoweb.com/2006/11/08/post2pdf-11-correccion-de-dos-pequenos-errores.php</link>
<guid>http://sentidoweb.com/2006/11/08/post2pdf-11-correccion-de-dos-pequenos-errores.php</guid>
<category>WordPress</category>
<pubDate>Wed, 08 Nov 2006 09:16:15 +0000</pubDate>
</item>
<item>
<title>Laboratorio: carga de combos</title>
<description><![CDATA[<p>Ayer nos preguntaban <a href="http://sentidoweb.com/2006/07/13/nueva-versian-de-wamp5.php#comentario_1657">cómo cargar combos según lo que se seleccione en otro combo</a>, y aunque no me quedó muy claro la duda que tenía, pues para intentar ayudarle, voy a explicar cómo hacerlo mediante tres posibilidades.</p>
<p>Primeramente explicaremos cómo hacerlo <strong>mediante el uso de <em>iframes</em> ocultos</strong>. Sí, no es nada web 2.0, pero no a todo el mundo se le da bien el desarrollo web, o no le gusta el uso de Ajax, o simplemente, como me pasa a mí, no nos dejan usar Ajax en los desarrollos del cliente (¡vete a saber por qué!).</p>
<p>Después lo haremos <strong>mediante Ajax</strong> (¡viva la web 2.0!), pero instanciando nosotros mismos el objeto XMLHttpRequest, para que los conceptos de Ajax no se pierdan dentro del uso de librerías, es importante conocer la base para luego usar una librería que te lo haga todo más fácil.</p>
<p>Y por último, ya no solo en plan web 2.0, sino en plan lo último de lo último, lo haremos <strong>usando la librería <a href="http://prototype.conio.net/">Prototype</a></strong>. Que la verdad sea dicha, no se trata de reinventar la rueda.</p>]]></description>
<link>http://sentidoweb.com/2006/10/31/laboratorio-carga-de-combos.php</link>
<guid>http://sentidoweb.com/2006/10/31/laboratorio-carga-de-combos.php</guid>
<category>Laboratorio</category>
<pubDate>Tue, 31 Oct 2006 14:20:00 +0000</pubDate>
</item>
<item>
<title>Laboratorio: cómo hacer un plugin para WordPress</title>
<description><![CDATA[<p><img alt="Post2PDF" src="http://sentidoweb.com/img/2006/10/post2pdf.png" class="right" height="127" width="112"/>Algo que nos gusta hacer en <strong>Sentido Web</strong> es explicar cómo se hacen las cosas, no solo mostrar scripts que encontramos o que inventamos, para así que quienes estén interesados, puedan aprender cómo hacerlo ellos mismos.</p>
<p>El otro día sacamos a la luz el plugin para WordPress <a href="http://sentidoweb.com/2006/10/27/post2pdf-plugin-de-wordpress-para-exportar-a-pdf.php">Post2PDF</a> y hoy vamos a explicar que es lo que hicimos para desarrollarlo.</p>
<p><strong>El plugin consta de dos archivos principales</strong> (a parte de los de la librería), el plugin en sí y un script que es llamado por el plugin que es el que se encarga de la exportación a formato PDF.</p>
]]></description>
<link>http://sentidoweb.com/2006/10/30/laboratorio-como-hacer-un-plugin-para-wordpress.php</link>
<guid>http://sentidoweb.com/2006/10/30/laboratorio-como-hacer-un-plugin-para-wordpress.php</guid>
<category>WordPress</category>
<pubDate>Mon, 30 Oct 2006 13:00:00 +0000</pubDate>
</item>


</channel>
</rss>