Feed a
Entradas
Comentarios
hacker emblem

OCS Inventory es una fantástica aplicación que recopila información sobre el hardware y software de equipos que hay en la red que ejecutan el programa de cliente OCS ("agente OCS de inventario"). Esta información la envían los agentes al servidor de inventariado. OCS puede utilizarse para visualizar el inventario a través de una interfaz web.

OCS Inventory ofrece la posibilidad de, además de realizar un inventariado, desplegar software en forma de scripts Bash en el equipo donde se ejecuta el agente.

El modus operandi sería el siguiente:

  1. El agente envía un inventario al servidor OCS
  2. El servidor procesa el inventario y comprueba si hay paquetes de software asignados para el despliegue en el equipo que ejecuta el agente que envía dicho inventario
  3. Si existen paquetes asignados a este agente, el servidor de comunicación y/o despliegue lo envía al agente mediante protocolo SSL
  4. El agente realiza un handshake con el servidor, descarga el paquete y lo ejecuta. Una vez ejecutado, devuelve un código que puede ser de éxito o de error. Se adjunta a continuación la tabla completa de casos de error y éxito.
  5. Código de estado Significado
    WAITING NOTIFICATION El servidor está esperando comunicación del agente para notificar que hay algo para descargar.
    NOTIFIED El agente ha sido notificado sobre un paquete a descargar. Esperando el código de resultado.
    SUCCESS [code] El agente ha descargado exitosamente un paquete y ha ejecutado comandos o ha almacenado datos extraídos. Con la acción "Lanzar", este estado podría completarse con el código de retorno de la ejecución.

    ERR_ALREADY_SETUP El paquete ya fue instalado previamente en este equipo.
    ERR_BAD_ID El agente es incapaz de descargar un paquete porque no puede encontrar la ID Del mismo en el servidor de despliegue.
    ERR_BAD_DIGEST Los datos descargados tienen un mal digest, por lo que el agente no ejecuta comandos asociados.
    ERR_DOWNLOAD_PACK El agente fue incapaz de descomprimir el fichero ZIP o TAR.GZ descargado.
    ERR_BUILD El agente fue incapaz de reconstruir los fragmentos del paquete.
    ERR_EXECUTE El agente fue incapaz de ejecutar el comando asociado al paquete.
    ERR_CLEAN El agente fue incapaz de limpiar el paquete descargado.
    ERR_TIMEOUT El agente fue incapaz de descargar durante el período DOWNLOAD_TIMEOUT.
    ERR_ABORTED El usuario canceló la ejecución del comando del paquete (ha escogido notificarlo, y le ha permitido cancelarlo).
    ERR_EXECUTE_PACK No utilizado

OCS Inventory está formado por las siguientes partes:

  1. Agentes instalados en sistemas Windows o GNU/Linux: programas escritos en PERL
  2. Un servidor de gestión que se compone de:
    1. Servidor de comunicación: servidor escrito en PERL que recibe los inventariados de los agentes y los almacena en la base de datos.
    2. Servidor de despliegue: se encarga de desplegar software en los equipos agentes. Puede ser el mismo que el servidor de comunicación. Obligatorio Apache con SSL activado.
    3. Consola de administración: interfaz Web escrita en PHP y visible mediante navegador. Lee directamente de la base de datos.
  • Instalación completa de OCS Inventory

En primer lugar debemos instalar tanto el agente como el servidor de comunicación, de despliegue y la consola de administración.

Lo ideal es que tengamos instalado un servidor Apache preparado y los módulos de PERL para los agentes y el servidor de comunicación. Instalamos lo necesario:

BASH:
  1. neonigma@neonigma-desktop:~$ sudo apt-get install apache2 php5 perl mysql-server-5.1 libphp-pclzip php5-gd libapache2-mod-perl2 php5-mysql libxml-simple-perl libcompress-zlib-perl libdbi-perl libdbd-mysql-perl libapache-dbi-perl libnet-ip-perl libsoap-lite-perl

Ahora comenzamos instalando un agente en cualquier equipo que vaya a realizar un inventariado y enviarlo al servidor:

BASH:
  1. neonigma@neonigma-desktop:~$ sudo apt-get install ocsinventory-agent

Continuamos con la consola de administración, que no la vamos a instalar de los repositorios porque suele dar algún que otro problema en la configuración:

  • Descargamos la consola de administración Web:
BASH:
  1. neonigma@neonigma-desktop:~$ wget http://launchpad.net/ocsinventory-server/stable-1.3/1.3.2/+download/OCSNG_UNIX_SERVER-1.3.2.tar.gz

  • Lo movemos a nuestro servidor Apache:
BASH:
  1. neonigma@neonigma-desktop:~$ sudo mv OCSNG_UNIX_SERVER-1.3.2.tar.gz /var/www

  • Lo descomprimimos:
BASH:
  1. neonigma@neonigma-desktop:~$ sudo tar xvzf OCSNG_UNIX_SERVER-1.3.2.tar.gz

  • Entramos en el directorio:
BASH:
  1. neonigma@neonigma-desktop:~$ cd OCSNG_UNIX_SERVER-1.3.2

  • Comenzamos la instalación:
BASH:
  1. neonigma@neonigma-desktop:~$ sudo ./setup.sh

No voy a detallar el proceso de instalación, está bien explicado aquí. Sólo voy a poner unos incisos a la explicación anterior, leedlos antes de seguirla y tenerlos en cuenta.

No os preocupéis si veis mensajes como:

BASH:
  1. Checking for SOAP::Lite PERL module...
  2.    *** Warning: PERL module SOAP::Lite is not installed !
  3.    This module is only required by OCS Inventory NG SOAP Web Service.
  4.    Do you wish to continue ([y]/n] ?
  5.    Checking for XML::Entities PERL module...
  6.    *** Warning: PERL module XML::Entities is not installed !
  7.    This module is only required by OCS Inventory NG SOAP Web Service.
  8.    Do you wish to continue ([y]/n] ?

Esto sólo se utiliza para el servicio Web (experimental y para desarrolladores), y no influye para el despliegue de software en los agentes.

En todo caso, debemos tener cuidado en estas respuestas:

BASH:
  1. Which host is running database server [localhost] ?127.0.0.1
  2.    OK, database server is running on host 127.0.0.1 ;-)
  3.    
  4.    Where to copy Administration Server static files for PHP Web Console
  5.    [/usr/share/ocsinventory-reports] ?/var/www/ocsinventory-reports
  6.    OK, using directory /var/www/ocsinventory-reports to install static files ;-)
  7.    
  8.    Where to create writable/cache directories for deployement packages and
  9.    IPDiscover [/var/lib/ocsinventory-reports] ?/var/www/ocsinventory-reports

En los dos últimos casos casos, contestaré /var/www/ocsinventory-reports para almacenar tanto el servidor como los paquetes de despliegue en esa ruta.

Si tenemos problemas en la instalación, por ej. se sale de la misma por no estar los módulos PERL instalados, los instalamos a mano:

BASH:
  1. perl -MDBD::mysql -e 1
  2.  
  3. sudo perl -MCPAN -e shell
  4. # si nos pregunta, pulsamos INTRO
  5. install YAML
  6. install Apache::DBI
  7. install DBD::mysql
  8. install Compress::Zlib # si nos pregunta, pulsamos INTRO
  9. install XML::Simple
  10. install Net::IP

Continuamos con la instalación del servidor de comunicación desde los repositorios. Esto lo vamos a hacer en la máquina servidora.

BASH:
  1. neonigma@neonigma-desktop:~$ sudo apt-get install ocsinventory-server

A partir de aquí tenemos un servidor OCS Inventory totalmente funcional.

Ahora, en la o las máquinas que vayan a utilizarse como clientes (puede ser la misma también si se quiere), instalaremos el agente de OCS Inventory (en el método de instalación escogemos http y en el nombre 127.0.0.1):

BASH:
  1. neonigma@neonigma-desktop:~$ sudo apt-get install ocsinventory-agent

desde el que podemos mandar inventarios con la siguiente orden:

BASH:
  1. neonigma@neonigma-desktop:~$ sudo ocsinventory-agent --server ip.ip.ip.ip

Sin embargo, lo que nos ocupa aquí es el poder desplegar software en los agentes, es decir, que cuando un agente envíe un inventariado, compruebe en el servidor de despliegue si hay paquetes para él. En caso afirmativo, el agente descargará el paquete tar.gz, lo descomprimirá y ejecutará el script que lleve dentro el fichero comprimido.

Para esto, vamos a utilizar el mismo equipo servidor en el que está instalado el servidor de comunicación en PERL, activando en éste el soporte SSL y convirtiéndolo también en servidor de despliegue.

Vamos a ejecutar los siguientes tres comandos para activar el soporte SSL en Apache:

BASH:
  1. neonigma@neonigma-desktop:~$ sudo a2enmod ssl
  2. Enabling module ssl.
  3. See /usr/share/doc/apache2.2-common/README.Debian.gz on how to configure SSL and create self-signed certificates.
  4. Run '/etc/init.d/apache2 restart' to activate new configuration!
  5.  
  6. neonigma@neonigma-desktop:~$ sudo a2ensite default-ssl
  7. Enabling site default-ssl.
  8. Run '/etc/init.d/apache2 reload' to activate new configuration!
  9.  
  10. neonigma@neonigma-desktop:~$ sudo /etc/init.d/apache2 restart

Ahora nos vamos a apoyar del excelente tutorial de Vicente Navarro en este post. Concretamente, necesitamos sólo la parte que generará un certificado cacert.pem, esta parte está nombrada en el tutorial como Crear un certificado firmado por nuestra propia autoridad certificadora y haremos todos los pasos que se indican hasta llegar a la parte Cuidado con ponerle contraseña a la clave privada

