Llevo un rato peleándome con esto y finalmente he encontrado solución.

El caso es que los de PHP al hacer los métodos MultiByte (un apaño que harían al ver que se olvidaron del resto de juegos de caracteres…) no hicieron ni el de primera letra mayúscula ni minúscula.

Ambos métodos me son muy útiles para activar / desactivar usuarios de una wiki (ya que un usuario no puede acceder al sistema mediawiki si tiene la primera letra en minúscula), así que en realidad no he tenido más remedio que hacerlo (nooo, yo no queríaaa..!! xD)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
// First letter uppercase
if (!function_exists('mb_ucfirst')) {
  function mb_ucfirst($str, $to_lower = false, $charset = 'utf-8')
  {
    $first = mb_strtoupper(mb_substr($str, 0, 1, $charset), $charset);
    $end = mb_substr($str, 1, mb_strlen($str, $charset), $charset);
    // Convert them all to lowercase (if specified)
    if ($to_lower) {
      $end = mb_strtolower($end, $charset);
    }
    return $first . $end;
  }
}
// First letter lowercase
if (!function_exists('mb_lcfirst')) {
  function mb_lcfirst($str, $charset = 'utf-8') {
    $first = mb_strtolower(mb_substr($str, 0, 1, $charset), $charset);
    return $first . mb_substr($str, 1, mb_strlen($str, $charset), $charset);
  }
}

Como podréis ver al método mb_ucfirst le he añadido un parámetro $to_lower que sirve para convertir el resto de la frase a minúscula (muy útil contra hoygans).

El parámetro $charset es el que más me ha dado por culo (al principio no lo tenía en cuenta); tened en cuenta vuestra codificación de caracteres y cambiadlo directamente en la función si es preciso.

Buen fin de semana!

Referencias

Requisitos

  1. Ubuntu 9.10. Testeado por otros usuarios en 10.04
  2. Android SDK y platform-tools concretamente la herramienta ADB (Android Debug Bridge)
  3. Un dispositivo Android, en mi caso una Tablet Toshiba Folio 100 con Android 2.2 con el SO cambiado (el test se realizó con el TNTModFolio de Dexter)

Definición Conceptual

Nuestro problema principal era que teníamos una web desarrollada en un servidor de test y para acceder a ella necesitábamos modificar el archivo «hosts» para añadir una directiva de nombres.

Lo primero que apreciamos en la Tablet de Toshiba fue que no podíamos acceder al market ni a las google apps ya que viene restringido de fábrica. Nos decantamos, así, por instalar un mod del SO desarrollado por Dexter. Estos mods tienen una eficacia y mejoras contrastadas y nos decidimos finalmente por el TNTMod.

El TNTMod te permite tener permisos root en tu dispositivo pero para modificar los archivos de sistema hay que ser un poco más creativo.

El único método que me ha dado resultado es el uso y configuración de ADB, una aplicación estilo putty para acceder mediante Shell o comando típicos como pull o push a los archivos de SO de un dispositivo Android. Mediante ADB es posible conectarse con un terminal a los archivos de sistema y comprobar la ruta del archivo hosts. También permite traértelo al pc local, modificarlo y después volverlo a meter en el sistema de archivos del dispositivo.

Procedimiento

Primero de todo hay que descargar e instalar android SDK (necesitarrás JAVA SDK):

  • Descargar tar.gz para Linux
  • Extraer en carpeta personal
  • Acceder a /home/carpeta_personal/android-sdk/tools/android
  • Instalar todos paquetes disponibles

Ahora es momento de conectar la Toshiba Folio 100 al PC y hacer lo siguiente…

  • Activar el USB DEBUG en Android (Ajustes -> Aplicaciones -> Desarrollo)
  • /home/carpeta_personal/android-sdk/platform-tool/adb devices
  • Nos devolverá una salida de este tipo: ???????? No Permissions

Hay que configurar el driver para que reconozca la Tablet:

  • Entrar en /etc/udev/rules.d/
  • Dentro de este directorio tiene que haber un fichero llamado 51-android.rules o con otro número.
  • Editamos este fichero para añadir la siguiente línea:
