Mostrando entradas con la etiqueta programación. Mostrar todas las entradas
Mostrando entradas con la etiqueta programación. Mostrar todas las entradas

30 junio 2008

Tablas movibles y ordenables

Si tenéis páginas con tablas donde se presentan multitud de datos seguro que os habréis preguntado cómo se podrían ordenar los resultados como en algunas aplicaciones de escritorio. O mejor aún colocar las columnas como mejor te vengan para visualizar mejor los datos.

Los dos scripts que os presento hacen precisamente eso de una forma muy fácil. Tan solo necesitas incluir en la cabecera de la página los scripts y añadir la definición de la clase.

Con eso tendrías una tabla como la siguiente:

NombreTeléfonoFecha ingresoSueldo
José González6789012/10/20071500
Francisco García676891/1/20061120
Alejandra Domínguez676394/3/20041800

Si pinchas en una columna la tabla se ordenará de acuerdo a esta columna. Si la arrastras podrás llevarla a la posición que necesites.

Como he dicho antes tan solo debes incluir el script necesario para ordenar (sorttable.js) y el correspondiente para mover (dragtable.js). Para que funcione debes incluir este código en la cabecera:


<script src="dragtable.js"></script>
<script src="sorttable.js"></script>

Y a la tabla que queramos añadirle estas funciones le pondremos como clase draggable o sortable según nos convenga. Si, como en este ejemplo, queremos que pueda hacer las dos cosas tendremos que ponerlo de esta forma:


 <table class="draggable sortable">

28 noviembre 2007

Tutorial de Python: Parte 2 de 2


Sentencias de control de flujo

Las sentencias de control de flujo son while, if y for. No existe el select, en vez de eso se usa if. Para enumerar los miembros de una lista se usa for. Y para obtener una lista de números se utiliza range(). La síntaxis de esta sentencia es así:

lstRange = range(10)
>>> print lstRange
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for intNumero in lstRange:
# Comprobar si intNumero es uno
# de los números en la lista.
if intNumber in (3, 4, 7, 9):
# "Break" finaliza un for sin
# ejecutar la clausula "else".
break
else:
# "Continue" sigue con la siguiente iteración
# del bucle. Aquí no sirve para nada
# ya que es la última sentencia del bucle.
continue
else:
# La clausula "else" es opcional y se ejecuta solo
# si el bucle no encuentra un "break".
pass # Hacer algo

if lstRange[1] == 2:
print "El segundo elemento (las listas empiezan en 0) es 2"
elif lstRange[1] == 3:
print "El segundo elemento (las listas empiezan en 0) es 3"
else:
print "No lo he encontrdo"

while lstRange[1] == 1:
pass

Funciones

Las funciones se declaran con la palabra clave "def". Los argumentos opcionales se colocan en la declaración de la función después de los argumentos obligatorios asignándoles un valor por defecto. También se pueden llamar a parámetros por su nombre, indicando el valor que van a tener. Las funciones pueden devolver una tupla (y desempaquetando la tupla puedes devolver múltiples valores). Las funciones lambda son un tipo especial de función que solo están compuestas por una instrucción. Los parámetros son pasados por referencia. Pero los tipos de datos modificables (tuplas, listas, enteros, cadenas, etc) no pueden cambiarse. Por ejemplo:

# arg2 y arg3 son opcionales. Tienen valores por defecto
# si uno no se pasa (100 y "prueba" respectivamente).
def fnMiFuncion(arg1, arg2 = 100, arg3 = "prueba"):
return arg3, arg2, arg1

ret1, ret2, ret3 = fnMiFuncion("Argumento 1", arg3 = "argumento con nombre")

fnVariable = lambda x: x + 1
>>> print fnVariable(1)
2

Clases

Python soporta una forma limitada de herencia múltiple en las clases. Se pueden declarar variables y métodos privados (por convención, el lenguaje no obliga a hacerlo) añadiendo al menos dos subrayados en el nombre (p.e. "__spam"). También se pueden asignar variables arbitrarias a las instancias de la clase. Por ejemplo:

class MiClase:
varComun = 10
def __init__(self):
self.varMiVariable = 3
def fnMiFuncion(self, arg1, arg2):
return self.varMiVariable

# Esta es la instanciación de la clase
>>> clsInstancia = MiClase()
>>> clsInstancia.fnMiFuncion(1, 2)
3
# Esta variable está compartida por tadas las clases
>>> clsInstancia2 = MiClase()
>>> clsInstancia.varComun
10
>>> clsInstancia2.varComun
10
# Fíjate cómo se usa el nombre de la clase
# en vez del de la instancia.
>>> MiClase.varComun = 30
>>> clsInstancia.varComun
30
>>> clsInstancia2.varComun
30
# Esto no actualizará la variable en la clase,
# sino que creará una nueva en la instancia
# de la clase y le asignará el valor.
>>> clsInstancia.varComun = 10
>>> clsInstancia.varComun
10
>>> clsInstancia2.varComun
30
>>> MiClase.varComun = 50
# Esto no cambia, porque varComun es
# ahora una variable de la instancia.
>>> clsInstancia.varComun
10
>>> clsInstancia2.varComun
50

# Esta clase hereda de MiClase. La herencia múltiple
# se declara como:
# class OtraClase(MiClase1, MiClase2, MiClaseN)
class OtraClase(MiClase):
def __init__(self, arg1):
self.varMiVariable = 3
print arg1

>>> clsInstancia = OtraClase("hola")
hola
>>> clsInstancia.fnMiFuncion(1, 2)
3
# Esta clase no tiene un miebro llamado prueba.
# Pero podemos añadírselo de todas formas.
# Date cuenta que solo será un miembro de clsInstancia.
>>> clsInstancia.prueba = 10
>>> clsInstancia.prueba
10

Excepciones

Las excepciones en Python se tratan con los bloques try-except [nombre de excepción]:

def fnExcepcion():
try:
# La división por cero eleva una excepción
10 / 0
except ZeroDivisionError:
print "Vaya, no válido."

>>> fnExcepcion()
Vaya, no válido.

Importación

Se pueden usar librerías externas con la palabra clave import [nombre de la librería]. También puedes usar la sintaxis from [nombre de la librería] import [nombre función] para funciones individuales. Por ejemplo:

import random
from time import clock

intRandom = random.randint(1, 100)
>>> print intRandom
64

E/S de Archivos

Python tiene un gran conjunto de librerías incluidas. Como ejemplo, así es como se usa la serialización (convertir estructuras de datos en cadenas usando la librería pickle) con E/S a archivos:


import pickle
lstLista = ["Esta", "es", 4, 13327]
# Abrir el archivo C:\binario.dat para escribir. La letra r antes del nombre del archivo es
# evitar el escape de la barra.

flArchivo = file(r"C:\binario.dat", "w")
pickle.dump(lstLista, flArchivo)
flArchivo.close()

flArchivo = file(r"C:\texto.txt", "w")
flArchivo.write("Esta es una cadena de ejemplo")
flArchivo.close()

flArchivo = file(r"C:\texto.txt")
>>> print flArchivo.read()
'Esta es una cadena de ejemplo'
flArchivo.close()

# Abrir el archivo para leer.
flArchivo = file(r"C:\binario.dat")
lstCargada = pickle.load(flArchivo)
flArchivo.close()
>>> print lstCargada
['Esta', 'es', 4, 13327]

Otros

  • Puedes usar del para eliminar variables o elementos de una matriz.
  • La comprensión de cadenas facilita una forma rápida de crear y manipular listas. Consiste en una expresión seguida de una clausula for seguida por cero o más clausulas if@ de esta forma:
lst1 = [1, 2, 3]
lst2 = [3, 4, 5]
>>> print [x * y for x in lst1 for y in lst2]
[3, 4, 5, 6, 8, 10, 9, 12, 15]
>>> print [x for x in lst1 if 4 > x > 1]
[2, 3]
del lst1[0]
>>> print lst1
[2, 3]
del lst1
  • Las variables globales se declaran fuera de las funciones y pueden ser leídas sin ninguna declaración especial. Pero si quieres escribir en ellas tendrás que declararlas al principio de la función con la palabra clave global. Sino Python creará una variable local y le asignará el valor a ella. Hay que tener cuidado con esto porque puede dar lugar a errores muy difíciles de encontrar.
intNumero = 5

def fnMiFuncion():
# Esto mostrará 5
print intNumber

def fnOtra():
# Esto elevará una excepción porque la variable no ha sido asignada
# antes de mostrarse. En la segunda creará una nueva variable local
# y le asignará el número.
print intNumero
intNumero = 3

def fnOtraMas():
global intNumero
# Esta cambiará correctamente el número
intNumero = 3

Epílogo

Este tutorial no pretende ser una lista exhaustiva de todas (ni tan siquiera de una parte) de las posibilidades de Python. Python tiene una cantidad increíble de librerías y muchas más funcionalidades que puedes descubrir si te gusta el lenguaje o te hace falta. Puedes, por ejemplo leer este libro online Dive into Python. Espero que te haya sido útil y amena la lectura. Si crees que haría falta algo más o modificar cualquier cosa, coméntalo.


Versión original

19 noviembre 2007

Tutorial de Python - Parte 1 de 2

Introducción

Si quieres aprender el lenguaje de programación Python y no encuentras un tutorial conciso pero que a la vez explique las principales características del lenguaje, este puede ser el que te alegre el día. Está claro que si quieres aprender un lenguaje necesitas programar con el durante un tiempo. Se asume que ya estás familiarizado con la programación y saltaremos la parte general a cualquier lenguaje. Las palabras clave están señaladas para que puedas encontrarlas fácilmente. También tienes que estar atento porque algunas cosas se indican directamente en el código y se explican muy brevemente.

Propiedades

Python es fuertemente tipado (el tipo está asegurado), dinámico, de tipado implícito (no necesitas declarar las variables), sensible a mayúsculas (var y VAR son dos variables diferentes) y orientado a objetos (todo es un objeto).

Sintaxis