Donde pone y cambiar la configuración del sitio para que lo use:, se refiere al archivo /etc/apache2/sites-enabled/default-ssl, donde tenemos que cambiar las rutas de SSLCertificateFile y SSLCertificateKeyFile.

Una vez generado el certificado cacert.pem firmado por nuestra propia autoridad certificadora, reiniciado el servidor (nos pedirá el password proporcionado al generar el certificado) ya podemos instalar la consola de administración accediendo por primera vez a nuestro servidor SSL:

BASH:
  1. https://localhost/ocsreports/install.php

Seguimos los pasos para configurar la base de datos y listo. Una vez terminado el proceso, vamos a:

BASH:
  1. https://localhost/ocsreports/

y entramos con usuario admin y contraseña admin

A partir de aquí podemos realizar nuestro primer inventariado con cualquier agente (sudo ocsinventory-agent --server 127.0.0.1), nos vamos al directorio del servidor /var/lib/ocsinventory-agent/http:__127.0.0.1_ocsinventory y copiamos allí el fichero cacert.pem.

BASH:
  1. neonigma@neonigma-desktop:/var/lib/ocsinventory-agent/http:__127.0.0.1_ocsinventory$ sudo cp /usr/lib/ssl/misc/demoCA/cacert.pem .

En este punto, ya podemos crear un paquete con un script para desplegar en cualquier agente.


  • Creación y despliegue de scripts

Vamos a habilitar mediante la consola de administración el despliegue o distribución de software. Para ello vamos al menú de configuración:

y activamos la opción correspondiente, bajando además el tiempo de ciclo de latencia que es algo elevado:

Ahora vamos a crear un script cualquiera, lo vamos a asignar a un equipo en concreto (en el que se ejecuta un agente) y vamos a hacer la prueba de despliegue. El script que vamos a utilizar puede ser este mismo, lo llamamos myscript por ejemplo:

BASH:
  1. #!/bin/bash
  2. echo "¡¡ESTOY EJECUTANDO SOFTWARE DESPLEGADO CON OCS INVENTORY!!";
  3. echo "Información sobre tu CPU";
  4. lscpu
  5. sudo apt-get install teeworlds --assume-yes
  6. echo "¡¡JUEGO INSTALADO, A DISFRUTAR DE LAS VACACIONES!!";

Vamos a comprimir este script:

BASH:
  1. neonigma@neonigma-desktop:~$ tar cvzf myscript.tar.gz myscript

Y ahora vamos a crear el paquete software en la consola de administración de OCS Inventory. Vamos a la Web y escogemos la opción Crear del menú Distribución software.

Introducimos los datos del nuevo paquete, indicando que tenemos un fichero comprimido myscript.tar.gz y que lanzaremos un script que tiene en su interior llamado myscript con prioridad 5 (podemos ponerle prioridad 0 que es prioridad absoluta).

Si al aceptar nos da el mensaje de error ERROR: can't create or write in /var/lib/ocsinventory-reports/download/xxxxxxxxx folder, please refresh when fixed. (or try disabling php safe mode), tenemos que cambiar estas rutas en la base de datos. Podemos hacerlo de la siguiente manera:

BASH:
  1. neonigma@neonigma-desktop:~$ mysql -u root -p
  2. mysql> UPDATE config SET TVALUE="/var/www/ocsinventory-reports" WHERE NAME="DOWNLOAD_PACK_DIR"
  3. mysql> UPDATE config SET TVALUE="/var/www/ocsinventory-reports" WHERE NAME="IPDISCOVER_IPD_DIR"

y reiniciamos el proceso, creando de nuevo el paquete.

A continuación dejamos los siguientes datos como están porque nuestro paquete no es muy grande y no queremos fragmentar su envío. Pulsamos en Enviar y el paquete queda creado.

Realizamos ahora el proceso de activación del paquete. Escogemos la opción activar del menú Distribución software.

Nos vamos a la fila que muestra el paquete recién creado y pulsamos en el botón Activar.

Dejamos marcada Activación manual y especificamos como URL https y como Partes URL la misma cadena https://127.0.0.1/ocsinventory-reports/download

Al aceptar, se nos informa de que el paquete ya se ha activado y lo podemos afectar. Aquí, afectar un paquete se refiere a asignarlo a un equipo o a un conjunto de ellos.

El último paso es asignar o afectar un paquete a un equipo. Para ello escogemos un equipo de la lista de los que tenemos inventariados:

Pulsamos en la opción Personalizar de la lista de opciones disponibles para un equipo concreto:

Pulsamos en adicionar paquete:

De la lista de paquetes, localizamos la fila del paquete deseado y pulsamos en el botón Afectar. A la pregunta de si estamos seguros, respondemos que sí.

Podemos ver que el paquete queda asignado al equipo y esperando notificación de envío.

Ahora la ejecución de un inventariado por parte del agente instalado en el equipo afectado, provocará que éste se descargue del servidor el paquete disponible para él. Lo vemos:

BASH:
  1. neonigma@neonigma-desktop:~$ sudo ocsinventory-agent --debug --info --logfile logfile.log
  2. [sudo] password for neonigma:
  3. DOWNLOAD: Writing config file.
  4. DOWNLOAD: Making working directory for 1281884908.
  5. DOWNLOAD: Retrieving info file for 1281884908
  6. DOWNLOAD: Initialize ssl layer...
  7. DOWNLOAD: Connect to server: 127.0.0.1/ocsinventory-reports/download...
  8. DOWNLOAD: Starting SSL connection...
  9. DOWNLOAD: Info file:
  10.  
  11. DOWNLOAD: Success. :-)
  12. DOWNLOAD: Beginning work. I am 26331.
  13. DOWNLOAD: Checking timeout for 1281884908... OK
  14. DOWNLOAD: New period. Nb of cycles: 10
  15. DOWNLOAD: Now pausing for a cycle latency => 5 seconds
  16. DOWNLOAD: Now pausing for a cycle latency => 5 seconds
  17. DOWNLOAD: Now pausing for a cycle latency => 5 seconds
  18. DOWNLOAD: Now pausing for a cycle latency => 5 seconds
  19. DOWNLOAD: Downloading 1281884908-1
  20. ...
  21. DOWNLOAD: Success :-)
  22. DOWNLOAD: Now pausing for a fragment latency => 10 seconds
  23. DOWNLOAD: Now pausing for a cycle latency => 5 seconds
  24. DOWNLOAD: Now pausing for a cycle latency => 5 seconds
  25. DOWNLOAD: Now pausing for a cycle latency => 5 seconds
  26. DOWNLOAD: Now pausing for a cycle latency => 5 seconds
  27. DOWNLOAD: Now pausing for a cycle latency => 5 seconds
  28. DOWNLOAD: Download of 1281884908... Finished.
  29. DOWNLOAD: Execute orders for package 1281884908.
  30. DOWNLOAD: Building package for 1281884908.
  31. DOWNLOAD: Checking signature for ./1281884908/tmp/build.tar.gz.
  32. DOWNLOAD: Digest format: Hexadecimal
  33. DOWNLOAD: Digest algo: MD5
  34. DOWNLOAD: Digest OK...
  35.  
  36. => retrieving tar...
  37. => tar is at /bin/tar
  38.  
  39. myscript
  40. DOWNLOAD: Building of 1281884908... Success.
  41. DOWNLOAD: Launching myscript...
  42. ¡¡ESTOY EJECUTANDO SOFTWARE DESPLEGADO CON OCS INVENTORY!!
  43. Información sobre tu CPU
  44. Architecture:          i686
  45. CPU op-mode(s):        64-bit
  46. CPU(s):                4
  47. Thread(s) per core:    1
  48. Core(s) per socket:    4
  49. CPU socket(s):         1
  50. Vendor ID:             AuthenticAMD
  51. CPU family:            16
  52. Model:                 4
  53. Stepping:              3
  54. CPU MHz:               800.000
  55. Virtualization:        AMD-V
  56. L1d cache:             64K
  57. L1i cache:             64K
  58. L2 cache:              512K
  59. L3 cache:              6144K
  60. Reading package lists... Done
  61. Building dependency tree      
  62. Reading state information... Done
  63. The following extra packages will be installed:
  64.   teeworlds-data
  65. Suggested packages:
  66.   teeworlds-server
  67. The following NEW packages will be installed:
  68.   teeworlds teeworlds-data
  69. 0 upgraded, 2 newly installed, 0 to remove and 5 not upgraded.
  70. Need to get 5288kB of archives.
  71. After this operation, 8364kB of additional disk space will be used.
  72. Get:1 http://mirrors.nfsi.pt/ubuntu/ lucid/universe teeworlds-data 0.5.1-3ubuntu1 [5114kB]
  73. Get:2 http://mirrors.nfsi.pt/ubuntu/ lucid/universe teeworlds 0.5.1-3ubuntu1 [174kB]
  74. Fetched 5288kB in 4s (1209kB/s)
  75. Selecting previously deselected package teeworlds-data.
  76. (Reading database ... 274481 files and directories currently installed.)
  77. Unpacking teeworlds-data (from .../teeworlds-data_0.5.1-3ubuntu1_all.deb) ...
  78. Selecting previously deselected package teeworlds.
  79. Unpacking teeworlds (from .../teeworlds_0.5.1-3ubuntu1_i386.deb) ...
  80. Processing triggers for man-db ...
  81. Processing triggers for desktop-file-utils ...
  82. Processing triggers for python-gmenu ...
  83. Rebuilding /usr/share/applications/desktop.C.cache...
  84. Processing triggers for python-support ...
  85. Setting up teeworlds-data (0.5.1-3ubuntu1) ...
  86. Setting up teeworlds (0.5.1-3ubuntu1) ...
  87. ¡¡JUEGO INSTALADO, A DISFRUTAR DE LAS VACACIONES!!
  88. DOWNLOAD: Package 1281884908... Done. Sending message...
  89. DOWNLOAD: Sending message for 1281884908, code=SUCCESS_0.
  90. DOWNLOAD: Cleaning 1281884908 package.
  91. unlink 1281884908/task_done
  92. unlink 1281884908/done
  93. unlink 1281884908/since
  94. unlink 1281884908/tmp/myscript
  95. rmdir tmp
  96. unlink 1281884908/info
  97. unlink 1281884908/task
  98. unlink 1281884908/1281884908-1
  99. rmdir 1281884908
  100. DOWNLOAD: Now pausing for a fragment latency => 10 seconds
  101. DOWNLOAD: Now pausing for a cycle latency => 5 seconds
  102. DOWNLOAD: No more package to download.
  103. DOWNLOAD: End of work...