1
SUBSYSTEMS=='usb', ATTRS{idVendor}=='0bb4',ATTRS{idProduct}=='0c97', MODE='0666'

Como, seguramente, no sabremos el idVendor y el idProduct del dispositivo hacemos:

1
lsusb

Esto nos mostrará la información de los dispositivos usb conectados.

Seguidamente le damos permisos y reiniciamos el server de ADB:

1
2
3
4
5
6
7
8
sudo chmod a+r /etc/udev/rules.d/51-android.rules
sudo ~/android-sdk/tools/adb kill-server
sudo ~/android-sdk/tools/adb start-server
# * daemon not running. starting it now on port 5037 *
# * daemon started successfully *
~/android-sdk/tools/adb devices
# List of devices attached
# HT03JNX00008 device

Una vez tenemos el dispositivo vamos a acceder a él:

1
2
3
4
5
6
7
8
9
10
11
12
13
~/android-sdk/tools/adb shell
su
mount
# rootfs / rootfs ro 0 0
# tmpfs /dev tmpfs rw,mode=755 0 0
# devpts /dev/pts devpts rw,mode=600 0 0
# proc /proc proc rw 0 0
# sysfs /sys sysfs rw 0 0
# tmpfs /sqlite_stmt_journals tmpfs rw,size=4096k 0 0
# /dev/block/mtdblock3 /system yaffs2 ro 0 0
# /dev/block/mtdblock5 /data yaffs2 rw,nosuid,nodev 0 0
# /dev/block/mtdblock4 /cache yaffs2 rw,nosuid,nodev 0 0
# /dev/block/mmcblk0p1 /sdcard vfat rw,dirsync,nosuid,nodev,noexec,uid=1000,gid=1000,fmask=0711,dmask=0700,codepage=cp437,iocharset=iso8859-1,utf8 0 0

Remontamos el FS como rw para poder escribir o hacer pushes:

1
mount -o remount,rw -t yaffs2 /dev/block/mtdblock3 /system

YA TENEMOS ACCESO COMPLETO

Cosas a tener en cuenta

Si apagamos el dispositivo habrá que hacer otra vez la parte del mount ya que el dispositivo por defecto monta /system en ro. Si se desea dejar permanentemente en rw modificar reglas de mount.

En todo momento del proceso hay que activar el modo debug de USB en la distro de Android que utilicemos.

El otro día expliqué cómo unir ficheros mp3 en un mismo fichero y dije que dejaría para más tarde lo de la creación del Podcast. Vamos a ello pues.

Para crear nuestro Podcast necesitaremos, a parte de CakePHP, la librería getid3, ya que para que itunes coja bien las imágenes de cada podcast lo mejor que podemos hacer es añadir la imagen al mp3 directamente.

Suponiendo que habéis leído el tutorial del otro día voy a ir directo al grano…

Lo primero que haremos es escribir la información ID3 a nuestro fichero de podcast. Para el ejemplo utilizaré el componente que tengo hecho para getid3 y que he(mos) utilizado anteriormente. Lo podéis descargar del post anterior o de github.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
// .. aquí tendríamos la carga del mp3 al servidor o la unión de los mp3 ..
// ....
// Escribimos la información id3..
$id3_data = array(
  'title'   => 'Título del podcast',
  'artist'  => 'Artista(s)',
  'album'   => 'Álbum',
  'year'    => 'Año',
  'genre'   => 'Género musical',
  'comment' => 'Comentario',
  'images'  => array(
    array(
      'image'       => '/ruta/completa/hacia/la/imagen.jpg',
      'type'        => 'cover', // tipos válidos: cover, back y cd,
      'mime'        => 'image/jpg',
      'description' => 'Nombre o descripción que queramos poner'
    ),
    array(
      'image'       => '/ruta/completa/hacia/la/imagen.jpg',
      'type'        => 'cover', // tipos válidos: cover, back y cd,
      'mime'        => 'image/jpg',
      'description' => 'Nombre o descripción que queramos poner'
    ),
    array(
      // ... más imágenes (itunes sólo carga una, así que a partir de la primera es ocupar más espacio..
    )
  )
);
if ($this->Getid3->write($podcast, $id3_data)) {
  // Los datos se han escrito correctamente
} else {
  pr($this->Getid3->errors);
}