Python no tiene ningún caracter de fin de sentencia y los bloques se especifican indentándolos. Las sentencias que esperan un nivel de indentación terminan en dos puntos (:). Los comentarios empiezan con la almohadilla (#) solo para esa línea. Los valores se asignan con el signo igual y para comprobar la igualdad con dos signos (==). Se pueden incrementar o decrementar valores usando los operadores += y -= respectivamente. Esto sirve para muchos tipos, incluyendo las cadenas. También puedes usar varias variables en una línea. Por ejemplo:

int MiVar = 3
int MiVar += 2
int MiVar -= 1
str MiVar = "Hola"
str MiVar += " mundo."

int MiVar, str MiVar

Tipos de datos

Los tipos de datos en python son listas, tuplas y diccionarios. Los conjuntos están disponibles en la librería sets. Las listas son matrices unidimensionales (pero puedes tener listas de otras listas). Los diccionarios son matrices asociativas (como las tablas hash) y las tuplas son matrices unidimensionales inmutables (las matrices en Python pueden ser de cualquier tipo, así que puedes mezclar enteros, cadenas, etc en las listas, diccionarios o tuplas). El primer elemento en todos los tipos de matrices es el 0. Los números negativos empiezan a contar a partir del final. Así que -1 es el último elemento. Las variables pueden apuntar a funciones. El uso es el siguiente:

lstEjemplo = [1, ["otra", "lista"], ("una", "tupla")]
lstLista = ["Elemento de lista 1", 2, 3.14]
lstLista[0] = "Elemento de lista 1 otra vez"
lstLista[-1] = 3.14
dicDiccionario = {"Clave 1": "Valor 1", 2: 3, "pi": 3.14}
dicDiccionario["pi"] = 3.15
tplTupla = (1, 2, 3)
fnVariable = len
>>> print fnVariable(lstLista)
3

Se puede acceder a rangos de matrices usando los dos puntos (:). Dejando el índice de inicio vacío asume que es el primer elemento y dejando el final asume que es el último.

lstLista = ["Elemento de lista 1", 2, 3.14]
>>> print lstLista[:]
['Elemento de lista 1', 2, 3.1400000000000001]
>>> print lstLista[0:2]
['Elemento de lista 1', 2]
>>> print lstLista[-3:-1]
['Elemento de lista 1', 2]
>>> print lstLista[1:]
[2, 3.14]

Cadenas

Las cadenas pueden usar tanto comillas simples como dobles. Y puedes tener una cadena con un tipo dentro de otra (por ejemplo "El dijo 'Hola'" es válido). Las cadenas de más de una línea se encierran en triples dobles (o sencillas) comillas ("""). Python soporta Unicode usando la síntaxis u"Esta es una cadena Unicode". Para llenar una cadena con valores se debe usar el operador módulo (%) y una tupla. Cada %s se reemplaza con el elemento de la tupla, de izquierda a derecha. También se pueden usar diccionarios para las sustituciones:

>>>print "Nombre: %s\nNúmero: %s\nCadena: %s" % (class.name, 3, 3 * "-")
Name: Poromenos
Number: 3
String: ---

strString = """Esta es una
cadena con más
de una línea."
""

# ATENCION: Observa la s en %(clave)s
>>> print "Esto %(verbo)s un %(nombre)s." % {"nombre": "test", "verbo": "es"}
Esto es un test.

Versión original

16 noviembre 2007

Msgbox con CSS y Ajax

Si estás haciendo una web 2.0 totalmente alucinante y para mostrar un simple mensaje al visitante optas por redirigirlo a otra página que realmente no sirve para nada, o haces uso de alguna librería muy pesada para mostrar una ventanita con Javascript o, simplemente, no te gusta demasiado cómo queda lo que tienes deberías echarle un vistazo a la librería que Andrey Okonetchnikov ha creado.

Basada en los dialogos que muestra Mac OS X presenta un cuadro con un efecto desde arriba que cada realmente bien. Pesa 28,1 Kb incluyendo la hoja de estilo y la imagen para cerrar la ventana.

El uso es muy simple. Hay que incluir el archivo de script y el css.


<script type="text/javascript" src="../js/modalbox.js"></script>
<link rel="stylesheet" href="../js/modalbox.css" type="text/css"/>

Y en el lugar que queramos que aparezca podemos hacer algo tan simple como esto:

Modalbox.show('pagina_por_ajax.html');

O un poco más elaborado:

Modalbox.show('inputBox.php',
{title:'Título',width:300,height:200,
method:'post',
params:{mensaje:'Hola mundo'}});

Como se puede ver, se pueden pasar fácilmente parámetros a la página de destino que queremos que se muestre. También se puede mostrar texto estático generado en la propia página.

Pero como reza el título, lo que busco es diseñar un cuadro que permita la interacción con el usuario. Y para ello, una vez tenemos el cuadro deberemos hacer estos arreglos.

El llamar a otra página para mostrar el cuadro nos ayudará a abstraer la funcionalidad que queremos ofrecer para distintas páginas. Pero esa misma abstracción nos hará un poco más difícil su uso.

Deberemos pasarle a la página que mostrará el contenido el nombre de 2 funciones que serán las encargadas de ejecutar las acciones cuando responda afirmativa o negativamente y crear dichas funciones, por supuesto.


Así tendríamos que añadir a la llamada anterior algo como esto:

params:{...,funcSi:'funcionSi',funcNo:'funcionNo'}

La página que muestra el contenido podría ser algo tan simple como esto:

<?php
echo $_POST['mensaje'];
?>
<div id="botones">
<input type="button" value="Sí" onClick="<?= $_POST['funcSi']?>()"/>
<input type="button" value="No" onClick="<?= $_POST['funcNo']?>()"/>
</div>

Y para terminar, las funciones llamadas desde la ventana deberían utilizar esta función:

Modalbox.hide();

Para ocultar la ventana ya que no hace falta. Con esto tendríamos un funcional (aunque aún no demasiado estético) cuadro de mensaje listo para usar.

14 noviembre 2007

Función de la semana

A partir de ahora cada miércoles pondré alguna función que pueda ser útil en algún momento. Posiblemente me centre en el lenguaje PHP que es el que más uso actualmente. Pero ello no quiere decir que no ponga alguna que otra cosa de otros.

Esta semana hablaré de una instrucción muy útil cuando tenemos que buscar archivos dentro de un directorio.

Si has tenido que mostrar alguna vez las entradas de un directorio y has tenido que filtrarlas de alguna forma (para ver, por ejemplo, todos los archivos de texto, los archivos XML, las imágenes ...) posiblemente lo hayas resuelto abriendo el directorio, leyendo una a una cada entrada, comparándola con lo que buscabas y, después de hacer lo que necesitas, cerrando el directorio.

Pues has hecho mal. Existe una forma mucho más rápida de hacerlo. En PHP desde la versión 4.3 se hace uso de una función también disponible en la biblioteca estándar de C llamada glob().

En PHP su uso es, como suele suceder con todas las demás, mucho más fácil que en C. Pero qué mejor que un ejemplo:


<?php
foreach (glob("*.txt") as $nombre_archivo)
{
echo $nombre_archivo ' con tamaño ',
filesize($nombre_archivo), " bytes\n";
}
?>

Esto mostrará una salida como esta:

archivo1.txt con tamaño 44686 bytes
archivo2.txt con tamaño267625 bytes
archivo3.txt con tamaño137820 bytes

16 octubre 2007

40 formas de optimizar tu código PHP

En la página de diseño Reinholdweber.com he encontrado una lista muy interesante con 40 formas de optimizar el código PHP. Algunas de ellas me han sorprendido porque pensaba que era precisamente lo contrario.

Para aquellos que prefirais leer en la lengua de Cervantes la pongo aquí traducida.

  1. Si un método puede ser estático, decláralo estático. Puede ser hasta 4 veces más rápido.
  2. echo es más rápido que print.
  3. Usa multiples parámetros de echo en vez de concatenar cadenas.
  4. Establece el valor máximo para los bucles for antes y no en el bucle.
  5. Haz un unset a las variables para liberar memoria. Especialmente con matrices grandes.
  6. Evita las variables como __get, __set, __autoload
  7. require_once() es muy costoso
  8. Usa paths completos en los includes y los requires. Así se usará menos tiempo en resolver los paths del SO.
  9. Si necesitas saber el momento en que se empezó a ejecutar el script $_SERVER[’REQUEST_TIME’] es mejor que time().
  10. Comprueba si puedes usar strncasecmp, strpbrk y stripos en vez de regex
  11. str_replace es más rápido que preg_replace, pero strtr es aún más rápido, hasta 4 veces.
  12. Si la función a utilizar, como una función para reemplazar una cadena, acepta tanto matrices como caracteres simples y tu lista de argumentos no es demasiado grande, intenta escribir varias sentencias que reemplacen un carácter cada vez en vez de una llamada que utilice la matriz.
  13. Es mejor usar select que múltiples sentencias if, else if.
  14. La supresión de errores con @ es muy lenta.
  15. Establece la opción mod_deflate en Apache.
  16. Cierra la conexión a la base de datos cuando hayas terminado de utilizarla.
  17. $fila[’id’] es 7 veces más rápida que $fila[id].
  18. Los mensajes de error son caros.
  19. No uses funciones dentro del bucle for. Por ejemplo en esta sentencia for ($x=0; $x <> a la función count() se le llama en cada ciclo.
  20. Incrementar una variable local en un método es el método más rápido. Casi como llamar a una variable local en una función.
  21. Incrementar una variable global es 2 veces más lento que una local.
  22. Incrementar la propiedad de un objeto (ej. $this->prop++) es 3 veces más lento que hacerlo con una variable local.
  23. Incrementar una variable local indefinida es de 9 a 10 veces más lento que una ya inicializada.
  24. Declarar una variable global en una función que no la usa enlentece la función (casi lo mismo que el tiempo necesario para incrementar una variable local). Probablemente PHP comprueba que la variable global exista.
  25. La invocación de métodos parece independiente del número de métodos definidos en la clase. Después de añadir 10 métodos a una clase de prueba (antes y después del método de prueba) no cambia el desempeño.
  26. Los métodos definidos en las clases derivadas se ejecutan más rápido que aquellos en las clases base. Por lo que lo mejor es hacer las clases base lo más específicas posibles.
  27. La llamada a una función con un parámetro y sin cuerpo tarda lo mismo que 7 u 8 operaciones $variablelocal++.
  28. Rodeando una cadena con ' en vez de con " hace la interpretación un poco más rápido, ya que PHP comprueba si hay variables en las cadenas con " pero no en las de '. Por supuesto, esto solo puede hacerse cuando no utilizas variables en la cadena.
  29. Cuando muestras una cadena con echo es más rápido separarlas con comas en vez de con puntos. Nota: esto solo funciona con echo porque es una función que puede utilizar varios argumentos de cadena.
  30. Un script PHP tarda entre 2 y 10 veces más tiempo en servirse que una página estática HTML por Apache. Intenta usar más páginas estáticas y menos scripts.
  31. Los scripts PHP son recompilados cada vez que se ejecuta a menos que sea cacheado. Instalar un producto de cacheo PHP puede aumentar el rendimiento entre un 25 y un 100% eliminando el tiempo de compilación.
  32. Cachea tanto como sea posible. Usa memcached. Memcached es sistema de cacheo de objetos en memoria para acelerar las aplicaciones de web dinámicas aliviando la carga de bases de datos. Como en el anterior, permite no tener que recompilar todo el código.
  33. Cuando trabajes con cadenas y necesites comprobar la longitud de la cadena necesitarás usar strlen(). Aunque esta función es muy rápida porque lee directamente el valor almacenado en la estructura zval de la cadena (que es la estructura en C donde PHP almacena la información de la variable), sigue siendo lenta porque requiere varias operaciones como lowercase y una búsqueda en una tabla hash seguido de la ejecución de la función. Algunas veces se puede acelerar siguiendo este truco con isset().

    Ej.
    if (strlen($foo) < 5) { echo "Foo es demasiado corto"; }
    contra
    if (!isset($foo{5})) { echo "Foo es demasiado corto"; }

    Llamar a isset() parece más rápido que strlen(). La diferencia estriba en que la primera es una construcción del lenguaje y la segunda una función. Por lo que no requiere buscar ninguna función. Esto significa que así no habrá ninguna sobrecarga para buscar la longitud de la cadena.
  34. Cuando se incrementa o decrementa el valor de la variable así $i++ es más lento que $++i. Esto es específico a PHP y no es aplicable a otros lenguajes. Esto sucede porque el postincremento utiliza en PHP 4 opcodes y el preincremento solo 3. La primera actualmente necesita crear una variable temporal mientras que el otro no. Con el optimizador de PHP de Zend esto ya no es así, sin embargo, puede que el servidor que utilices no disponga de este optimizador.
  35. No todo tiene porque ser POO. Los objetos sobrecargan mucho. Cada método o llamada a un objeto consume mucha memoria.
  36. No implementes cada estructura de datos como una clase. Las matrices también son útiles.
  37. No dividas los métodos demasiado. Piensa qué código vas a reutilizar realmente. Siempre puedes dividir el código luego, cuando haga falta.
  38. Haz uso de las funciones predefinidas.
  39. Si tienes funciones que consumen mucho tiempo, considera escribirlas como extensiones en C.
  40. Usa el módulo mod_gzip. Es un módulo de Apache que comprime los datos al vuelo y puede reducir las transferencias hasta en un 80%.
También podrás encontrar un excelente artículo sobre optimizar PHP por John Lim. Que si tengo tiempo traduciré algún otro día.

13 septiembre 2007

Diseñador visual de SQL bajo Web

En esta página podrás encontrar, como dice el título, un diseñador visual de SQL. Desde allí mismo podrás hacer un diseño visual de una base de datos y después exportarlo a Oracle, MS SQL, MySQL o PostgreSQL.

Es muy sencillo de utilizar. Para utilizarlo tan solo tienes que bajártelo a tu servidor Web y descomprimirlo. Permite también guardar las definiciones creadas en una base de datos por una palabra clave para poder recuperarlas luego.
El cuadrado que se ve en la esquina inferior derecha nos permite movernos libremente por el documento. Al estilo de las áreas para moverse en los juegos de estrategia.
Lo único achacable creo que sería que abusan en algunos sitios de efectos Javascript.

30 agosto 2007

Json en PHP

Si tenéis que utilizar JSON en algún servidor que tenga la versión 5.1 de PHP y en el que no tengáis control, una de las mejores soluciones es usar el framework de Zend. Sin embargo, tener que bajar todo el framework tan solo para mandar un array a un script es un poco exagerado.
Para esos casos, os dejo el archivo retocado para poder utilizar esta útil función en PHP.

Para usarlo tan solo debéis llamar a esta función:

$json = Zend_Json_Encoder::encode ($array);

20 agosto 2007

Vuelta de vacaciones

Dos semanas y dos días. Parece una condena. Pero, ¡ qué alegría ! En todo ese tiempo solo he visto durante 1 hora un ordenador :) De vez en cuando viene bien desconectar completamente.
Y, nada más llegar, a currar. Eso es lo peor. Al menos no me lo he tomado mal. Me ha dado tiempo a reírme al enterarme como dos servidores con Windows Server 2003 se autodespromocionaron y dejaron a una treintena de usuarios sin poder validar. Menos mal que la mayor parte del marrón se lo ha comido otro :)

Después he puesto en una página el control que "facilita" Google para hacer búsquedas desde tu dominio. Y como soy un poco maniático con los estándares (para algo están) me dió por validar el código. Más de 80 errores en el modo Strict del XHTML. Así que ... lo he dejado así por si a alguien le interesa:


<form method="get" action="http://www.google.es/custom">
<table>
<tr><td valign="top" align="left">

</td>
<td>
<input type="hidden" name="domains" value="intragenda.sourceforge.net"></input>
<label for="sbi" style="display:none">Introduzca los términos de búsqueda.</label>
<input type="text" name="q" size="20" maxlength="255" id="sbi" style="border:1px solid #9F9F9F"></input>
</td></tr>
<tr>
<td></td>
<td>
<table>
<tr>
<td>
<input type="radio" name="sitesearch" checked="checked" id="ss0"></input>
<label for="ss0" title="Buscar en la web" style="font-size:0.8em;color:black;">Web</label></td>
<td>
<input type="radio" name="sitesearch" value="intragenda" id="ss1"></input>
<label for="ss1" title="Buscar intragenda" style="font-size:0.8em;color:black;">intragenda</label>
<input type="submit" name="sa" value="Buscar en Google" id="sbb" style="border:1px solid #9f9f9f"></td>
</tr>
</table>
<input type="hidden" name="client" value="pub-6274563489879012">
<input type="hidden" name="forid" value="1"></input>
<input type="hidden" name="ie" value="ISO-8859-2"></input>
<input type="hidden" name="oe" value="ISO-8859-2"></input>
<input type="hidden" name="flav" value="0001"></input>
<input type="hidden" name="sig" value="BIcefx84Mn5GJZMi"></input>
<input type="hidden" name="cof" value="GALT:#999999;GL:1;DIV:#4C4C4C;VLC:663399;AH:center;BGC:FFFFFF;LBGC:4C4C4C;ALC:000000;LC:000000;T:000000;GFNT:66B5FF;GIMP:66B5FF;FORID:1"></input>
<input type="hidden" name="hl" value="es"></input>
</td></tr></table>
</form>


Ya se que se podría hacer más "elegante" pero tampoco soy tan maniático. El resultado lo podéis ver aquí.