REFERENCIAS
http://www.ocsinventory-ng.org/
http://es.wikipedia.org/wiki/OCS_Inventory
http://wiki.intropedro.com/index.php?title=Instalar_el_servidor_de_ocs_inventory
http://www.vicente-navarro.com/blog/2009/02/22/crear-los-certificados-ssl-para-nuestro-servidor-web-https-con-apache-openssl-y-debian-lenny

A 1 persona le gusta esta entrada

Me dejo aquí un pequeño tip que tengo que buscar cada vez que tengo movimiento en mi netbook con Fedora.

a) wget http://mirror2.openwrt.org/sources/broadcom-wl-4.150.10.5.tar.bz2
b) tar xjf broadcom-wl-4.150.10.5.tar.bz2
c) cd broadcom-wl-4.150.10.5/driver
d) b43-fwcutter -w /lib/firmware wl_apsta_mimo.o

Fuente: http://hp2133.umsw.de/

A 1 persona le gusta esta entrada

Existen varios bugs reconocidos en la generación y uso del WSDL en soaplib v8.1. Los clientes Soapui y Axis no van a aceptar el WSDL generado por soaplib.

El primer bug se reconoce aquí: http://github.com/jkp/soaplib/issues/#issue/12

Está solucionado en el trunk de soaplib, editamos el fichero /usr/local/lib/python2.6/dist-packages/soaplib-0.8.1-py2.6.egg/soaplib/serializers/primitive.py y cambiamos una línea que nos indican aquí:

http://github.com/jkp/soaplib/commit/1dd0aa6e01ebb04a7c802f261c93534061de8b7d

El segundo bug visto en este enlace da una salida como ésta al acceder al servicio Web:

FutureWarning: The behavior of this method will change in future versions. Use specific ‘len(elem)’ or ‘elem is not None’ test instead.

Este bug provoca errores al utilizar el cliente Java desde Axis. En este caso buscaríamos dos líneas del fichero wsgi_soap.py con la siguiente sentencia:

PYTHON:
  1. if payload:

las dos líneas vamos a cambiarlas por:

PYTHON:
  1. if payload is not None:

Una solución a mano es modificar los ficheros primitive.py y wsgi_soap.py y compilarlos con

BASH:
  1. python -mcompileall .

una vez dentro de cada ruta de los ficheros mencionados (es decir, dos veces en total).

Soaplib genera entonces un WSDL correcto, que Axis acepta perfectamente. Axis podrá también acceder sin problemas al servicio Web.

La mejor solución es generar un egg nuevo para la instalación con easy_install.

Pasos:

1. Nos bajamos el código de soaplib de aquí, por ejemplo en ~/Descargas.
2. Modificamos el fichero primitive.py y el fichero wsgi_soap.py
3. Generamos un nuevo egg:

BASH:
  1. python setup.py bdist_egg

Esto crea un nuevo fichero en ~/Descargas/soaplib-0.8.1/dist/soaplib-0.8.1-py2.6.egg

4. Eliminamos todas las referencias al egg del soaplib antiguo, así como a sus ficheros:

BASH:
  1. easy_install -mxN PackageName

5. También a sus ficheros:

BASH:
  1. rm -rf /path.to/python2.6/site-packages/soaplib-0.8.1-py2.6.egg

6. Podemos instalar el nuevo egg:

BASH:
  1. easy_install ~/Descargas/soaplib-0.8.1/dist/soaplib-0.8.1-py2.6.egg



Podemos probar wsdl2java descargando e instalando Axis:

1. Descargar Axis:

BASH:
  1. wget http://apache.rediris.es/ws/axis2/1_5_1/axis2-1.5.1-bin.zip

2. Descomprimir Axis:

BASH:
  1. unzip axis2-1.5.1-bin.zip

3. Accedemos a las utilidades de Axis:

BASH:
  1. cd axis2-1.5.1/bin

4. Establecer variable JAVA_HOME:

BASH:
  1. export JAVA_HOME=/path/to/jvm/jre

5. Lanzar wsdl2java contra nuestro servidor ejecutando:

BASH:
  1. ./wsdl2java.sh -uri http://127.0.0.1:8080/ws?wsdl

A 1 persona le gusta esta entrada

Para un trabajo grupal de un curso de LPIC1 que estoy realizando necesitaba insertar una bibliografía al finalizar cada capitulo, y no quería dejar de utilizar Flexbib para BibTeX por las virtudes que ya comenté en esta entrada. Entre otras cosas, Flexbib permite:

  • Un traducción de las partículas and, in, editors, etc. de acuerdo con el idioma seleccionado. Este es el punto más importante, puesto que podemos tener partículas "y", "en", "editores" para nuestro idioma.
  • Seleccionar un modelo de cita numérico o un modelo autor-año sin necesidad de hacer cambios en el argumento de \bibliographystyle bibliografía
  • Cuando el número de autores es superior a dos puede elegirse si se utilizan todos los autores o sólo el primero seguido del grupo «y otros», o lo que corresponda según el idioma en el modelo de citas autor-año.
  • Es posible seleccionar el tipo de letra utilizado para los autores y los títulos
  • Las bases de datos añaden a los campos estándard nuevos campos como abstract, comment, url, isbn, mrnumber, translator que son gestionados por flexbib.

Para poder tener una bibliografía al finalizar cada capítulo, hacemos uso del paquete bibtopic. Lo primero que vamos a hacer es hacer una copia de los .bib necesarios y guardarlos también en otra ubicación, por si acaso. En nuestro ejemplo, vamos a tener dos capítulos y por tanto, dos .bib, uno llamado bibliodebian.bib y otro llamado biblioubuntu.bib. Ahora eliminamos todos los intentos de creación de varias bibliografías en nuestro directorio de trabajo:

BASH:
  1. rm *.aux
  2. rm *.bbl
  3. rm *.blg

Añadimos bibtopic a la lista de paquetes a usar:

LATEX:
  1. \usepackage{bibtopic}

Piponazo me informa que este paquete tiene dependencias del paquete texlive-bibtex-extra, yo lo tendría ya instalado, pero el que no lo tenga, debe hacerlo:

BASH:
  1. sudo apt-get install texlive-bibtex-extra

En mi caso, yo estructuro cada capitulo con su \chapter, \section y \subsection variados dentro de un \include{fichero}. Antes de estos \include vamos a definir el estilo de bibliografía a flexbib:

LATEX:
  1. \bibliographystyle{flexbib}

Seguidamente, es donde viene lo importante, nuestro \include{fichero} que contiene el cuerpo del primer capítulo, lo vamos a encerrar entre el siguiente código:

LATEX:
  1. \begin{btSect}{bibliodebian} % Aquí es donde se le indica el primer fichero .bib
  2. \include{debian} % Aquí tenemos nuestro primer contenido con \chapter, \section, \subsection y demás
  3. \btPrintAll % Aquí le estamos diciendo que imprima todas las entradas de bibliodebian.bib, aunque no estén citadas
  4. \end{btSect} % Fin del código que engloba la primera bibliografía

Y análogamente, para el segundo capítulo:

LATEX:
  1. \begin{btSect}{biblioubuntu} % Aquí es donde se le indica el segundo fichero .bib
  2. \include{ubuntu} % Aquí tenemos nuestro segundo contenido con \chapter, \section, \subsection y demás
  3. \btPrintAll % Aquí le estamos diciendo que imprima todas las entradas de biblioubuntu.bib, aunque no estén citadas
  4. \end{btSect} % Fin del código que engloba la segunda bibliografía

En lugar de que se imprima en cada bibliografía de cada capítulo todas las entradas del .bib asociado, podemos desear que se impriman sólo las que han sido citadas. En este caso en lugar de \btPrintAll utilizaremos el comando \btPrintCited.

El siguiente paso es compilar con PDFLaTeX el código del documento. Si usamos una GUI como Kile, se nos informará de lo siguiente:

"Package bibtopic Warning: Please (re)run BibTeX on the file(s):

(bibtopic) principal1
(bibtopic) principal2
(bibtopic) and after that rerun LaTeX."

Para hacer lo que se nos pide, nos movemos al directorio de trabajo y escribimos en una consola:

BASH:
  1. bibtex principal1
  2. bibtex principal2

Una vez hecho esto, recompilamos con PDFLaTeX y ahora sí, cada uno de los dos capítulos tendrá al final su bibliografía.

Enlace: Airminded

A 1 persona le gusta esta entrada

Para la formalización de ciertos proyectos, se pide "redactar" un manual de código en el que aparezcan todos los ficheros que componen la aplicación.

Si utilizáis LaTeX, he creado un script que te permite crear el manual de código de la aplicación con sólo llamarlo con los parámetros adecuados.