Con algún programa para gestionar etiquetas id3 (como kid3 por ejemplo) o con un simple reproductor podréis comprobar si se han escrito correctamente las etiquetas id3.

Ahora necesitamos crear una plantilla (layout) para nuestro podcast, ya que la estructura básica de un podcast difiere un poco respecto a la de un RSS convencional.

Para no complicarnos mucho y para hacer este layout lo más versátil posible simplemente concatenaremos los elementos que nos interesen delante del contenido (cada ítem del podcast). Así que a partir del layout de RSS de CakePHP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
// /app/views/layouts/xml/podcast.ctp
echo $this->Rss->header();
if (!isset($documentData)) {
  $documentData = array();
}
if (!isset($channelData)) {
  $channelData = array();
}
if (!isset($channelData['title'])) {
  $channelData['title'] = $title_for_layout;
}
$before = '';
if (!empty($beforeContent) && is_array($beforeContent)) {
  foreach ($beforeContent as $i) {
    $before .= $i;
  }
}
$channel = $this->Rss->channel(array(), $channelData, $before . $content_for_layout);
echo $this->Rss->document($documentData,$channel);

Lo único que he hecho es añadir las líneas 13 a 20 y la variable $before en la línea 21.

Con esto podremos mandar un array de elementos XML desde nuestra vista del podcast con toda la información extra que queramos. Si no acabáis de verlo tranquilos porque ahora con la vista del podcast os acabará de quedar claro.

Del mismo modo que con el layout, utilizo como base la vista de RSS de CakePHP.

Aquí viene la vista:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<?php
// /app/views/podcasts/podcasts/index.ctp
// Pasamos info a la variable documentData del layout
$this->set('documentData', array('xmlns:itunes' => 'http://www.itunes.com/dtds/podcast-1.0.dtd'));
// Pasamos información a la variable channelData del layout
$this->set('channelData', array(
  'title'       => 'Racó Tècnic Podcast',
  'link'        => $this->Xml->url(array('controller' => 'podcasts', 'action' => 'index'), true),
  'description' => __('Racó Tècnic Audio & Video Podcasts.', true),
  'language'    => 'es',
  'image'       => array(
    'url'    => $this->Xml->url('/img/logo.jpg', true),
    'link'   => $this->Xml->url('/', true),
    'width'  => 144,
    'height' => 144,
    'title'  => 'Racó Tècnic logo'
  )
));

