Un cliente de Subversion gráfico: TortoiseSVN

(21 comentarios)

TortoiseSVN puede descargarse desde su página web. La instalación no tiene ninguna complicación y una vez se termine la instalación se añadirán una serie de entradas en el menú contextual del explorador de Windows.

Si abrimos el explorador y hacemos clic sobre el menú Archivo vemos una entrada de menú checkout y otra TortoiseSVN. Si pulsamos sobre checkout Tortoise comprobará los archivos del repositorio, para lo cual tenemos que pasarle la URL del repositorio (si estamos en nuestro PC, http://127.0.0.1/repos si seguisteis los pasos de la entrada anterior de la bitácora: ¿Qué es un CVS? Instalación de Subversion) y el directorio en que guardaremos en local los archivos que bajemos del repositorio. Al darle a aceptar se creará el directorio donde guardar la copia del repositorio si este no existía y se descargarán los archivos del repositorio a este. En nuestro caso dado que no tenemos ningún archivo en el repositorio aún, simplemente creará un directorio vacío. Una vez que hemos hecho esto podremos añadir, borrar o modificar los archivos en local y mandarlos luego al repositorio.

Si os habéis fijado ha aparecido una marca con fondo verde sobre el directorio donde tenemos el repositorio local. Esto significa que el directorio está actualizado respecto del repositorio remoto. En el momento en que editemos algún archivo en local aparecerá una exclamación sobre fondo rojo en el archivo editado y los directorios que lo contienen, para avisarnos de que tenemos que actualizar el repositorio remoto con nuestra copia (commit).

Vamos a añadir nuestro primer archivo al repositorio remoto. Para ello creamos un archivo en el directorio local, por ejemplo un HolaMundo.java

class HolaMundo{
    public static void main (String arg[ ]){
        System.out.println("Hola Mundo");
    }
}

si hacemos clic con el botón derecho sobre el archivo recién creado dentro de la entrada de menú para TortoiseSVN tenemos una opción Add. Hacemos clic sobre ella, lo que hace que aparezca una cruz en azul, indicando que se ha añadido el archivo al repositorio pero tenemos que enviarlo usando commit. Si hacemos clic con el botón derecho ahora veremos que tenemos una opción commit (actualizar el repositorio remoto con nuestra copia) y otra update (actualizar la copia local con la versión del repositorio remoto). Hacemos clic sobre commit, con lo que nos pedirá nombre de usuario y contraseña si indicamos a Apache que era necesario para escribir en el repositorio. Una vez introducida la contraseña podemos abrir el repositorio remoto (http://127.0.0.1/repos) y comprobamos que aparece un nuevo archivo HolaMundo.java y que ha cambiado el número de revisión de 0 a 1.

Ahora si hacemos algún cambio en el archivo HolaMundo local (nunca se debe editar directamente el repositorio remoto porque podría corromperse la información) y añadimos otra línea, por ejemplo

class HolaMundo {
    public static void main (String arg[ ]) {
        System.out.println("Hola Mundo");
        System.out.println("Adios");
    }
}

vemos que aparece una exclamación en fondo rojo sobre el archivo editado como ya habíamos comentado. Si queremos actualizar la copia remota con la nueva versión de HolaMundo usamos commit.

También podría ser que quisiéramos actualizar la copia de HolaMundo con posibles cambios que hubiera hecho otro programador, para lo cual utilizaríamos update.

Sin embargo hay ocasiones en que el proceso para commit y update no es tan sencillo. Puede ocurrir que otro programador haya editado alguna línea que nosotros también hemos editado, en ese caso hay un conflicto el cual subversion no puede resolver y tiene que ser el propio usuario el que realice los cambios necesarios antes de usar commit. Si intentamos actualizar el repositorio remoto y la copia del repositorio tiene algún conflicto con la nuestra nos mostrará un mensaje de error

‘Commit failed (details follow)
Your file HolaMundo.java is probably out of date.
The version resource does not correspond to the resource within the transaction…’

y aparecerá una señal de alerta sobre el icono del archivo en cuestión. Una vez que termináramos de editar el archivo usaríamos Resolve, y esta vez si, se actualizaría el repositorio remoto.

Además de crear o editar archivos o directorios también podemos por ejemplo eliminarlos, renombrarlos, moverlos,… para más información podéis leer la Guía de Introducción a TortoiseSVN o su FAQ, ambos en inglés.

TortoiseSVN, un cliente de Subversion

¿Qué es un CVS? Instalación de Subversion

(21 comentarios)

CVS es el sistema de control de versiones concurrentes mas utilizado en el mundo del software libre, sin embargo es bastante arcaico (nació en 1985) y tiene bastantes carencias, como son la falta de versionado de directorios, de renombrado o de copias; su uso poco eficiente de la red o el que no almacene archivos binarios completos, solo los cambios entre versiones.

Subversion es un CVS de código abierto, bajo una licencia del tipo Apache/BSD, que nació como alternativa a CVS y que intenta solucionar estos problemas y carencias. Tenemos multitud de alternativas a CVS aparte de Subversion, por ejemplo GNU Arch del que he oido muchas alabanzas y ninguna crítica, pero en esta entrada nos centraremos en Subversion por ser el más popular.

Lo primero es lo primero. ¿A qué se refiere el control de versiones? Estamos hablando de controlar los cambios que realizan los programadores sobre el código fuente (o cualquier otro tipo de usuario sobre un documento cualquier que tenga varias versiones) de forma que no se produzcan inconsistencias por trabajar mas de una persona con el mismo documento y se pueda volver hacia atrás en las versiones de forma cómoda o se pueda comprobar los cambios o los errores corregidos en cada versión.

Vamos a ver ahora su instalación en sistemas Windows. Lo primero que tenemos que hacer es descargar Subversion desde su página web, donde podemos encontrar una versión comprimida o una versión que incorpora un instalador para facilitar mas aún las cosas.

Una vez instalado deberíamos poder ejecutar Subversion de forma local escribiendo svn en la consola. Esto bastaría si no fuéramos a utilizar Subversion en red. En el caso de que si vayamos a hacerlo necesitamos instalar además un servidor web como Apache.

Para comenzar a trabajar con Subversion tenemos que crear un repositorio primero. Un repositorio es donde se va a guardar todas las versiones de nuestro proyecto. Supongamos que queremos crear el repositorio en C:/Documentos/Proyectos, primero nos moveríamos a ese directorio en la línea de comandos y luego usaríamos la herramienta svnadmin para crear el repositorio:

svnadmin create repositorio

Esto crearía un directorio C:/Documentos/Proyectos/Repositorio para actuar como mi repositorio.

Para activar la posibilidad de usar Subversion en red vía Apache necesitamos copiar el módulo mod_dav_svn.so de la carpeta httpd en el directorio donde se instaló subversión a la carpeta de módulos (modules) de Apache tras lo cual editamos nuestro httpd.conf para informar a Apache de la existencia del nuevo módulo. Buscamos la línea

#LoadModule dav_module modules/mod_dav.so

la descomentamos (quitamos el #) y añadimos también

LoadModule dav_svn_module modules/mod_dav_svn.so

Copiamos también las dll de la carpeta bin de Apache y de Subversion al directorio modules para que puedan ser accedidas por los módulos y añadimos al final del httpd.conf lo siguiente

<Location /repos>
DAV svn
SVNPath c:/documentos/proyectos/repositorio
AuthType Basic
AuthName “Repositorio Subversion”
AuthUserFile c:/programas/Apache2/usuarios.txt
# Cualquiera puede leer, para otras operaciones
# hay que estar autentificado.
<LimitExcept GET PROPFIND OPTIONS REPORT>
Require valid-user
</LimitExcept>
</Location>

con esto creamos un repositorio ficticio repos y le indicamos la ruta a él (notad que las barras no son invertidas en las rutas, además si hay espacios en las rutas tenemos que incluir la ruta entre comillas). Además pedimos que los usuarios se autentifiquen para poder realizar cualquier operación que no sea la lectura, comparando el nombre de usuario y contraseña introducidas con la información sobre los usuarios del fichero c:/programas/Apache2/usuarios.txt (nota: dado que no estamos utilizando una conexión segura se podrían capturar las contraseñas usando un sniffer).

El archivo usuarios.txt no existe por defecto, es simplemente un archivo que crearemos nosotros utilizando la herramienta de Apache htpasswd que se encuentra en el directorio bin de la carpeta de Apache y que nos sirve para crear una clave de tipo MD5.

htpasswd -mc c:/programas/Apache2/usuarios.txt Zootropo
htpasswd -m c:/programas/Apache2/usuarios.txt Otro

Le indicamos donde queremos guardar el fichero de contraseñas (en el caso de que la ruta tenga espacios la escribimos entre comillas) y qué nombre de usuario queremos, tras lo cual nos pedirá la contraseña a utilizar para ese usuario. Fijaos en que para el primer usuario que creemos (Zootropo) hay que añadir también el flag c para que cree el fichero.

Si reiniciamos el servidor ahora deberíamos poder acceder al repositorio vía web. Abrimos el navegador de internet y en la barra de localización escribimos http://127.0.0.1/repos/. Si nos sale un mensaje de página no encontrada algo ha fallado.

Ahora que el servidor CVS ya está listo necesitamos un cliente CVS para trabajar con él. Subversion incluye herramientas de línea de comandos para este fin, pero es mas cómodo utilizar una interfaz gráfica como la de TortoiseSVN

Construye tu propia extensión para Firefox con Zootropo

(19 comentarios)

Este post es simplemente una recopilación de los enlaces al mini curso sobre XUL que he estado escribiendo los últimos días. El resultado final del curso como ya sabrán los que lo hayan seguido es la creación de una pequeña extensión que muestra la hora del sistema cuando se pulsa sobre la entrada de menú correspondiente a nuestra extensión. Las entradas del curso son las siguientes:

  1. Pequeña introducción a XUL, que explica simplemente que es XUL.
  2. Nuestro primer programa en XUL en el que creamos una especie de ‘Hola Mundo’. Vemos por primera vez la estructura de un programa XUL y nuestro primer widget en XUL, el botón.
  3. Otro programa XUL, donde vemos los atributos de los botones
  4. Comunicándonos con el usuario en el que aprendemos como crear widgets label, textboxes y algo sobre el layout manager de XUL.
  5. Otros widgets de XUL donde completamos nuestro recorrido por los widgets básicos de XUL
  6. Creación de Menús
  7. RDF, pequeña introducción a este formato derivado de XML y estandarizado por el W3C.
  8. El registro chrome aprendemos que es el registro chrome, XPCOM y XPConnect y como instalar nuestras aplicaciones en chrome.
  9. Modularidad en XUL sobre como separar la interfaz de usuario definida con XUL, el estilo de la aplicación definido con css y el funcionamiento en código javascript.
  10. Nuestra primera extensión
  11. XPI, sobre el formato de instalación automático de extensiones de Mozilla.

There is only XUL (XI, Fin)

(7 comentarios)

Ahora que ya hemos terminado nuestra pequeña extensión para Firefox vamos a ver como podemos crear un xpi para que se instale automáticamente. La extensión xpi viene de XPInstall donde XP se refiere a Cross (X) Platform o multiplataforma, es decir, que puede ser ejecutado en distintas plataformas (Windows, Linux, Mac OS,…).

Un xpi es un simple archivo zip con extensión renombrada que contiene un fichero de instalación y los ficheros que componen la extensión. En el caso de las versiones anteriores a la 0.9 se utilizaba install.js como script instalador, ahora en cambio se utiliza un fichero rdf, install.rdf, pero podemos incluir ambos si queremos que la extensión se pueda instalar en la versión 0.9 y también en las anteriores.

Un archivo install.rdf tendría un aspecto parecido a este:

<?xml version=”1.0″?>
<RDF xmlns=”http://www.w3.org/1999/02/22-rdf-syntax-ns#” xmlns:em=”http://www.mozilla.org/2004/em-rdf#”>
<Description about=”urn:mozilla:install-manifest”>
<em:id>{eb78c871-3d9d-433f-b49b-12468119be89}</em:id>
<em:name>Mostrar Hora</em:name>
<em:version>0.1</em:version>
<em:description>Muestra la hora del sistema en un alert al hacer click sobre la entrada de menu correspondiente.</em:description>
<em:creator>Zootropo</em:creator>
<em:homepageURL>http://mundogeek.net </em:homepageURL>

<em:file>
<Description about=”urn:mozilla:extension:file:hora.jar”>
<em:package>/content/hora/</em:package>
</Description>
</em:file>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>0.9</em:minVersion>
<em:maxVersion>0.9</em:maxVersion>
</Description>
</em:targetApplication>

</Description>
</RDF>

Tenemos primero una serie de predicados de información sobre nuestra aplicación que son los siguientes:

  • em:id que se refiere a un id único que podemos generar con esta utilidad de Microsoft y no al identificador de la extensión
  • em:name, el nombre de la extensión
  • em:version, la versión de nuestra extensión.
  • em:description, una descripción de la utilidad de nuestra aplicación.
  • em:creator, nombre del creador de la aplicación.
  • em:homepageURL, página web de la extensión.
  • em:optionsURL, ventana que se mostrará al hacer click sobre las preferencias.
  • em:aboutURL, ventana que se mostrará como About de la aplicación.
  • em:iconURL, icono para identificar la extensión.
  • em:updateURL, URL opcional para la autoactualización.

Tenemos después un predicado em:file que agrupa información sobre los ficheros de nuestra aplicación, donde se encuentra content, skin y locale dentro del archivo .jar, y otro predicado em:targetApplication sobre el programa para el que está destinado nuestra extensión. em:id se refiere al id del navegador, en este caso Firefox y minVersion y maxVersion se refieren respectivamente a la versión mínima y máxima de la aplicación.

Si no quereis complicaros podeis utilizar para crear los xpi un script creado por Ted Mielczarek que podeis encontrar en su página web. También podeis consultar la traducción del documento de Ben Gooder sobre install.rdf.

Ahora que tenemos todos los archivos necesarios lo primero que tenemos que hacer es crear un archivo jar que incluya los archivos de nuestra extensión. Comprimimos en un mismo archivo zip las carpetas content, locale y skin de nuestra aplicación (o content solo si no hemos definido las otras dos). Cambiamos la extensión del archivo resultante a .jar de forma que nos quede un archivo hora.jar en este ejemplo y ya tenemos el archivo jar que empaqueta nuestra aplicación, nos queda empaquetar el .jar y el archivo de instalación en un xpi. Para eso creamos una carpeta chrome a la que movemos hora.jar y comprimimos en un .zip la carpeta chrome y el install.rdf (e install.js en el caso de que lo tengamos) y renombramos la extensión del archivo a .xpi.

El archivo xpi resultante para que comprobeis como se instala automáticamente como cualquier otra extensión de firefox es este.

There is only XUL (X)

(6 comentarios)

Ahora que hemos visto un poco por encima como funciona XUL vamos a ponernos un poco mas serios y vamos a crear una pequeña extensión para nuestro navegador. Por ahora algo sencillo como añadir una nueva entrada de menú al menú de herramientas de Firefox, que nos muestre un alert con la hora del sistema cada vez que se haga click sobre él. El proceso para Mozilla sería un proceso bastante parecido. Para ello tenemos que ver que son los overlays, cuya traducción al castellano sería algo asi como superposiciones.

Lo primero vamos a recordar que toda la interfaz de Mozilla y Firefox esta hecha con XUL, por lo tanto será razonable suponer que en algún lugar tiene que haber un fichero xul que lo define. Y acertariais al hacer esta suposición. La interfaz del navegador esta definida en un archivo llamado browser.xul en el subdirectorio chrome dentro de la carpeta en la que esta instalado nuestro navegador, pero si entrais en esa carpeta para echar un vistazo al código no lo encontrareis. browser.xul esta empaquetado dentro de uno de esos archivos .jar que veis.

Un archivo jar es simplemente un archivo .zip con la extensión renombrada. Podemos descomprimirlo con winzip u otro programa de compresión de archivos. Si descomprimimos browser.jar tendremos una carpeta browser que alberga una carpeta content en la que tenemos a su vez las carpetas browser y browser-region. Dentro de la primera se encuentra además del browser.xul al que nos referíamos otros archivos .xul, archivos js con código javascript y archivos css, hojas de estilo.

Si abrimos el archivo browser.xul tendremos una ventana del navegador de Firefox pero con la peculiaridad de que no veremos las barras de herramientas, los botones u otros widgets de las extensiones que tengamos instaladas. Esto es así por que cuando abrimos una ventana del navegador estamos abriendo browser.xul, pero también otros archivos .xul que se superponen. Sería algo así como coger varios dibujos en papel cebolla y ponerlos uno encima de otro, algo así como las capas de Photoshop. Esto es lo que vamos a ver ahora, el overlay.

Lo primero que tenemos que hacer es crear una carpeta para nuestra aplicación, por ejemplo ‘hora’ completando una estructura de directorios hora/content/hora. Dentro del directorio mas profundo de esta jerarquía creamos un archivo hora.xul con el siguiente código:

<?xml version=”1.0″?>

<overlay xmlns=”http://www.mozilla.org/keymaster/gatekeeper/ there.is.only.xul” xmlns:nc=”http://home.netscape.com/NC-rdf#”>

<popup id=”menu_ToolsPopup”>

<menuitem
oncommand=”fecha=new Date(); hora=fecha.getHours(); minuto=fecha.getMinutes(); segundo=fecha.getSeconds(); alert(hora + ‘ : ‘ + minuto + ‘ : ‘ + segundo);” label=”Mostrar Hora;”/>

</popup>

</overlay>

Lo que estamos haciendo aquí es crear un documento XUL que solo contiene una entrada de menú dentro de un popup que queremos superponer sobre el navegador. La única diferencia con los ejemplos que hemos visto hasta ahora es que en lugar de definir una ventana definimos un overlay pero aparte del nombre no hay mucha diferencia. Simplemente indicamos a Mozilla que este no es un documento xul completo, si no que vamos a combinarlo con otro/s documentos xul.

Ahora veamos como funcionaría el overlay con un caso sencillo antes de pasar a mayores. Supongamos un archivo XUL que importa el overlay que acabamos de definir; para ello usaría algo parecido a este código:

<?xul-overlay href=”chrome://hora/content/hora.xul”?>

Lo que ocurriría entonces es que para cada elemento con un id definido en el archivo de overlay se buscaría el mismo elemento con el mismo id en el documento sobre el que queremos superponer. Antes de nada tendríamos que haber abierto el fichero browser.xul para ver donde se definen las entradas de menú del menú herramientas y cual es el ID de conjunto que las contiene. Ese trabajo ya esta hecho; como se puede ver en el código tenemos un elemento popup con id menu_ToolsPopup, aquí es donde está la clave.

En el caso de que no se encuentre una instancia de ese elemento con ese ID entonces este elemento no se tiene en cuenta al hacer el overlay. Si por el contrario si se encuentra, entonces el elemento y todo lo que se defina dentro de este en el overlay pasará a superponerse al documento raíz, lo cual quiere decir que el elemento del documento raíz adquirirá todos los atributos que se definan en el overlay además de todos sus subelementos.

En este ejemplo el documento raíz es el que se encargaba de añadir el overlay usando la etiqueta xul-overlay, sin embargo esto no nos es de utilidad en nuestro caso ya que no queremos tener que cambiar el código de browser.xul. La solución es hacer que sea el overlay el que diga a que documento se tiene que superponer. Esto podemos hacerlo añadiendo la información de los overlay a nuestro contents.rdf

<?xml version=”1.0″?>

<RDF:RDF xmlns:RDF=”http://www.w3.org/1999/02/22-rdf-syntax-ns#”
xmlns:chrome=”http://www.mozilla.org/rdf/chrome#”>

<RDF:Seq about=”urn:mozilla:package:root”>
<RDF:li resource=”urn:mozilla:package:hora”/>
</RDF:Seq>

<RDF:Description about=”urn:mozilla:package:hora”
chrome:displayName=”Mostrar Hora 0.1″
chrome:author=”Zootropo”
chrome:authorURL=”http://mundogeek.net”
chrome:name=”hora”
chrome:extension=”true”
chrome:description=”Añade una opción al menú de herramientas para mostrar la hora actual.”>
</RDF:Description>

<RDF:Seq about=”urn:mozilla:overlays”>
<RDF:li resource=”chrome://browser/content/browser.xul”/>
</RDF:Seq>

<RDF:Seq about=”chrome://browser/content/browser.xul”>
<RDF:li>chrome://hora/content/hora.xul</RDF:li>
</RDF:Seq>

</RDF:RDF>

Vemos que tenemos un par de listas nuevas, una referida a urn:mozilla:overlays en el que los elementos de la secuencia son los documentos sobre los que se va a hacer el overlay (en este caso el browser.xul para superponer a nuestro navegador) y otra secuencia con el archivo xul sobre el que hacemos el overlay como sujeto y como entradas de la secuencia los archivos xul a superponer.

Borramos overlay.rdf como en el post anterior, añadimos una entrada nueva a installed-chrome.txt para el content de nuestra aplicación (no tendremos locale porque la única función javascript que utilizamos esta embebida en el archivo xul ni skin ya que no utilizamos ningún css) y reiniciamos nuestro navegador, tras lo cual tendremos una nueva entrada al final del menú de herramientas que nos permitirá ver la hora del sistema.

There is only XUL (IX)

(0 comentarios)

A la hora de organizar los ficheros de nuestra aplicación se sigue un estándar de facto en el que tenemos tres subdirectorios dentro de la carpeta de nuestra aplicación: content, local y skin.

El directorio content incluye un subdirectorio con el nombre de nuestra aplicación que a su vez contiene todos los archivos xul de la aplicación, además de los archivos js con el código javascript de la aplicación y un fichero contents.rdf que nos dice entre otras cosas cual es el archivo .xul principal.

En el directorio locale se incluyen una serie de subdirectorios con el código del idioma correspondiente, por ejemplo es-ES para castellano o en-US para inglés de estados unidos. Dentro de cada uno de estos directorios tenemos un subdirectorio con el nombre de la aplicación que incluye a su vez los ficheros dtd que sirven para separar la aplicación de los mensajes de esta para facilitar el proceso de traducción y un fichero contents.rdf.

Por último el directorio skin contiene otro subdirectorio de nombre classic que incluye un directorio con el nombre de nuestra aplicación con los ficheros css que se utilizan para modificar el aspecto de la aplicación y un archivo contents.rdf.

Hasta ahora el código javascript que hemos utilizado estaba incrustado dentro de los ficheros xul, en el siguiente ejemplo veremos como importar el código de un fichero js separado del código xul. Como simple comentario, el código css que hemos utilizado hasta ahora estaba almacenado en archivos css aparte como dicta la norma para conseguir modularidad; sin embargo, al igual que el código javascript, el código css puede almacenarse en un fichero aparte o incrustado dentro del propio archivo xul.

<?xml version=”1.0″?>
<!DOCTYPE window SYSTEM “chrome://caffeine/locale/caffeine.dtd”>

<?xml-stylesheet href=”chrome://caffeine/skin/caffeine.css” type=”text/css”?>
<window xmlns=”http://www.mozilla.org/keymaster/gatekeeper/ there.is.only.xul”
xmlns:nc=”http://home.netscape.com/NC-rdf#”>

<script type=”application/x-javascript”
src=”chrome://caffeine/content/caffeine.js”/>

<button onclick=”funcion();” label=”&nombreBoton;” accesskey=”&teclaAcceso”/>

</window>

La línea de script es la que se encarga de importar el código javascript desde el fichero javascript especificado de forma que podemos usar la función funcion() al hacer click en el botón aunque no la hallamos definido en este fichero. Podemos ver también la forma en que se importan los ficheros dtd con los mensajes de la aplicación en la segunda línea del código fuente. Hasta ahora no habíamos utilizado ficheros dtd, sino que incluíamos el texto de la aplicación imbuida dentro de esta; ahora con estos ficheros llevamos la modularidad de xul hasta el extremo, separando del código xul el funcionamiento, la apariencia y los mensajes.

El uso de los mensajes que se definen en los DTDs podemos verlo en el label y la accesskey del botón, un símbolo ampersand, el nombre que le dimos al mensaje en el dtd y punto y coma. El fichero DTD para este programa sería:

<!ENTITY nombreBoton “Mi Boton”>
<!ENTITY teclaAcceso “M”>

Para cada mensaje como vemos tenemos un !ENTITY, la segunda entrada de !ENTITY es el nombre del mensaje que se utilizará para referirse a él en el código XUL como ya vimos y el tercero el valor. En este DTD nombreBoton es “Mi Boton” pero podríamos tener otro DTD para el inglés en cuyo caso sería “My Button”, etc, de forma que la traducción de la aplicación es mas sencilla al separar el código y los mensajes.

Por último como ya dijimos cada uno de los directorios de la aplicación incluye un fichero contents.rdf para poder registrar ese directorio en el registro chrome. Veamos el caso del contents.rdf de los locales:

<?xml version=”1.0″?>
<RDF:RDF xmlns:RDF=”http://www.w3.org/1999/02/22-rdf-syntax-ns#”
xmlns:chrome=”http://www.mozilla.org/rdf/chrome#”>

<!– Lista de todos los locales que incluimos, en este caso solo es-ES –>
<RDF:Seq about=”urn:mozilla:locale:root”>
<RDF:li resource=”urn:mozilla:locale:es-ES”/>
</RDF:Seq>

<!– Informacion sobre cada uno de los locales –>
<RDF:Description about=”urn:mozilla:locale:es-ES”
chrome:displayName=”Castellano (ES)”
chrome:name=”es-ES”>
<chrome:packages>
<RDF:Seq about=”urn:mozilla:locale:es-ES:packages”>
<RDF:li resource=”urn:mozilla:locale:es-ES:caffeine”/>
</RDF:Seq>
</chrome:packages>
</RDF:Description>

</RDF:RDF>

Y el contents.rdf de los skins:

<?xml version=”1.0″?>

<RDF:RDF xmlns:RDF=”http://www.w3.org/1999/02/22-rdf-syntax-ns#”
xmlns:chrome=”http://www.mozilla.org/rdf/chrome#”>

<RDF:Seq about=”urn:mozilla:skin:root”>
<RDF:li resource=”urn:mozilla:skin:classic/1.0″ />
</RDF:Seq>

<RDF:Description about=”urn:mozilla:skin:classic/1.0″>
<chrome:packages>
<RDF:Seq about=”urn:mozilla:skin:classic/1.0:packages”>

</RDF:Seq>
</chrome:packages>
</RDF:Description>

</RDF:RDF>

rdf no necesitan comentarios. Son muy parecidos al contents.rdf del directorio contents. Tan solo queda registrar en el registro chrome contents, locals y skins. Si recordamos en el anterior post sobre XUL al registrar el contents añadíamos a installed-chrome.txt una línea similar a esta:

content,install,url,resource:/chrome/caffeine/content/caffeine/

Si echamos un vistazo a las demás entradas en installed-chrome veremos que algunas comienzan con skin, otras con locale y otras con contents. Esto es lo único que tenemos que cambiar a la hora de registrar skins y locales, lo demás es similar, por lo tanto para nuestra aplicación de ejemplo tendríamos que añadir las líneas:

skin,install,url,resource:/chrome/caffeine/content/caffeine/
locale,install,url,resource:/chrome/caffeine/locale/es-ES/
content,install,url,resource:/chrome/caffeine/skin/classic/caffeine/

There is only XUL (VIII)

(5 comentarios)

Hasta ahora dividiamos un programa XUL en la interfaz en si, creada con XUL; el aspecto de los widgets, definido con CSS y la funcionalidad, programada en Javascript. Sin embargo Javascript no nos permite cosas como acceder al disco duro, de modo que si tuvieramos que limitar la funcionalidad de los programas basados en XUL a lo que podemos hacer con Javascript, estos no tendrían demasiada utilidad.

La solución a este problema nos viene dada en forma de una libreria escrita en código dependiente de la plataforma llamada XPCOM y una capa intermedia entre javascript y esta librería llamada XPConnect, que traduce los objetos XPCOM en objetos Javascript que puedan ser manipulados en nuestro programa.

El problema de esto es que la seguridad de Mozilla dejaría mucho que desear si cualquier script pudiera hacer cosas como borrar los archivos del disco duro. Aquí es donde vemos la utilidad de chrome.

Como ya se comentó la url chrome:// se refiere al directorio chrome dentro del directorio de instalación de Mozilla/Firefox. Solo los scripts que se encuentren en este directorio tienen permiso para utilizar XPConnect, lo cual implica que para usar los objetos de XPCOM necesitamos instalar nuestra aplicación en chrome. Sin embargo no basta simplemente con mover nuestros ficheros a ese directorio, si no que tenemos que añadir una serie de entradas a installed-chrome.txt para que nuestra aplicación sea añadida al ‘registro chrome’ de forma que Mozilla pueda resolver las urls chrome:// al directorio en que esta instalada nuestra aplicación.

Lo primero que tenemos que hacer es crear un archivo de tipo rdf llamado contents.rdf que proporcionará información a Mozilla sobre el nombre de la aplicación, el autor, la versión…

<?xml version=”1.0″?>

<RDF:RDF xmlns:RDF=”http://www.w3.org/1999/02/22-rdf-syntax-ns#”
xmlns:chrome=”http://www.mozilla.org/rdf/chrome#”>

<RDF:Seq about=”urn:mozilla:package:root”>
<RDF:li resource=”urn:mozilla:package:caffeine”/>
</RDF:Seq>

<RDF:Description about=”urn:mozilla:package:caffeine”
chrome:displayName=”The Caffeine XPerience”
chrome:author=”Zootropo”
chrome:name=”caffeine”
chrome:extension=”true”/>

</RDF:RDF>

En este ejemplo displayName es el nombre completo para la aplicación; author el autor de la aplicación, name el identificador de la aplicación (como se referirá Mozilla a él, tiene que ser igual que el valor después de urn:mozilla:package:) y extension indica si la aplicación es una extensión, de forma que se mostrará en la lista de extensiones de Mozilla en el caso de que sea true (cierto).

Una vez creado nuestro fichero contents.rdf lo copiamos a la carpeta de nuestra aplicación. Editemos ahora el fichero installed-chrome.txt que se encuentra dentro del directorio chrome, teniendo en cuenta cerrar antes el navegador, para registrar nuestra aplicación de forma que Mozilla sepa donde encontrarla. Al final del fichero añadimos por ejemplo para el caso de que el directorio de la aplicación sea caffeine/content/caffeine/ dentro del directorio chrome:

content,install,url,resource:/chrome/caffeine/content/caffeine/

Solo queda borrar el fichero chrome.rdf que contiene la información sobre las extensiones registradas en chrome para que vuelva a crearse cuando ejecutemos Mozilla/Firefox y tenga en cuenta nuestra aplicación. La información de cada una de las aplicaciones la recolectará basándose en installed-chrome.txt para encontrarla y en el contents.rdf que creamos para procesar el contenido de nuestra aplicación.

Ahora deberíamos ser capaces de poder ejecutar cualquier archivo xul de nuestra aplicación (y por añadidura referirnos a cualquier archivo de nuestra aplicación) usando una url chrome de la forma:

chrome://id-aplicacion/content/archivo.xul

Por ejemplo para abrir el archivo foo.xul de la aplicación con id caffeine podríamos simplemente escribir chrome://caffeine/content/foo.xul

There is only XUL (VII)

(6 comentarios)

Vamos a hacer un inciso para hablar sobre RDF. RDF es un estándar de la W3C cuyo nombre deriva de las siglas de ‘Resource Description Framework’ y que es un subconjunto de XML, al igual que XUL, que nos sirve para expresar hechos (cosas que son ciertas, la información en forma de predicados que tenemos sobre el mundo real). Sería algo parecido a los predicados de Prolog. Por ejemplo, un predicado del tipo ‘A Linus le gustan los pingüinos’ en RDF podría expresarse como

<Description about=”Linus”>
<le-gustan>pingüinos</le-gustan>
</Description>

La etiqueta Description sirve para representar uno o mas hechos, con un atributo about para indicar sobre quien es ese hecho, es decir, el sujeto del hecho. Description puede contener una o varias etiquetas hijas siendo cada una de ellas un predicado.

<Description about=”Linus”>
<le-gustan>pingüinos</le-gustan>
<informatico>true</informatico>
<linuxero>true</linuxero>
<nacionalidad>finlandesa</nacionalidad>
</Description>

En este ejemplo definimos una serie de predicados sobre Linus, con una etiqueta definida por nosotros mismos para describir ese predicado y con el valor entre las etiquetas.

Ahora, ¿como expresaríamos el hecho de que a Linus le gustan además de los pingüinos los sombreros o los camaleones?. Podríamos usar varios predicados le-gustan pero RDF nos proporciona un método mejor. En RDF se definen tres etiquetas que sirven como contenedoras: <Bag>, que define un conjunto de items; <Seq>, que define un conjunto de items ordenados y <Alt>, que define un conjunto de elementos que son alternativos, es decir, que se consideran equivalentes. Los elementos de un contenedor se encierra entre <li> y </li> (li de list, lista, al igual que la etiqueta de HTML). Veamos un ejemplo:

<Description about=”Linus”>
<Bag ID=”Gustos”>
<li>pingüinos</li>
<li>sombreros</li>
<li>camaleones</li>
</Bag>
<informatico>true</informatico>
<linuxero>true</linuxero>
<nacionalidad>finlandesa</nacionalidad>
</Description>

Ahora tenemos una lista de los gustos de Linus usando un Bag que sería equivalente a un predicado con varios valores. Para identificar el predicado que define este Bag usamos el atributo ID. Seq y Alt serían similares, tan solo cambia su significado.

Para finalizar esta pequeña introducción a RDF vamos a ver un ejemplo completo para explicar un par de cosas que quedan en el tintero.

<RDF:RDF xmlns:RDF=”http://www.w3.org/1999/02/22-rdf-syntax-ns#”
xmlns:BEBIDAS=”http://www.caffeine.com/rdf#”>

<RDF:Bag about=”http://www.caffeine.com/tipos-de-bebida”>
<RDF:li>
<RDF:Description about=”http://www.caffeine.com/infusiones/te”>
<BEBIDAS:nombre>Te</BEBIDAS:nombre>
<BEBIDAS:precio>1,12</BEBIDAS:precio>
</RDF:Description>
</RDF:li>
<RDF:li>
<RDF:Description about=”http://www.caffeine.com/infusiones/tila”>
<BEBIDAS:nombre>Tila</BEBIDAS:nombre>
<BEBIDAS:precio>0,9</BEBIDAS:precio>
</RDF:Description>
</RDF:li>
<RDF:li>
<RDF:Description about=”http://www.caffeine.com/cafes/solo”>
<BEBIDAS:nombre>Cafe solo</BEBIDAS:nombre>
<BEBIDAS:precio>1,2</BEBIDAS:precio>
</RDF:Description>
</RDF:li>
</RDF:Bag>
</RDF:RDF>

Lo primero que tenemos que comentar es la línea con la que comienza el RDF. Esta es similar al window de XUL en el que definimos que espacio de nombres vamos a utilizar. Usaremos RDF que esta definida en tal URL y BEBIDAS que esta definido en tal otra. Relacionado con esto, el RDF: que aparece antes de las palabras clave de RDF o el BEBIDAS: que aparece antes de los nombres de los predicados indica el espacio de nombres que estamos utilizando, es decir, RDF:li por ejemplo significa que nos estamos refiriendo a la etiqueta li que está definida dentro de RDF.

There is only XUL (VI)

(0 comentarios)

Antes de empezar con cosas mas serias vamos a ver como se implementan los menús en XUL.

<?xml version=”1.0″?>
<?xml-stylesheet href=”chrome://global/skin” type=”text/css”?>
<window xmlns=”http://www.mozilla.org/keymaster/gatekeeper/ there.is.only.xul”>
<menubar>
<menu label=”Archivo”>
<menupopup>
<menuitem label=”Nuevo”/>
<menuseparator/>
<menuitem label=”Abrir”/>
<menuitem label=”Salvar”/>
<menuitem label=”Cerrar”/>
</menupopup>
</menu>
<menu label=”Editar”>
<menupopup>
<menuitem label=”Deshacer”/>
<menuseparator/>
<menuitem label=”Copiar”/>
<menuitem label=”Cortar”/>
<menuitem label=”Pegar”/>
</menupopup>
</menu>
<menu label=”Ver”>
<menupopup>
<menuitem label=”Regla”/>
<menu label=”Fuente”>
<menupopup>
<menuitem label=”Arial”/>
<menuitem label=”Verdana”/>
</menupopup>
</menu>
<menu label=”Color”>
<menupopup>
<radiogroup>
<radio label=”Rojo”/>
<radio label=”Azul”/>
<radio label=”Negro”/>
<radio label=”Verde”/>
</radiogroup>
</menupopup>
</menu>
<menuitem label=”Pantalla Completa”/>
</menupopup>
</menu>
</menubar>
</window>

menubar define la barra de menús. Dentro de menubar tenemos un conjunto de etiquetas menu que son las que definen las entradas de menú en si. Si al pulsar sobre el menú se va a desplegar una lista de submenús, entonces utilizamos menupopup y menuitem para cada uno de los submenús. En el caso de que no hubieramos agrupado los submenús (menuitems) en un menupopup se habrían mezclado todas las entradas en una sola, que no es lo que queremos. Además tenemos otro elemento que es menuseparator, que introduce una línea para dividir el menú en partes. Notar también que los submenús también pueden ser menús con varias entradas como vemos en el caso de fuente en que definimos un nuevo menú y no un menuitem. Como último comentario decir que en lugar de menuitems o menús, como entradas dentro de los menús también podríamos tener otros elementos como checkboxes o radiobuttons como vemos en el caso del menú color.

There is only XUL (V)

(0 comentarios)

Esta vez vamos a terminar de ver los widgets básicos de XUL, junto con los botones, las cajas de texto o las etiquetas que ya vimos anteriormente. El código del programa ejemplo es el siguiente:

<?xml-stylesheet href=”chrome://global/skin/” type=”text/css”?>
<window xmlns=”http://www.mozilla.org/keymaster/gatekeeper/ there.is.only.xul”>

<tabbox orient=”vertical”>
<tabs orient=”horizontal”>
<tab label=”Radiobuttons”/>
<tab label=”Checkboxes”/>
<tab label=”Listbox”/>
<tab label=”Imagenes”/>
</tabs>

<tabpanels>
<radiogroup orient=”vertical”>
<radio label=”Cafe solo”/>
<radio label=”Cafe con leche”/>
<radio label=”Descafeinado”/>
</radiogroup>
<box>
<checkbox label=”Donuts”/>
<checkbox label=”Croisants”/>
<checkbox label=”Caracolas”/>
</box>
<image src=”file://C:widgetsmozilla.png”/>
</tabpanels>
</tabbox>

</window>

Las pestañas se declaran mediante la etiqueta tabbox. orient nos dice si la orientación, en este caso de las pestañas, será horizontal o vertical. Cada una de las pestañas esta compuesta por dos componentes diferenciados, las pestañas en si, que vienen definidas por una serie de etiquetas tab dentro de un elemento tabs y el contenido de las pestañas que se define dentro de tabpanels. Dentro de tabpanels habrá tantos elementos como pestañas definamos en tabs (esto no es necesariamente cierto pero vamos a simplificar) y se seguirá una correspondecia por orden de declaración a la hora de asignar el contenido que corresponde a cada pestaña. En este caso, por ejemplo, en la pestaña 1 tenemos un elemento radiogroup compuesto por varios elementos radio, en la pestaña 2 tenemos una serie de checkboxes que agrupamos en un box para que sea un solo elemento y en la tercera pestaña tenemos una imagen.

Veamos ahora los otros widgets. En primer lugar, en la primera pestaña tenemos radiobuttons, que representan opciones excluyentes. Un grupo de radiobuttons se define con radiogroup y cada uno de los radiobuttons con radio, como podemos ver. Los checkboxes son opciones no excluyentes que se definen con la etiqueta checkbox. Por último las imágenes se definen con la etiqueta image que tiene como atributo la ruta a la imagen (src de source, fuente).

Página 25 de 26« Primero...2223242526