Deciros que el esqueleto del script, la parte que recorre directorios y ficheros, está basado en el script Arbol de directorios de Paco Debian.

El código del script es el siguiente:

BASH:
  1. #!/bin/bash
  2. #set -e
  3. #set -u
  4.  
  5. #set -x
  6.  
  7. func_ficheros()
  8. {
  9.     listaficheros=$(find -maxdepth 1 -type f -iname "*."$extpar | sort) # extraer lista ordenada de ficheros del directorio actual
  10.  
  11.     for item in $listaficheros # recorrer lista de ficheros
  12.     do
  13.         extfich=${item##*.} # obtener extension del fichero
  14.         if [ $extfich = $extpar ] # si la extension es la misma que especificamos en $2
  15.         then
  16.             fichero=${item##*"./"} # eliminar todos los "./" que coloca find
  17.             fichero=${fichero//"_"/"\_"} # reemplazar todos los "_" por "\_", LaTeX trata "_" como error
  18.             echo "\section{Fichero "$fichero"}" # creamos la seccion con el nombre del fichero
  19.             echo -e "\lstinputlisting{"$PWD"/"${item##*"./"}"}\n" # incluimos el fichero con ruta completa y con "_" porque es ruta fisica
  20.         fi
  21.     done
  22. }
  23.  
  24.  
  25. func_recursiva()
  26. {
  27.     for OBJ in * # recorrer el directorio $1
  28.     do
  29.         (
  30.         if [ -d "${OBJ}" ] # es un directorio
  31.         then
  32.             cd "${OBJ}" # cambiar a este directorio
  33.             var=$(find -maxdepth 1 -type f -iname "*."$extpar | wc -l) # ver si hay en el directorio actual ficheros de tipo $2
  34.            
  35.             if [ $var != "0" ] # si hay ficheros de tipo $2
  36.             then
  37.                 nombrecarpeta=${PWD##*$raiz_pwd} #extraer solo el nombre de la carpeta
  38.                 carpeta=${nombrecarpeta//"_"/"\_"} # reemplazar todos los "_" por "\_", LaTeX trata "_" como error
  39.                 echo -e "\n" # retorno de carro
  40.                 echo "\chapter{Carpeta "$dirpar$carpeta"}" # crear nueva subseccion con esta subcarpeta
  41.  
  42.                 func_ficheros # procesar los ficheros de este directorio
  43.             fi
  44.             func_recursiva # seguir mirando subcarpetas
  45.         fi
  46.         )
  47.     done
  48. }
  49.  
  50. #################
  51. #          INICIO            #
  52. #################
  53.  
  54. if [ $# != 3 ] # control de errores - utilizar los tres parametros: nombrecarpeta, extensionfichero y ficherolatex
  55. then
  56.     echo -e "ERROR: Incorrecto numero de argumentos\n"
  57.     echo "Uso del programa: ./myscript nombrecarpeta extensionfichero ficherolatex"
  58.     echo "Ejemplo: ./myscript micarpeta php codigo.tex"
  59.     exit 0
  60. fi
  61.  
  62. dirpar=$1 # obtener el parametro de carpeta raiz, en func_recursiva se pierde la visibilidad del parametro
  63. extpar=$2 # obtener el parametro de extension a buscar, en func_recursiva se pierde la visibilidad del parametro
  64.  
  65. exec 1>$3 # enlazar salida de echo a fichero pasado como parametro $3
  66.  
  67. cd $1 # entrar al directorio especificado en $1
  68. raiz_pwd=$PWD # guardar la raiz de este directorio, la perderemos al recorrer el arbol en func_recursiva
  69. echo "\chapter{Carpeta "$1"}" # crear la carpeta raiz como capitulo
  70.  
  71. func_ficheros # procesar directorio raiz
  72. func_recursiva # llamar a func_recursiva para exploracion completa del directorio

Como veis, cada línea está bien comentada para que no haya ningún problema de comprensión.

La ejecución del script se realiza de la siguiente manera:

./myscript nombrecarpeta extensionfichero ficherolatex

Si por ejemplo queremos realizar un manual de código del conocido CMS Drupal, deberemos llamar al script de la siguiente forma:

neonigma@neonigma-laptop:/opt/lampp/htdocs$ ./myscript drupal php codigo.tex

En líneas generales, el script recorre la carpeta drupal de nuestro servidor Web buscando ficheros con extensión PHP y genera un fichero de LaTeX con la siguiente estructura:

\chapter{Carpeta o subcarpeta}
\section{Sección para fichero .php encontrado}
\lstinputlisting{ruta al fichero php, que provocará el listado de código del mismo}

  • El pdf resultante del proyecto LaTeX puede descargarse haciendo clic aquí.
  • Podéis descargar el script pulsando aquí.
  • El ejemplo de proyecto LaTeX, que incluye el fichero codigo.tex generado automáticamente por el script, puede descargarse pulsando aquí.
A 5 personas les gusta esta entrada

Seguro que a muchos os ha tocado la tediosa tarea de documentar una aplicación. Para la parte técnica, incluso debemos preparar imágenes de las tablas de la base de datos. Pues bien, no para que la máquina realice el 100% del trabajo, pero sí para ahorrarnos mucho pero que mucho curro, mi buen amigo Joaquín Gracia aka Kubiat se curró este script en PHP que me he encargado de modificar a mi gusto y de complementar con otro script en Bash.

Tenemos, por tanto, el siguiente material:

* Script en PHP que muestra en una página Web todas las tablas extraídas de la base de datos y que, además, ha sido modificado para obtener cada tabla en un fichero HTML independiente. Más o menos lo que muestra la siguiente imagen:

La visita a la página Web crea cada tabla de la base de datos en un fichero HTML, como puede verse en el listado por consola:

* Script en Bash que recoge cada fichero HTML del directorio de trabajo y lo convierte a imagen JPG, como muestra la siguiente imagen:

Para utilizar el script en PHP, basta con acceder a la dirección Web local donde lo hayamos colocado. Este script sólo necesita modificar las variables de acceso a la base de datos de la función conecta. En el caso de la imagen, la dirección Web a la que se accede es http://localhost/jscript/tablas.php

Para utilizar el script en Bash, le damos permisos de ejecución con chmod +x nscript y lo ejecutamos (importante que sea dentro de este directorio de trabajo) escribiendo ./nscript

El código de este script es tan sencillo como esto:

BASH:
  1. #!/bin/bash
  2. #
  3. # Script para la obtencion de imagenes a partir de HTMLs
  4.  
  5. sudo chmod 777 *.html
  6. archivos=$(ls *.html)
  7.  
  8. for lista in $archivos
  9. do
  10.     convert -verbose -density 600x600 -trim +repage $lista $lista.jpg
  11.     rm $lista
  12. done

Lo que significa que el script tomará todos los archivos html de la carpeta, y uno a uno los convertirá a jpg con una densidad de página de 600x600, recortando los espacios en blanco sobrantes y paginando en varias imágenes si no cupiera en una. Después de esta conversión, se borra el fichero html que ya deja de sernos útil.

En el ejemplo hemos utilizado una instalación de Drupal para extraer todas las tablas de la base de datos y convertirlas a imágenes. La muestra de la lista de imágenes resultantes podemos verla en la siguiente imagen:

Obviamente podemos ver que el texto que acompaña a cada campo es demasiado genérico, de ahí a que no todo el trabajo lo realice la máquina. Nosotros podemos, en el paso de la generación de los archivos html, modificar cada campo de cada tabla en los ficheros html para que la descripción se ajuste a lo que queremos. Una vez terminadas las descripciones, lanzaríamos el script en bash y obtendríamos las imágenes automáticamente.

Recordar que este script borra cada fichero HTML, por lo que deberemos guardar en una carpeta los ficheros html por si luego queremos retocar alguna descripción, o eliminar la línea del script en bash que borra cada fichero html.

Descarga el script PHP aquí.
Descarga el script en Bash aquí.

Si la imagen resultante de alguno de los ficheros html nos resulta demasiado grande o demasiado dividida (por ejemplo, la tabla comments de Drupal se ha dividido en dos imágenes), podemos utilizar otro script para conversión manual de un único fichero html.

Este script contiene el siguiente código:

BASH:
  1. #!/bin/bash
  2. #
  3. # Script para la obtencion de imagenes a partir de HTMLs
  4. # Uso: ./xscript 0.55 nombre-de-pagina-html-sin-extension
  5.  
  6. html2ps -U -s $1 -o $2.ps $2.html
  7. convert -density 600x600 -trim +repage $2.ps $2.jpg

La llamada a este script se realiza de la siguiente forma: ./xscript factor-escala nombre-de-pagina-html-sin-extension

Por ejemplo: ./xscript 0.55 comments

Descarga el script manual aquí.

A 1 persona le gusta esta entrada

Ante la inclusión en el Anteproyecto de Ley de Economía sostenible de modificaciones legislativas que afectan al libre ejercicio de las libertades de expresión, información y el derecho de acceso a la cultura a través de Internet, los periodistas, bloggers, usuarios, profesionales y creadores de internet manifestamos nuestra firme oposición al proyecto, y declaramos que…

1.- Los derechos de autor no pueden situarse por encima de los derechos fundamentales de los ciudadanos, como el derecho a la privacidad, a la seguridad, a la presunción de inocencia, a la tutela judicial efectiva y a la libertad de expresión.

2.- La suspensión de derechos fundamentales es y debe seguir siendo competencia exclusiva del poder judicial. Ni un cierre sin sentencia. Este anteproyecto, en contra de lo establecido en el artículo 20.5 de la Constitución, pone en manos de un órgano no judicial -un organismo dependiente del ministerio de Cultura-, la potestad de impedir a los ciudadanos españoles el acceso a cualquier página web.

3.- La nueva legislación creará inseguridad jurídica en todo el sector tecnológico español, perjudicando uno de los pocos campos de desarrollo y futuro de nuestra economía, entorpeciendo la creación de empresas, introduciendo trabas a la libre competencia y ralentizando su proyección internacional.

4.- La nueva legislación propuesta amenaza a los nuevos creadores y entorpece la creación cultural. Con Internet y los sucesivos avances tecnológicos se ha democratizado extraordinariamente la creación y emisión de contenidos de todo tipo, que ya no provienen prevalentemente de las industrias culturales tradicionales, sino de multitud de fuentes diferentes.

5.- Los autores, como todos los trabajadores, tienen derecho a vivir de su trabajo con nuevas ideas creativas, modelos de negocio y actividades asociadas a sus creaciones. Intentar sostener con cambios legislativos a una industria obsoleta que no sabe adaptarse a este nuevo entorno no es ni justo ni realista. Si su modelo de negocio se basaba en el control de las copias de las obras y en Internet no es posible sin vulnerar derechos fundamentales, deberían buscar otro modelo.

6.- Consideramos que las industrias culturales necesitan para sobrevivir alternativas modernas, eficaces, creíbles y asequibles y que se adecuen a los nuevos usos sociales, en lugar de limitaciones tan desproporcionadas como ineficaces para el fin que dicen perseguir.

7.- Internet debe funcionar de forma libre y sin interferencias políticas auspiciadas por sectores que pretenden perpetuar obsoletos modelos de negocio e imposibilitar que el saber humano siga siendo libre.

8.- Exigimos que el Gobierno garantice por ley la neutralidad de la Red en España, ante cualquier presión que pueda producirse, como marco para el desarrollo de una economía sostenible y realista de cara al futuro.

9.- Proponemos una verdadera reforma del derecho de propiedad intelectual orientada a su fin: devolver a la sociedad el conocimiento, promover el dominio público y limitar los abusos de las entidades gestoras.

10.- En democracia las leyes y sus modificaciones deben aprobarse tras el oportuno debate público y habiendo consultado previamente a todas las partes implicadas. No es de recibo que se realicen cambios legislativos que afectan a derechos fundamentales en una ley no orgánica y que versa sobre otra materia.

Este manifiesto, elaborado de forma conjunta por varios autores, es de todos y de ninguno. Si quieres sumarte a él, difúndelo por Internet.

Desde aquí el pequeño granito de arena a intentar hacer un poco de ruido sobre este tema, que ya de por sí es ruidoso y desagradable. Lo que nos faltaba, vamos. Queremos migrar de la industria del ladrillazo al I+D+i y lo hacemos... ¡recortando presupuestos en I+D+i! Cuanto menos, contraproducente.

Un extracto bastante explicativo de la iniciativa surgida en La aldea irreductible

"Decir que España es un país de Ciencia es hacer verdaderos contorsionismos con las palabras... Decir que en España hay grandes científicos es obviar su precaria realidad, pero decir que se busca un modelo productivo basado en la Innovación y reducir a la vez el presupuesto del Ministerio de Ciencia en hasta un 37% podría ser tildado perfectamente de tomadura de pelo. Finalmente, el Tijeretazo estará en torno a un 15%... Inquietante, incluso para miembros del propio Gobierno..."

Leo en el blog Manzana Mecánica esta sorprendente noticia que quería compartir con todo el que desarrolla documentos en LaTeX:

LaTeX es un lenguaje para creación de documentos tremendamente poderoso y ampliamente usado en la academia para escribir artículos científicos, particularmente por lo fácil que es escribir símbolos matemáticos. Sin embargo, la gran variedad de comandos también hace que a veces uno no recuerde cómo describir ciertos símbolos. Detexify te permite que dibujes un símbolo y te indica posibles comandos en LaTeX que describen ese símbolo. ¡Muy útil y totalmente recomendado!

Esta es una imagen de como funciona el tinglado:

detexify

Puedes acceder a Detexify haciendo clic aquí

Fuente: Manzana Mecánica

A 1 persona le gusta esta entrada

Ha sido especialmente tedioso conseguir que para un iframe, se me redimensionara la altura dependiendo del contenido que se cargara dentro de éste. Firefox lo realizaba a la perfección, pero no así Internet Explorer. Tomando un par de referencias me he construido la solución que quiero compartir.

En el archivo que declara el iframe:

JAVASCRIPT:
  1. <script language="javascript">
  2. function grand(h)
  3. {
  4.   iframe=document.getElementById("mi_iframe")
  5.   iframe.height=h;
  6. }
  7. function reSize(h)
  8. {  
  9.     try
  10.   {
  11.     var oBody   =   mi_iframe.document.body;
  12.     var oFrame  =   document.all("mi_iframe");
  13.        
  14.         oFrame.style.height = oBody.scrollHeight + (oBody.offsetHeight - oBody.clientHeight);
  15.         oFrame.style.width = oBody.scrollWidth + (oBody.offsetWidth - oBody.clientWidth);
  16.     }
  17.     catch(e) //An error is raised if the IFrame domain != its container's domain
  18.     {
  19.      window.status =    'Error: ' + e.number + '; ' + e.description;
  20.     }
  21. }
  22. </script>

En el mismo fichero, declaramos el iframe y colocamos esto en el body para IE:

HTML:
  1. <body onLoad="reSize(0)"><iframe id="mi_iframe" src="iframe.php" width="100%" frameborder="0" height="100%"></iframe></body>

En el contenido del iframe (iframe.php en el ejemplo):

JAVASCRIPT:
  1. <script language="javascript">
  2.      function redimensiona()
  3.      {
  4.       var navegador = navigator.appName
  5.       if (navegador == "Microsoft Internet Explorer")
  6.       {
  7.         top.reSize(0);
  8.       }
  9.       else
  10.       {
  11.         principal=document.getElementById("div_principal");
  12.         top.grand(principal.scrollHeight+40);
  13.       }      
  14.      }
  15. </script>

El código HTML del contenido de este iframe sería algo así como:

HTML:
  1. <html><body onLoad="redimensiona()"><table id="div_principal"><tr><td>Incluir contenido de la pagina</td></tr></table></body></html>

Referencias:

A 2 personas les gusta esta entrada

Siguiendo con el meme que proponía CrowDat en este post, hago referencia aquí a uno de los temas que más me marcaron en mi adolescencia, en este caso me alejo de mi vertiente metalera y escojo el tema Principles of Lust de Enigma (grande Michael Cretu).

Y como me recuerda CrowDat, un buen meme que se precie tiene que ser pasado, así que me gustaría que Piponazo recogiera el testigo.

Para los que usamos Lampp y reconocemos como un fastidio usar PHPMyAdmin, existe una solución para que cada vez que se arranca Lampp, se inicie también un socket al demonio de mysql que permita la ejecución de MySQL Query Browser.

Para crear el socket sería necesario hacer esto cada vez que se quiere iniciar Lampp:

$ sudo mkdir /var/run/mysqld
$ cd /var/run/mysqld
$ sudo ln -s /opt/lampp/var/mysql/mysql.sock mysqld.sock

Podemos automatizar este proceso de forma que sólo sería necesario realizarlo una sola vez. Abrimos el archivo /opt/lampp/lampp como root, buscamos el case startmysql y añadimos las tres líneas anteriores al último else, tal y como sigue a continuación:

"startmysql")
if testrun /opt/lampp/var/mysql/`/bin/hostname`.pid mysqld
then
$de && echo "XAMPP: XAMPP-MySQL laeuft bereits."
$de || echo "XAMPP: XAMPP-MySQL is already running."
else
if testport 3306
then
$de && echo "XAMPP: Ein anderer MySQL daemon laeuft bereits."
$de || echo "XAMPP: Another MySQL daemon is already running."
else
/opt/lampp/bin/mysql.server start > /dev/null &
$de && echo "XAMPP: Starte MySQL..."
$de || echo "XAMPP: Starting MySQL..."
sudo mkdir /var/run/mysqld
cd /var/run/mysqld
sudo ln -s /opt/lampp/var/mysql/mysql.sock mysqld.sock
fi
fi

Fuente: Ubuntu Forums

Muy adecuado para mi momento actual. De obligada lectura.

¿Alguna vez habéis querido hacer algo, pero simplemente no estabais suficientemente motivados para hacerlo? Este es siempre mi primer motivo para no pasar a la acción, y estoy seguro de que probablemente también será el vuestro. Si no estáis motivados, simplemente no tenéis la energía o el impulso para hacer lo que tenéis que hacer, ¿verdad? Bastante simple.

Pero aquí hay tres de los mayores problemas que surgen por pretender estar altamente motivados todo el tiempo:

  1. Quizá no te importa realmente lo que estás haciendo. Quizá no importa en realidad y te estás forzando a hacer algo que no quieres hacer. En este caso, tu falta de motivación es tu subconsciente que te dice “esto no es importante” o “esto no se alinea con mis valores.”
  2. La energía llega en oleadas. Y del mismo modo que cada ola tiene su cresta, también tiene una depresión. A veces tu nivel de motivación será como un tsunami arrollador. En otras ocasiones, será un flujo estable. Son ritmos naturales y seguir estos ritmos es importante, porque si no lo haces, te quemarás.
  3. A veces no te animarás antes de pasar a la acción, pero te sentirás bien después de haberlo hecho. Pongamos por ejemplo hacer ejercicio. Mucha gente teme o aborrece hacer el esfuerzo de sudar la camiseta. No están motivados a priori, al menos no lo suficiente para romper la resistencia mental ante el trabajo. Pero se sienten sensacionales cuando han terminado el ejercicio. Por lo tanto, a veces no puedes confiar en tener una alta motivación a priori, a veces hay que confiar en que la motivación llegará después.

¿Quiere esto decir que la motivación no es importante? No, sigue siendo importante y juega su papel. Pero demasiado a menudo resulta fácil caer en la trampa de pensar que hay que estar totalmente entusiasmados por algo antes de hacerlo. No vas a tener siempre un entusiasmo total. Y está bien. Es lo que se llama seguir tu ritmo natural.

El motivo por el cual empecé a considerar este modo diferente de abordar mis objetivos fue porque las soluciones típicas de motivación no funcionaban para mí. A veces funcionaban y otras no. A veces visualizar el éxito era muy motivante y otras simplemente fracasaba tontamente. Sólo cuando me permití dejarme ir, el éxito me llegó de forma más fácil. Lo admito, esto puede parecer difícil al principio, pero es cuestión de confianza. Tienes que confiar en tu auténtico ser para decirte cuando es hora de pasar a la acción y cuando lo es para sólo estar.

Aquí tenemos algunos ejemplos para practicar este modo no-competitivo de manifestar tus deseos:

  1. Practica escuchar tu intuición. Muchos de nosotros tendemos a cuestionar la validez de nuestra intuición. Favorecemos nuestra mente racional y su método de explicación seguro y lógico. Nuestra mente intuitiva, sin embargo es igual de válida. Tal como dijo Einstein una vez “La mente intuitiva es un don sagrado y la mente racional es un sirviente fiel. Hemos creado una sociedad que honra al sirviente y ha olvidado el don.” Aprende a honrar tu don intuitivo practicando el hacerte preguntas y escuchando la respuesta que te das sin intentar deducir la respuesta lógicamente.
  2. Tómate el tiempo de preguntarte qué quieres realmente. A menudo queremos adherirnos a la consecución de nuestros objetivos y olvidamos escuchar lo que realmente queremos. En lugar de crear objetivos como medio de satisfacer nuestros deseos, nos convertimos en esclavos de ellos. Siempre que sientas que tu objetivo te está frenando, pregúntate “¿Qué quiero en realidad?” y escucha en silencio la respuesta que recibes.
  3. Acepta que no estarás siempre locamente motivado. Si esperas a estar increíblemente motivado para pasar a la acción estás poniendo mucha presión en ti mismo. Es mejor aligerar la presión permitiéndote estar medianamente interesado o incluso en un estado de temor. Al permitirte estar en este estado haces que sea más fácil moverte y continuar porque ya no te estás resistiendo. Cuando te resistes al estado en que estás, lo perpetúas.
  4. Conéctate a tu flujo. Todos tenemos momentos en que somos más creativos o tenemos más energía, y todos tenemos momentos en que nos apetece descansar o recargar. Para algunos de nosotros, esos recesos y flujos pueden darse en ciertos momentos del día, para otros se darán de forma totalmente aleatoria. La clave está en poner atención y explotar esos flujos de energía. Cuando pasas a la acción volcándote totalmente estás lleno de energía y al permitirte relajarte cuando estás en un estado de calma, te respetas y te honras a ti mismo. Cumplirás más cosas siguiendo tus ritmos naturales que tratando de forzarte a ti mismo.

Hay un momento para ser productivo y hay un momento para descansar. Del mismo modo que en la naturaleza hay un momento para la actividad y la vida nueva en primavera, y hay un momento para descansar y retraerse al interior en invierno. Igual que la naturaleza sabe que su crecimiento perpetuo es insostenible, nosotros tenemos que darnos cuenta de que esperar ser productivos todo el tiempo nos puede llevar a quemarnos o a ser menos productivos.

Puede que sea hora de que empecemos a escuchar nuestros cuerpos. Pienso que cuanto más seguimos el modo de actuar de la naturaleza, más inteligentemente vivimos.

Fuente: Yoriento

Ante las preguntas de ana y GG en este post, me he decidido a liberar el código de un proyecto que estaba desarrollando y que desafortunadamente, por falta de tiempo, he tenido que cancelar.

Se trata de un generador y parser de ficheros XMI, uno de los tantos estándares de XML, concretando, una especificación para el intercambio de diagramas que fue escrita para proveer una manera de compartir modelos UML entre diferentes herramientas de modelado.

La forma tanto de utilizar el serializer (escritura en fichero XMI) como la de utilizar el parser es muy sencilla y parecida. Por ejemplo, para el serializer basta con:

JAVA:
  1. KXmlSerializer serializer = new KXmlSerializer();
  2. serializer.startTag(null, "packagedElement");
  3. serializer.attribute(NAMESPACE_XMI, "xmi:id", pClassID);
  4. serializer.attribute(null, "name", pClassName);
  5. serializer.attribute(null, "visibility", "public");
  6. serializer.attribute(null, "isAbstract", "true");
  7. serializer.endTag(null, "packagedElement");

Lo que da lugar a un XMI del tipo:

XML:
  1. <packagedElement xmi:type="uml:Class" name="Jam" xmi:id="BOUML_0x1f883_4" visibility="public" isAbstract="true">

Espero que le sirva a alguien el código desarrollado. Podéis descargarlo de aquí.

La vida es riesgo

Via El blog de Crowdat

Tener un hijo, plantar un árbol y escribir un libro. Las tres cosas que se supone debemos completar antes de diñarla. El mecanismo de la primera no necesita mucha explicación. Para la segunda bastan unas pocas semillas y eso que, lejos de las pantallas, se le sigue llamando "el campo". Y para la tercera, lejos de editoriales planetarias y anagramas con el símbolo del euro, resulta que hace ya un año que existe Bubok. Una especie de Lulu a la española. Pues vaya, que después de un año me entere de proyectos como este, como lector (al menos) empedernido que me considero...

Os dejo con la descripción bastante exacta que hacen del servicio en IncubaWeb.

Bubok es un servicio web a través del cual puedes preparar y publicar tus libros para su difusión en formato impreso o PDF. Pero no solo eso.

En realidad es toda una plataforma de venta de libros online pues una vez te registras en la página (genial el sistema, muy impactante) dispones de tu dirección personal del tipo tunombre.bubok.com en la que ofrecer tu perfil de escritor y acceso a la tienda segura donde adquirir los libros en formato físico o en descarga de PDF. Si quieres ver un ejemplo, tienes la página de Alberto Vázquez-Figueroa, que dicho sea de paso es una obligación visitar y poder descargar material de este gran autor de forma gratuita.

La tienda virtual es también digna de mención, pues no solo es sencillísima de usar sino que su aspecto y velocidad es impresionante, y además incluso puedes personalizar tu copia del libro impreso. La verdad es que el funcionamiento de todo Bubok es impresionante, merece la pena solo visitar sus secciones, la visita virtual, incluso la ayuda online es una gozada.

En cuanto al modelo de negocio, Bubok no te cobra nada por el alojamiento de tu espacio, ni siquiera por la elaboración de tus libros, solo gana cuando tu ganas, en una relación de 80% del precio de la venta para ti y el 20% para Bubok, y no es mal intercambio por disponer de una plataforma de esta calidad.

Más información aquí y aquí.

A 1 persona le gusta esta entrada

Yo tengo una pregunta para ustedes:

¿Por qué la ingeniería informática no es una profesión regulada en España, al igual que la prostitución?

escudo_informatica

hp2133

La verdad que no ha sido un camino de rosas instalar Ubuntu 8.10 Intrepid Ibex en mi nuevo y flamante HP 2133 (gracias Morty!).

Al final lo conseguí instalar en una ocasión con un CD Ubuntu 8.10 versión alternate y en otra ocasión con la versión Desktop.

Vamos a ver la instalación con la versión Alternate y seguidamente comentaré más o menos los cambios para instalar la Desktop.

Requisitos para este tutorial:

  • Un mini Notebook HP 2133, por supuesto.
  • ISO de Ubuntu 8.10 alternate.
  • Conexión por cable Ethernet disponible.
  • Un pc de escritorio con Ubuntu o Windows XP instalado.
  • Un pendrive USB. Como nota importante, reseño que utilizando un MP4 de Saytes todo eran problemas y que, sin embargo, utilizando un pendrive de 8gb de la marca Kingston la velocidad de lectura / escritura aumenta y el proceso transcurre sin errores.

Pasos para la instalación:

  • Formatear el pendrive. Desmontarlo y desde una terminal escribir:

    sudo mkfs.vfat /dev/sdX1

    donde X es la letra de la unidad USB, en mi caso es sdb1.

  • Para hacer el pendrive bootable, vamos a Ubuntu en nuestro PC de escritorio, y buscamos en el menú Sistema => Administración la opción Create a USB Startup Disk.
  • Una vez finalizado el proceso, debemos eliminar las referencias al CD-ROM en los ficheros que ha grabado la utilidad de autoarranque del pendrive. Esto evita que te pida montar el CD en uno de los pasos. En una de las carpetas hay una serie de archivos con extensión cfg que habrá que modificar de la siguiente forma:
    • En text.cfg, modificar la linea 5:

      append  noprompt cdrom-detect/try-usb=true persistent file=/cdrom/preseed/ubuntu.seed initrd=/install/initrd.gz quiet --

      por esta

      append  noprompt cdrom-detect/try-usb=true persistent file=/preseed/ubuntu.seed initrd=/install/initrd.gz quiet --
    • En adtext.cfg, modificar la linea 4:
      append  file=/cdrom/preseed/ubuntu.seed priority=low initrd=/install/initrd.gz --

      por esta

      append  file=/preseed/ubuntu.seed priority=low initrd=/install/initrd.gz --
    • En gfxboot.cfg, modificar la linea 8:
      replace cli=file=/cdrom/preseed/cli.seed

      por esta

      replace cli=file=/preseed/cli.seed

      y la linea 11:

      replace ltsp=file=/cdrom/preseed/ltsp.seed

      por esta

      replace ltsp=file=/preseed/ltsp.seed
  • Desmontar el pendrive del equipo de escritorio e insertarlo en el HP 2133. Arrancar el equipo y pulsar F9 para escoger el dispositivo USB como el que arrancará primero.
  • Seguir los pasos de la instalación de forma normal teniendo en cuenta que, al menos en mi caso, en el paso Seleccionar e instalar programas no puedes elegir nada, porque si lo haces, el sistema te dice que no se puede descargar (a pesar de estar conectado vía cable). Al no elegir nada se instala el sistema base.
  • Cuando termina la instalación te suelta en un terminal. Inicias sesión e instalas gdm:
    sudo apt-get install gdm
  • ANTES DE REINICIAR vamos a hacer uso de los pasos que nos ofrece Daniel Alberto Rodríguez Méndez, para dejar la tarjeta gráfica instalada y que al reiniciar podamos ver el entorno gráfico.

    Lo que yo he hecho, guiándome por esta referencia, ha sido lo siguiente:

    • Bajarme el driver correspondiente a nuestra tarjeta gráfica de este enlace.
    • Descomprimir el fichero descargado con el siguiente comando:
      tar xvzf 85a-43862-u810-2d-bin.tar.gz
    • Nos colamos en el directorio que se ha extraído del archivo:
      cd 85a-43862-u810-2d-bin/

    • Copiamos el driver al directorio que nos comentan en el Readme:
      cp via_drv.so /usr/lib/xorg/modules/drivers/
    • Nos descargamos el magnífico xorg.conf.chi ya preparado en el sitio de dabeto en este enlace.
    • Lo renombramos a xorg.conf:
      mv xorg.conf.chi xorg.conf
    • Y lo copiamos, sobrescribiendo el xorg.conf original:
      mv xorg.conf /etc/X11/
    • Listo el tema gráfico. Ahora ya podemos reiniciar y, al volver a Ubuntu, deberíamos ver la pantalla de bienvenida. Pero podemos observar que no tenemos WiFi. Para cargar el driver, he encontrado tres formas válidas. La primera, sirve para todos los kernels y no la he encontrado en ninguna referencia. Simplemente observando los errores que me "chillaba" Ubuntu, me di cuenta de que se "casi" se reconocían los drivers del chipset WiFi, pero se informaba que había que bajarse e instalar la última versión. Las otras dos formaron parte de instalaciones anteriores y, o bien no funcionaban en el kernel 2.6.27-11-generic, o bien la WiFi se iba y volvía de vez en cuando.
      • Se trata de seguir los pasos indicados en este enlace, que son los siguientes:
      • wget http://bu3sch.de/b43/fwcutter/b43-fwcutter-011.tar.bz2
        tar xjf b43-fwcutter-011.tar.bz2
        cd b43-fwcutter-011
        make
        cd ..
        export FIRMWARE_INSTALL_DIR="/lib/firmware"
        wget http://downloads.openwrt.org/sources/broadcom-wl-4.80.53.0.tar.bz2
        tar xjf broadcom-wl-4.80.53.0.tar.bz2
        cd broadcom-wl-4.80.53.0/kmod
        sudo ../../b43-fwcutter-011/b43-fwcutter -w "$FIRMWARE_INSTALL_DIR" wl_apsta.o

        Esta solución, al ser los drivers oficiales, funciona hasta en el kernel 2.6.27-11, al contrario que las dos soluciones que vienen a continuación.

      • La segunda solución, que no funciona en el kernel 2.6.27-11, trata de seguir los pasos de este enlace. Básicamente se traduce en ejecutar los siguientes comandos:
        sudo su
        wget docnielsen.dk/linux/hp2133-wlan-`uname -r`-wl.ko.gz
        mv hp2133-wlan-`uname -r`-wl.ko.gz wl.ko.gz
        gzip -d wl.ko.gz
        mv wl.ko /lib/modules/`uname -r`/kernel/drivers/net/wireless/
        depmod -a
        rmmod ndiswrapper
        rmmod wl
        insmod /lib/modules/`uname -r`/kernel/drivers/net/wireless/wl.ko
        reboot
      • Otra solución es seguir los pasos de este otro enlace, que dan en la sección "To install wireless". Esta solución trata de usar ndiswrapper, para instalar los drivers disponibles para Windows. Básicamente, se traduce en:
        • Ir a este enlace.

        • Seguir el Paso 1: All BCM43XX - Install NDISWrapper and Blacklist Native Driver.
        • Seguir el Paso 2a: SP34152 Driver Download / Extraction
        • Seguir el Paso 3: All BCM43xx - Configure NDISWrapper (and WPA Supplicant).
        • Una vez finalizados los pasos anteriores, seguir el paso HARDY BUG FIX.
        • Reiniciar Ubuntu.

Con esto, ya tenemos las dos cosas más importantes configuradas, que son la tarjeta gráfica y el chip WiFi. Es bueno seguir los consejos de una de las páginas que he dado para ajustar configuraciones como habilitar el escalado de la CPU o deshabilitar los altavoces externos cuando se enchufan unos auriculares.

Todo esto ha sido probado, como ya se ha dicho, con el CD de instalación Alternate. Recientemente he conseguido instalar de nuevo Ubuntu pero con el CD de instalación en su versión Desktop y realizando la instalación desde un lector de DVD externo conectado al puerto USB. Como contrapartida a lo que se puede leer en el 90% de los foros, a mi no me ha funcionado con el CD de instalación Desktop el incluir la opcion xforcevesa en los parámetros del kernel, ni tampoco arrancando directamente el CD con la orden live xforcevesa.

Lo que a mi me funcionó fue pulsar F4 y escoger la opción "Modo gráfico seguro" para la instalación. El único inconveniente es que no se veían los botones de aceptar y cancelar y tuve que ir jugando con la tecla Tab.

Felicidad…

Un saludo a la gente de Aicor ;)

A 1 persona le gusta esta entrada

En un proyecto en el que estoy trabajando necesitaba seleccionar ciertos datos dependiendo de si el usuario escogía un artículo o bien un pack de artículos.

En la siguiente tabla puede verse que cada fila o bien apunta a la id de un artículo o bien a la ide de un pack.
bd-listaboda

Para conseguir mi propósito y obtener o los datos del artículo o los datos del pack directamente con una sola consulta, desarrollé la siguiente:

SQL:
  1. SELECT DISTINCT
  2. IF (lb.id_articulo  '', f.id_categoria, '') AS id_categoria,
  3. IF (lb.id_articulo  '', f.parent_id_categoria, '') AS parent_id_categoria,
  4. IF (lb.id_articulo  '', concat('arti',a.id_articulo), concat('pack', p.id_pack)) AS id_articulo,
  5. IF (lb.id_articulo  '', a.nombre, p.nombre) AS nombre,
  6. IF (lb.id_articulo  '', a.descripcion, p.descripcion) AS descripcion,
  7. IF (lb.id_articulo  '', a.unidades, '') AS unidades,
  8. IF (lb.id_articulo  '', a.PVP, p.PVP_pack) AS PVP, lb.cantidad,
  9. e.id_enlaces_novios AS enlaces_novios
  10. FROM
  11. articulo a,
  12. categoria f,
  13. packs p,
  14. enlaces_novios e
  15. INNER JOIN listaboda lb ON (lb.id_enlaces_novios = e.id_enlaces_novios AND e.id_enlaces_novios = 10)
  16. WHERE
  17. f.id_categoria = a.id_categoria
  18. AND (lb.id_articulo = a.id_articulo AND lb.id_articulo  '') OR (lb.id_pack = p.id_pack AND lb.id_pack  '')
  19. AND a.visible = 1

Podemos ver la sintaxis en su esplendor en la línea 4:

SQL:
  1. IF (lb.id_articulo  '', concat('arti',a.id_articulo), concat('pack', p.id_pack)) AS id_articulo

Lo que viene a decir algo así como "si en la tabla listaboda (la que se ha mostrado en la imagen) el id del artículo NO es nulo, concatenar la frase 'arti' con el id del artículo (por ejemplo: arti128), en otro caso concatenar 'pack' con el id del pack. Asignar el resultado al campo 'id_articulo'".

Por tanto, al recuperar el campo 'id_articulo' tendremos o bien 'arti128' o 'pack3', con lo cual sabemos el tipo de artículo que hemos recuperado, su ID y el resto de los datos asociados.

A 1 persona le gusta esta entrada

Vamos a aprovechar el código Javascript creado en el post anterior para crear un campo <select> dinámico que contendrá una serie de artículos recuperados de la base de datos. Muy útil para la selección de varios articulos en varias listas desplegables.

Como siempre el código habla por sí solo:

PHP:
  1. <!-- Importamos del port de php a javascript>
  2. <script language="javascript" src="php.js"></script>
  3. <!-- Importamos el js creado en el post anterior>
  4. <script language="javascript" src="generar_fila.js"></script>
  5. <?php
  6.     // Si se ha pulsado el submit, procesamos datos que nos llegan
  7.     if ($_POST)
  8.     {
  9.       echo var_dump($_POST);
  10.       echo "<br/>";
  11.       echo "<br/>";
  12.       echo "<b>Articulos recibidos:</b><br/>";
  13.       for ($i=0;$i<count($_POST['articulos']);$i++)
  14.         echo "id del articulo: " . $_POST['articulos'][$i] . "<br/>";
  15.      
  16.       echo "<br/>";
  17.     }
  18. ?>
  19.  
  20. <!-- Formulario principal de la pagina -->
  21. <form name="formulario" id="formulario" action="listas.php" method="post" enctype="multipart/form-data">
  22. <?php
  23.  
  24. // imaginemos que esto lo sacamos de la base de datos,
  25. // pero ahora vamos a hacer un array de prueba
  26. $articulos_bd[] = array('id_articulo' => '1', 'nombre' => 'Powered');  
  27. $articulos_bd[] = array('id_articulo' => '2', 'nombre' => 'by');  
  28. $articulos_bd[] = array('id_articulo' => '3', 'nombre' => 'Linux');  
  29.  
  30. // creamos dos listas, una con los ids de los articulos recogidos presuntamente de la BD ($ids),
  31. // y otra lista con los nombres de dichos articulos ($array)
  32. $lista_articulos = '<select id="articulos_0" name="articulos[]">
  33.  <option selected value="">-Seleccione un artículo-</option>';
  34.   $ids[] = "";
  35.   $ids[] = "-Seleccione un artículo-";
  36.   $array[] = "";
  37.   $array[] = "-Seleccione un artículo-";
  38.  
  39.   // recorremos la lista de articulos del array supuestamente extraido de la BD
  40.   for ($i=0;$i<count($articulos_bd);$i++)
  41.   {
  42.     // montamos un elemento <select></select> normal de HTML con los datos
  43.     // de los articulos recogidos de la BD
  44.     $lista_articulos.='<option id="'.$articulos_bd[$i]['id_articulo'].'" name="'.$articulos_bd[$i]['id_articulo'].'"  value="'.$articulos_bd[$i]['id_articulo'].'">'.$articulos_bd[$i]['nombre'].'</option>';
  45.     $ids[] = $articulos_bd[$i]['id_articulo'];
  46.     $array[] = $articulos_bd[$i]['nombre'];
  47.   }
  48.   $lista_articulos.='</select>';  
  49.  
  50. // creamos un div que contendra los diferentes campos <select></select>
  51. // con todos los productos y sus datos asociados  
  52. echo '
  53.   <div id="tabla">
  54.   <b>Artículo relacionado:</b>
  55.   '.
  56.    $lista_articulos;
  57.    ?>
  58.    <!-- A la funcion addCampo le voy a pasar el evento originado,
  59.         una cadena con los nombres de los articulos separados por comas,
  60.         y otra cadena con los ids de los articulos tambien separados por comas -->
  61.    <input type="button" class="boton" value="Añadir" onClick="addCampo(event,'<?php echo implode(",", $array); ?>', '<?php echo implode(",", $ids); ?>')" alt="Insertar"> 
  62.  
  63.   <!-- continuamos la generacion del formulario -->
  64.    <?php echo '        
  65.   </div>
  66.   <br/>
  67.   <input type="submit" value="Submit"/>
  68. </form>';

Y como siempre, para estas cosas es necesaria una demostración

La lista de referencias utilizadas ha sido la siguiente:
http://www.trans4mind.com/personal_development/JavaScript2/createSelectDynamically.htm
http://www.cristalab.com/tips/32381/subir-multiples-archivos-con-php.html

A 1 persona le gusta esta entrada

Os pongo en situación. Quería pasar desde PHP un array a una función Javascript. En esa función Javascript necesitaba hacer un bucle que me recorriera desde el primer elemento hasta el último, por lo que necesitaba conocer el número total de elementos.

Sin percatarme de los sabios consejos de Moisés acerca de la existencia de la propiedad length existente también para arrays, busco funciones como sizeof o count (existentes en PHP) para desarrollar en Javascript de forma más cómoda.

Por curiosidad busqué referencia sobre el asunto y me topé con el sitio Web de Kevin Vanzonneveld, en el que ofrecen un archivo php.js que contiene una serie de funciones PHP portadas a Javascript de manera impecable. En el sitio de Kevin se relatan las 212 funciones portadas hasta ahora.

En mi caso, sólo tuve que descargar el mencionado fichero php.js, enlazarlo con una etiqueta script y utilizar la función count como si lo hiciera con PHP de forma normal para contar el número de elementos de un array o descomponer cadenas en array seccionando según un carácter. Os dejo con un ejemplo que completaré en una próxima entrada. Lógicamente se podrían haber utilizado las propiedades nativas de Javascript length y split para desarrollar el ejemplo, pero así vemos una prueba para otras funciones de esta librería que no estén desarrolladas de forma nativa en Javascript.

JAVASCRIPT:
  1. var numero = 0;
  2.  
  3. // esta funcion añade un nuevo 'dd' al div principal con el contenido especificado
  4. // recibe el evento disparado y dos arrays: el primero con las IDs de los campos
  5. // select y el segundo con las IDs de las opciones que irán dentro de cada select
  6.  
  7. function addCampo(evento, array, ids) {
  8.   newID = 'articulos_' + (++numero) // genero una nueva ID
  9.   nDD = document.createElement('dd')
  10.   nDD.id = 'dd_' + numero
  11.   bold = document.createElement('b') // creo una etiqueta en negrita
  12.   bold.innerHTML = "Artículo relacionado: "
  13.  
  14.   myselect = document.createElement("select") // creo un elemento <select>
  15.   myselect.name = "articulos[]" // le pongo de nombre el de un array previamente inicializado en PHP
  16.   myselect.id = newID
  17.  
  18.  // aquí puedo utilizar la función explode al haber importado php.js, obteniendo los mismos
  19.  // resultados que en PHP, es decir, me separará en un array el resultado de descomponer
  20.  // la cadena separada por comas
  21.   var newArray = explode(',', array)
  22.   var newIDs = explode(',', ids)
  23.  
  24.  // aquí puedo utilizar la función count de forma normal porque he incluido en el archivo .php
  25.  // correspondiente la importación de php.js. Como puede verse, puedo recorrer de forma normal
  26.  // el array incluyendo la funcion count dentro de mi for de Javascript
  27.   for (i=0;i<count(newArray);i++)
  28.   {
  29.     if (newArray[i] != ',' && newArray[i] != '')
  30.     {
  31.      // voy creando dinamicamente las opciones (<option>) para el <select>
  32.      // en base a los datos que he recibido como parámetros de la función
  33.       theOption = document.createElement("OPTION")
  34.       theText = document.createTextNode(newArray[i])
  35.       theOption.appendChild(theText)
  36.       theOption.id = newIDs[i]
  37.       theOption.name = newIDs[i]
  38.       theOption.value = newIDs[i]
  39.       myselect.appendChild(theOption)
  40.     }
  41.   }
  42.  
  43.  // creo un enlace que me servirá para eliminar elementos <select>
  44.  // incluidos dentro de un DD
  45.   a = document.createElement('a')
  46.   a.name = nDD.id
  47.   a.href = '#'
  48.   a.onclick = elimCamp
  49.   a.innerHTML = ' Eliminar'
  50.  
  51.  // adjunto todos los elementos al DD
  52.   nDD.appendChild(bold)
  53.   nDD.appendChild(myselect)
  54.   nDD.appendChild(a)
  55.  
  56.  // al contenedor que tengo en la página PHP le adjunto el DD creado
  57.   container = document.getElementById('tabla')
  58.   container.appendChild(nDD)
  59. }
  60.  
  61. //con esta función eliminamos el campo cuyo link de eliminación sea presionado
  62. function elimCamp(evt){
  63.    evt = evento(evt)
  64.    nCampo = rObj(evt)
  65.    div = document.getElementById(nCampo.name)
  66.    div.parentNode.removeChild(div)
  67. }
  68.  
  69. //con esta función recuperamos una instancia del objeto que disparo el evento
  70. rObj = function (evt) {
  71.    return evt.srcElement ?  evt.srcElement : evt.target;
  72. }
  73.  
  74. //esta funcion nos devuelve el tipo de evento disparado
  75. evento = function (evt) {
  76.    return (!evt) ? event : evt;
  77. }

Algunos habréis notado que al actualizar de forma automática con la nueva herramienta que nos ofrece WordPress 2.7 nos aparece un error Cannot redeclare pclziputilpathreduction, justo al intentar desempaquetar la nueva versión de WordPress.

La solucion es bien sencilla. Hay que desactivar el plugin WordPress Automatic Upgrade, con el que ya disfrutábamos de esta características que ahora WordPress integra de forma interna.

Os ofrezco 3 referencias que me han venido muy bien a la hora de reactivar mis neuronas para que recordasen el manejo correcto de expresiones regulares.

En la web txt2re podemos escribir la cadena de texto que deseemos y, en un segundo paso, elegir para cada ítem cómo queremos procesarlo. Por ejemplo, podemos elegir que algo empiece por la subcadena "blog" o podemos elegir que empiece por 4 letras.

Continuando con el repaso, os hago referencia a una página perteneciente a la Universidad de Oviedo, en la que podemos validar una cadena de texto cualquiera frente a un patrón determinado por el usuario. Se nos indican alrededor de la página algunos ejemplos útiles de patrones.

Terminamos el repaso con una librería bastante extensa de expresiones regulares, con prácticamente todos los tipos de patrones utilizados más frecuentemente: urls a páginas Web, direcciones de correo electrónico, nombrado de ficheros de imagen, etc. La única pega es que todo el contenido se encuentra en el idioma de Shakespeare.

Al intentar meter una nueva opción del box Clientes,


me he encontrado con un problema que me resultaba familiar y para el que no recordaba la solución:

se soluciona incluyendo el fichero que integra la nueva opción (en mi caso requests.php) en el array $aADMPages del fichero /admin/includes/functions/administrators.php, diciéndole cuál es su fichero superior (en mi caso customers.php, porque estoy dentro del box de clientes).

Older Posts »