// Aquí pasamos la información a la variable $beforeContent del layout
$this->set('beforeContent',
  array(
    $this->Xml->elem('image', array(
      'namespace' => 'itunes',
      'href' => $this->Xml->url('/img/logo.jpg', true)
    )),
    $this->Xml->elem('author', array('namespace' => 'itunes'), 'El Boletaire')
  )
);
// Empezamos a añadir <item>s a nuestro fichero podcast
foreach ($podcasts as $podcast)
{
  $time = strtotime($podcast['Podcast']['created']);

  $link = array(
    'controller' => 'foo',
    'action'     => 'view',
    'slug'       => $podcast['Podcast']['slug']
  );
  App::import('Sanitize');
  // This is the part where we clean the body text for output as the description
  // of the rss item, this needs to have only text to make sure the feed validates
  // Descripción del podcast
  $bodyText = preg_replace('=\(.*?\)=is', '', strip_tags($podcast['Podcast']['description']));
  $bodyText = $this->Text->stripLinks($bodyText);
  $bodyText = Sanitize::stripAll($bodyText);
  $bodyText = $this->Text->truncate($bodyText, array('limit' => 400, 'exact' => false));

  // Título del podcast
  $item = $this->Xml->elem('title', null, h($podcat['Podcast']['title']));
  // Autores
  $item .= $this->Xml->elem('author', array('namespace' => 'itunes'), h($podcast['Podcast']['authors']));
  // Enlace y guid del podcast
  $item .= $this->Xml->elem('link', null, $this->Html->url($link, true));
  $item .= $this->Xml->elem('guid', array('isPermaLink' => 'true'), $this->Html->url($link, true));
  // Añadimos la descripción
  $item .= $this->Xml->elem('summary', array('namespace' => 'itunes'), h($bodyText));
  $item .= $this->Xml->elem('description', null, h($bodyText));
  // Fecha de publicación
  $item .= $this->Xml->elem('pubDate', null, $this->Rss->time($podcast['Podcast']['created']));

  // Añadimos la carátula
  if (isset($podcast['Cover'][0]['file'])) {
    $item .= $this->Xml->elem('image', array(
      'namespace' => 'itunes',
      'href' => $this->Xml->url('/img/' . $podcast['Cover'][0]['file'], true)
    ));
  }
  // El fichero de podcast
  $item .= $this->Xml->elem('enclosure', array(
    'url'    => Router::url('/files/podcasts/' . $podcast['Podcast']['file'], true),
    'length' => filesize($podcast['Podcast']['file']),
    'type'   => 'audio/mpeg' // formato mime del archivo
  ));

  echo  $this->Xml->elem('item', null, $item);
}

Fijaros bien que todas las URL que he creado las he hecho especificando el segundo parámetro del método “url”; de este modo nos devolverá siempre URL absolutas en lugar de relativas (que es como debe ser en un RSS).

Bien, con esto tendríamos las vistas pero tenemos que decirle a cake que nos las cargue en algún momento. A mi me gusta acceder a través del nombre_del_controlador.pod

Para hacerle entender a Cake que queremos que distinga las direcciones terminadas en .pod tenemos que abrir nuestro fichero de rutas (routes.php) y añadir la siguiente línea:

1
2
3
4
5
<?php // /app/config/routes.php
Router::parseExtensions('pod');
// Si queréis utilizar más extensiones, como rss u otras
// que necesitéis simplemente id añadiendo parámetros:
// Router::parseExtensions('pod','rss','mrss');

En nuestro controlador necesitaremos cargar el componente Request Handler que nos permitirá distinguir desde el controlador los accesos a una página terminada en .pod:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php // /app/controllers/podcasts_controller.php
class PodcastsController extends AppController
{
  var $components = array('Getid3', 'RequestHandler');
  var $helpers = array('Xml', 'Rss', 'Html');

  public function index()
  {
    // Desactivamos el renderizado automático
    $this->autoRender = false;
    if ($this->RequestHandler->prefers('pod')) {
      // Hacemos lo que tengamos que hacer para poblar nuestro podcast
      // $podcasts = $this->Podcast->find(...)

      // Cargamos la vista y el layout del podcast
      $this->render('podcasts/index','xml/podcast');
    }
    // Si no es podcast hacemos el render normal
    else $this->render();
  }
}

Como podéis ver el método “prefers” nos permite distinguir entre una terminación u otra. Así pues, si añadiésemos la terminación “jpeg”, haríamos $this->RequestHandler->prefers(“jpeg”) para averiguar si el usuario accede a través de ella.

Con esto ya tendríais vuestro podcast funcionando. Para probarlo podéis añadirlo a iTunes desde Avanzado / Subscribirse a un podcast y acto seguido aparecerá en vuestro listado de podcasts.

Si queréis mandarlo a iTunes para que aparezca en la iTunes store podéis hacerlo desde este enlace.

Si queréis ver un ejemplo podéis ver el podcast del netlabel música vermella en itunes o desde la propia página web.

Ejemplo

Referencias

A disfrutarlo :)

Recientemente he decidido pagar un servidor compartido para hacer las copias de seguridad de mis documentos, imágenes y demás. Es un servidor realmente barato pero con la pega que no tengo acceso vía SSH, lo que me ha impedido utilizar aplicaciones como rsync o similares.

Después de buscar un rato la única solución viable que he encontrado ha sido lftp, ya que es el único modo que he encontrado de hacer copias incrementales vía FTP.

lftp tiene un método llamado “mirror” que es el que nos permitirá hacer esto. Un ejemplo sencillo de su uso sería…

1
2
3
4
5
6
7
lftp -c 'set ftp:list-options -a;
    open ftp://usuarioftp:passwordftp@hostftp;
    lcd /directorio/donde/copiar;
    cd /directorio/local/a/copiar;
    mirror --reverse \
           --delete;
    close -a;'

Como también quería hacer backups de MySQL he decidido entretenerme un rato y hacer mi primer script de bash para hacer copias de mysql y directorios del sistema periódicamente (cada dos días a través de un cron).

Para utilizar el script necesitaréis tener instalado en vuestro servidor linux los paquetes lftp y mailx (o hairloom-mailx); este último servirá para enviarnos un e-mail en caso de error en la subida de nuestros backups.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/bin/bash

# :: General Backup Setup ::
BACKUP=/tmp/backup.$$
NOW=$(date +'%Y%m%d')
EMAILID='tu_correo@ejemplo.com'
DOMYSQL=1 # 0 para no hacer copia de seguridad de mysql
DOSYSTEM=1 # 0 para no hacer copia de seguridad del sistema
DEBUG=0 # 1 activa un poco de debug (pero solo un poco xD)

# :: File System Backup Setup ::
FOLDERS=( '/home/usuario/Documentos' '/home/usuario/Imágenes' )
# FTP destination folder
SFOLDER='directorio_destino_donde_guardar_la_copia'

# :: MySQL Backup Setup ::
MUSER='usuarioSQL'
MPASS='pass'
MHOST='localhost'
# MySQL FTP destination folder
MFOLDER='directorio_destino_donde_guardar_los_sql'

# :: FTP server Setup ::
# Root FTP folder
RCD='/directorio_raiz_del_servidor'
FUSER='usuario'
FPASS='contraseña'
FHOST='servidor.dominio'
FOPTIONS='set ftp:list-options -a;set ftp:ssl-allow no' #;set net:limit-total-rate 61440'

# end config

# :: Binaries ::
MYSQL='$(which mysql)'
MYSQLDUMP='$(which mysqldump)'
GZIP='$(which gzip)'
FTP='$(which lftp)'

debug() {
  if [ $DEBUG == 1 ]; then
    echo $1
  fi
}

if [ $DOMYSQL == 1 ]; then
  # Create backup folder
  debug 'Creating backup folder ${BACKUP}'
  [ ! -d $BACKUP ] &amp;&amp; mkdir -p '${BACKUP}' || :

  # Start MySQL backup
  debug 'Showing databases...'
  DBS='$($MYSQL -u $MUSER -h $MHOST -p$MPASS -Bse 'show databases')'
  for db in $DBS
  do
   if [ $db != 'information_schema' ]; then
   debug 'Creating $db.sql.gz backup file'
   FILE=$BACKUP/$db.sql.gz
   $MYSQLDUMP -u $MUSER -h $MHOST -p$MPASS $db | $GZIP -9 > $FILE
   chmod 755 $FILE
   fi
  done
  # Start FTP backup using lftp
  debug 'Starting FTP transaction for MySQL backup files...'
  $FTP -c '$FOPTIONS;
  open ftp://$FUSER:$FPASS@$FHOST;
  lcd $BACKUP;
  cd $RCD/$MFOLDER;
  mkdir ${NOW};
  cd ${NOW};
  mirror  --reverse \
      --delete;
  close -a;'

  # Find out if ftp mysql backup failed or not
  if [ '$?' != '0' ]; then
   debug 'FTP upload failed'
   T=/tmp/backup.fail
   echo 'Date: $(date)'>$T
   echo 'Hostname: $(hostname)' >>$T
   echo 'Backup failed' >>$T
   mail  -s 'MYSQL BACKUP FAILED' '$EMAILID' <$T
   rm -f $T
  fi

  # Delete files
  debug 'Removing files...'
  if [ $DEBUG ]; then
    rm -frv $BACKUP
  else
    rm -fr $BACKUP
  fi
fi

if [ $DOSYSTEM == 1 ]; then
  # Start System Backup
  # Get number of folders
  FELEM=${#FOLDERS[@]}
  for (( i=0;i<$FELEM;i++)); do
    THISFOLDER=${FOLDERS[${i}]}
    if [ -d $THISFOLDER ]; then
      debug '$THISFOLDER exists'
      REMOTEFOLDER=( $(echo $THISFOLDER | tr '/' ' ') )
      REMOTEFOLDER=${REMOTEFOLDER[${#REMOTEFOLDER[@]}-1]}
      debug 'Using $REMOTEFOLDER as remote folder name'
      debug 'Starting FTP transaction from $THISFOLDER to $RCD/$SFOLDER/$REMOTEFOLDER'
      # FTP transfer
      $FTP -c '$FOPTIONS;
        open ftp://$FUSER:$FPASS@$FHOST;
        lcd $THISFOLDER;
        cd $RCD/$SFOLDER;
        mkdir $REMOTEFOLDER;
        cd $REMOTEFOLDER;
        mirror  --reverse \
            --delete;
        close -a;'
      # Find out if ftp system backup failed
      if [ '$?' != '0' ]; then
       debug 'File system backup failed'
       T=/tmp/backup.fail
       echo 'Date: $(date)'>$T
       echo 'Hostname: $(hostname)' >>$T
       echo 'Backup failed on dir $THISFOLDER' >>$T
       mail  -s 'SYSTEM BACKUP FAILED' '$EMAILID' <$T
       rm -f $T
      fi
    fi
  done
fi

Como he dicho, es el primer script de bash que he hecho en la vida (a parte de alguno otro realmente tonto..) así que si me proponéis ideas para mejorarlo o encontráis algún error os agradecería que me lo comentarais :)

HTML5 LogotypeDesde hace unos días estoy inscrito en un curso gratuito y online de HTML5, gestionado desde la P2PU con el soporte de Mozilla. Estos cursos los da gente anónima y desinteresada, por lo que en general no son docentes sino gente con conocimientos y ganas de explicar cosas. Es maravilloso que existan iniciativas de este tipo.

Así que sin más preámbulos, me dispongo a dejar constancia de lo aprendido en la primera clase.

¿Que hay de nuevo en HTML5?

El principal cambio en el que nos centraremos hoy radica en los elementos de la nueva semántica: Es decir, en el HTML vamos a empezar a utilizar unas etiquetas que son mucho más específicas (en lugar de los ambiguos DIV a los que estábamos acostumbrados).

Esto no significa que dejemos de utilizar DIVs, sino que ahora tenemos más etiquetas.

Por ejemplo, algunas de las más comunes:

1
<header><footer><nav><aside><article>

Su mismo nombre ya nos dice más o menos dónde irán ubicadas.

En w3schools tienen una lista completa de todas las etiquetas nuevas.

Vamos a comparar como se creaba antes la estructura de una web y como se hará com HTML5:

Antigua estructura:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id='header'><h1>Título de la página</h1></div>

<div class='post'>Contenido del post</div>

<div class='post'>Contenido del post</div>

<div id='sidebar'>
  <ul>
    <li>Elementos típicos del sidebar</li>
  </ul>
</div>

<div id='footer'>
  Elementos típicos del footer
</div>

Nueva estructura:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<header><h1>Título de la página</h1></header>

<article>Contenido del post</article>

<article>Contenido del post</article>

<aside>
  <ul>
    <li>Elementos típicos del sidebar</li>
  </ul>
</aside>

<footer>
  Elementos típicos del footer
</footer>

Como podéis observar, las etiquetas son mucho más semánticas y específicas.

Ejemplos reales : típicos usos:

1
2
3
4
5
6
7
8
9
10
11
12
<header>
  <hgroup>
    <h1>Thai cookery school></h1>
    <h2>Learn authentic Thai cookery in your own home </h2>
  </hgroup>
  <nav>
    <ul>
      <li>Home</li> <li> <a href=”courses.html”>Cookery courses</a> </li>
      <li><a href=”contact.html”>Contact</a></li>
    </ul>
  </nav>
</header>
1
2
3
4
5
6
7
<nav>
  <h2>Main navigation</h2>
  <ul>
    <li><a href=”/about”>About me</a></li>
    <li><a href=”/news”>News</a></li>
  </ul>
</nav>

<aside>

(Por ejemplo un sidebar de un blog)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<aside>
  <nav>
    <h2>Pages</h2>
    <ul> .. </ul>
  </nav>
  <nav>
    <h2>Recent comments</h2>
    <ul> ... </ul>
  </nav>
  <nav>
    <h2>Last posts</h2>
    <ul> ... </ul>
  </nav>
</aside>
1
2
3
<footer>
  <small>&copy;2010 Xpenta.com. Design by dpam23</small>
</footer>

Mínima expresión de página en HTML5

1
2
3
4
5
6
7
8
9
10
<!doctype html>
<html lang=en>
  <head>
    <meta charset=utf-8>
    <title>mi blog</title>
  </head>
  <body>
    Hoy dictaré la clase de html5. Desayunaré, Y volveré a la cama.
  </body>
</html>

Aquí podemos observar que las directrices básicas son mucho más escuetas (como por ejemplo el doctype).

Estilando HTML5 con CSS:

Bien, ahora que ya tenemos un poco más clara la estructura de etiquetas en HTML5, tendremos que aplicarle estilos.

En todos los navegadores menos uno, es muy simple dar estilos a un elemento. Igual que hacíamos con HTML4, creamos una hoja de estilos CSS y desde allí hacemos las llamadas a los elementos.

por ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
header {
  background-color : #ececec;
  text-align       : center;
  color            : #bbb;
}
  header h1 {
    padding : 30px;
    margin  : 0;
  }

article {
  padding       : 30px;
  width         : 570px;
  float         : left;
  border-bottom : 1px solid #ebebeb;
}
aside {
  width            : 269px;
  float            : right;
  border-left      : 1px solid #ebebeb;
  background-color : #F7F6F6;
}
  aside nav, aside section {
    padding:30px;
  }

Lo que más sorprende de este nuevo sistema, es que por defecto CSS asume que este tipo de elementos tienen la propiedad display:inline, así que si queremos poner alturas o anchuras, tendremos que cambiarlo a display:block;

1
2
3
4
/* Tell the browser to render HTML 5 elements as block */
header, footer, aside, nav, article, section {
  display: block;
}

Peculiaridades de Internet explorer (IE) y HTML5

Con las versiones actuales de IE, siguiendo las instrucciones anteriormente expuestas, la página permanecerá sin estilos. O mejor dicho, tendrán estilos aquellos elementos típicos de HTML4 (span, div, a…) pero dejará sin estilos los nuevos de HTML5 (header, footer, section, canvas…)

para evitar esto, tendremos que añadir un script al documento:

1
2
3
4
5
6
<script>
  document.createElement('header');
  document.createElement('nav');
  document.createElement('article');
  document.createElement('footer');
</script>

De esta manera, y son motivo aparente, los elementos cojerán los estilos indicados en el CSS. Tendremos que crear una línea para cada elemento que queramos crear. Este script lo podemos poner con los típicos condicionales para que sólo los lea IE, pero si los dejamos para todos los navegadores no pasa nada.

DEMO: Ejemplo práctico

Todo esto está muy bien, pero ¿y si lo vemos todo junto? Os dejo una página sencilla en HTML5 y algo de CSS3. Es el primer ejercicio que nos han pedido, veremos a ver que nota me ponen! :)

http://demos.racotecnic.com/tutorials/2011/HTML5/

Documentación interesante:

PD: Gracias a los alumnos del curso por compartir estos enlaces tan interesantes!