js-icon

Boilerplate (esqueleto) de plugin jQuery de la casa y en español ;)

Hola!. Me complace anunciar que pongo a disposición de todo el que quiera hacer plugins de jQuery, el boilerplate que he estado usando en los últimos años.

Es otro boilerplate como los demas, con la diferencia que al estar usandolo todo el tiempo lo fui puliendo poco a poco. Pero mucho mas importante: está en español XD

Pase pasen y vean https://github.com/capynet/jQueryPluginBoilerplate

Chau!

PHP

Detectar el navegador y toda su información desde PHP o Javascript

Hola, les hago una intro bien corta y pasamos al código.

Browscap es un proyecto que recoge la información de todos los navegadores web en una base de datos que distribuye de forma gratuita y en varios formatos.

PHP tiene soporte nativo para hacer uso de esta DB, pero debido a un par de limitaciones que PHP aun no ha solventado en su API, en GitHub un usuario creó una soluciona basada también en la DB de Browscap pero mucho mas robusta y fácil de usar.

Bien, llegados a este punto sabemos que lo que necesitamos usar es Browscap junto a la librería que se creó para explotarla.

Para mas información, toda la documentación completa de php-browscap la pueden encontrar aquí. Yo me centro en una implementación mucho mas simple porque realmente creo que de los 40 datos que entrega sobre el navegados, con apenas 7 tenemos el 99% de los casos cubiertos.

Acá tienen un ejemplo funcionando para que vean lo rápido y certero que es a la hora de conseguir la info del browser (pruébenlo con mas de uno)

Ok, pongamos algo de código que sino todo esto es muy aburrido:
Vamos a aprovechar esta librería bajo dos situaciones. en PHP y en JS

En PHP

Descarguen la librería php-browscap via Composer o a mano. Pueden saber mas sobre como descargarla aquí
Ahora que la tenemos creamos un archivo index.php para usarla.

<?php
require 'vendor/autoload.php';
use phpbrowscap\Browscap;
$bc = new Browscap('cache');
/** @var stdClass $current_browser */
$current_browser = $bc->getBrowser();
?>

Fácil, no?. Ok ya tenemos un stdClass en $current_browser con toda la información disponible del browser que llame a index.php

Aplicaciones?

Puedes por ejemplo tomar decisiones del tipo

<?php
$curr_br = $current_browser->Browser;
$CoolBrowsers = $curr_br == 'Chrome' || $curr_br == 'Firefox';

if ($CoolBrowsers) {
  add_asset('css', 'just-for-cool-browsers.css');
  $output = renderAdvancedLayout();
}
else {
  add_asset('css', 'just-for-bad-bad-browsers.css');
  $output = renderBasicLayout();
}
?>

También puedes detectar si están accediendo desde un móvil o una tablet, incluso saber si es Android, IOS, la version del SO, etc. Con toda esta información a resumidas cuentas puedes entregar un contenido muy preciso.

Beneficios

Estas entregando contenidos a medida, ergo necesitas entregar menos HTML, CSS, Js y las imágenes mas adecuadas al navegador. Esto finalmente se traduce en menos procesamiento del lado del servidor, lo que se entrega pesa menos KB y el navegador tarda menos en leer y renderizar todos los recursos (en especial JS).

En JS

Por ultimo me gustaría compartir un tip para poder aprovechar la detección de browsers en JS:
A ver, con js podemos detectar el browser usando un poco de expresiones regulares, pero llegar al detalle y la precisión a la que llega browscap es virtualmente imposible. Dicho esto, comentar que se puede entregar toda la información del browser detectado desde php simplemente haciendo esto:

<script type="application/javascript">
  var browser = <?php= json_encode($current_browser = $bc->getBrowser()); ?>;
  alert(browser.Browser);
  alert(browser.Version);
</script>

Hasta la próxima!

Desplegar una aplicación Node.js como servicio systemd (demonio)

En el mundo linux hay dos gestores de demonios que se suelen usar en casi todas las distribuciones. Upstart o systemd.

Si lo que quieres es crear un demonio para Ubuntu o cualquier otro SO que use Upstart, tienes que leer este articulo, y si lo que quieres es crear el demonio para Debian u otros que usen systemd, sigue leyendo este post.

Vamos al lio. Para nuestro demonio vamos a necesitar 3 cosas: forever, un script para crear el servicio y un script para avisar a monit para que mire nuestro servicio y lo reinicie si se cae.

El script del servicio

Como me gusta ponértelo fácil, voy a obviar la explicación y simplemente les digo que copies el siguiente código y lo pegues dentro de un archivo que se llame como el servicio que quieres crear dentro de /etc/init.d/my-service (por ejemplo).

Dentro de este archivo solo tienes que tocar dos o tres cosas al principio del archivo en la zona de configuración. Es fácil, básicamente es poner el nombre del servicio (en mi ejemplo es “my-service”), el directorio donde está alojado, y el nombre del archivo principal de la aplicación.
Hay algunas cositas mas que se pueden configurar, pero en general con esto basta.

#!/bin/bash
# This service requires you have forever app installed in your system.


### CONFIG ZONE
# This is the service name. It cant have any special char or space.
serviceName="my-service"

# Where is located your app
appFilePath="/var/node/my-service-node-app"

# Whats the name of your main app file.
appIndex="app.js"

enviroment=production
### END CONFIG ZONE


###### DO NOT EDIT BELOW THIS LINE ######
# Source function library.
. /lib/lsb/init-functions

pidFile="/var/run/$serviceName.pid"
logFile="/var/run/$serviceName.log"

command="node"
nodeApp="$appFilePath/$appIndex"
foreverApp="forever"

start() {
	echo "Starting $serviceName"
	PATH=/usr/local/bin:$PATH
	export NODE_ENV=$enviroment
	$foreverApp start --pidFile $pidFile -l $logFile -a -d -c "$command" $nodeApp
	RETVAL=$?
}

restart() {
	echo -n "Restarting $serviceName"
	$foreverApp restart $nodeApp
	RETVAL=$?
}

stop() {
	echo -n "Shutting down $serviceName"
	$foreverApp stop $nodeApp
	RETVAL=$?
}

status() {
   echo -n "Status $serviceName"
   $foreverApp list
   RETVAL=$?
}

case "$1" in
   start)
        start
        ;;
    stop)
        stop
        ;;
   status)
        status
       ;;
   restart)
   	restart
        ;;
	*)
       echo "Usage:  {start|stop|status|restart}"
       exit 1
        ;;
esac
exit $RETVAL

Tambien dejo el gist para que cualquier mejora que quieras añadir lo hagas por esta via.
https://gist.github.com/capynet/2de13de81ef8a71f2b2f

Ya tenemos servicio! ¿Y como lo usamos?. Pues como cualquier servicio:

sudo service my-service start
sudo service my-service stop
sudo service my-service restart
sudo service my-service status

Esto debería alcanzar, pero no. Este demonio no sabe volver a arrancarse si la aplicación de nodejs se para. Esa parte la vamos a solucionar usando monit.

Configurar monit

El rol de monit es el de “mirar” todo el tiempo al demonio que creamos anteriormente, y arrancarlo si este se muere.

En Debian y la mayoría de distros, a los archivos de configuración que tenemos que crear (uno por demonio) los pondremos en “/etc/monit/monitrc.d/“.
Yo por lo general creo un archivo con el formato monit-[MI-SERVICIO].conf.

Dentro de este archivo SIEMRPE vamos a usar el siguiente formato, así que simplemente cópialo, pon el nombre del demonio y el puerto, guarda los cambios, reinicia monit y listo.

check process my-service with pidfile "/var/run/my-service.pid"
        start program = "/etc/init.d/my-service start"
        stop program = "/etc/init.d/my-service stop"
        if failed
                port [THE NODEJS PORT HERE] protocol HTTP
                request / with timeout 10 seconds
        then restart

De nuevo, aqui está el gist para que contribuyas si quieres.
https://gist.github.com/capynet/c7aa09a996fe909a0960

NOTA: En algunas instalaciones de monit, hay que tocar su archivo de configuración general para que incluya los archvos de monitorización que añadamos. El archivo es /etc/monit/monitrc y tienes que editarlo como sudo y añadir al final “include/etc/monit/monitrc.d/*” si no lo tuviera ya puesto.

Bueno si a estas alturas todavía no tienes instalado forever, instalalo con sudo npm install forever -g

Hemos acabado, ya puedes arrancar monit o reiniciarlo si ya estaba corriendo. Monit va a usar el script que creamos para el para ver si el servicio está corriendo, si no es así, el solito se encarga de levantarlo y mantenerlo en funcionamiento inclusive después de reiniciar el servidor.

Chau!

CSS & maquetación

Front-end: Maquetar un buscador (input + botón) para que sea 100% fluido / responsive

La idea detrás de esto fue lograr que el buscador esté siempre al 100% del espacio que tenga disponible y no tener que preocuparme de dimensionar los input para que encajen y que el botón no caiga a dos lineas cuando se queda sin espacio.

Pueden ver como funciona si re dimensionan la ventana del navegador:

See the Pen vJBnL by Marcelo Tosco (@capynet) on CodePen.

CSS & maquetación

Front-end: Títulos multi linea con padding por cada una de las lineas

Así es como vemos un titulo cuando tiene aplicado un padding sin mas

texto-multilinea-error

Y asi es como queremos que se vea

titulo-con-padding-en-cada-linea

No voy a explicar toda la lógica que hay detrás de esto. Vayamos a la solución.

Este es el html. en div.titulo tenemos el background y en .highlight el background del texto, el padding que queremos que tenga y el hack mediante la propiedad box-shadow que es la que nos termina agregando los pedacitos de padding que nos falta entre lineas.

<div class="titulo">
    <span class="highlight">Títulos multi linea con padding por cada linea</span>
</div>
.titulo {
    padding-top: 32px;
    width: 700px;
    height: 200px;
    text-align: center;
    font-family: arial, helvetica, sans-serif;
    background: url('fondo.jpg') no-repeat 50% 50%;
}

.highlight {
    font-size: 50px;
    text-transform: uppercase;
    text-align: center;
    color: #fff;

    /*Esto es lo importante*/
    display: inline; /* Aseguramos que el elemento sea inline */
    background: black; /* Color de fondo del texto */
    line-height: 1.6em; /* Espaciado entre lineas */
    padding: 7px 0; /* padding deseado */
    box-shadow: 14px 0 0 black, -14px 0 0 black;
}

Del CSS que puse solo hagan caso a las lineas que tienen “/*Esto es lo importante*/“. El resto es decoración.

No comenté el box-shadow porque prefiero hacerlo aquí: La magia se da en box-shadow. Solo tienes que tocar dos parámetros. El color cámbialo de black al que hayas puesto en la propiedad background, y los valores 14px y -14px ajustalos para arriba o para abajo hasta que te guste el padding lateral que hay en cada una de las lineas.

Listo!

Artículo original: css-tricks.com

CSS & maquetación

CSS: Centrar vertical y horizontalmente un elemento sin saber su tamaño

Centrar un elemento cuando se sabe el tamaño que tiene es fácil. Pero y si el elemento que queremos centrar tiene un tamaño expresado en % o no tiene un tamaño especificado, ya no es tan fácil centrarlo.

Ahora que prácticamente IE8 ya no se usa, lo podemos quitar de la ecuación y usar CSS un poco mas avanzado.
Dicho lo anterior, ahora que podemos usar la propiedad transform: translate();, ya podemos centrar vertical y horizontalmente cualquier elemento.

<div class="centrado-porcentual">
  No nos hace falta conocer el tamaño de esta caja para poder centrarla vertical y horizontalmente :)
</div>
.centrado-porcentual {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    -webkit-transform: translate(-50%, -50%);
}

Resultado:
centrado-vert-hoz-transform

NOTA: si el elemento que quieres centrar está dentro de otro, recuerda que el padre tiene que tener una posición relativa.

Chau!

Idea original: css-tricks.com

Druplicon

Drupal 7: Controlar completamente los template de Field collection

El muy jodido tiene mas vueltas que una oreja cuando de theming se trata.

NOTA: todos los templates que hay que crear ponlos en tu theme. Lo ideal es dentro del directorio tu_theme/templates/field_collection o algo similar.

Primero creamos el tpl field-collection-view.tpl.php que es el wrapper que hay entre el html del field y el del field collection. Pon esto dentro:

<?php print $element['#children']; ?>

Luego field-collection-item.tpl.php que es el tpl que controla el render de cada uno de los item individuales de la colección:

<?php print render($content); ?>

Por ultimo necesitamos field–field-collection.tpl.php, un tpl para el field que contiene todo lo que field collection entregue:

<ul class="coleccion de items">
  <?php foreach ($items as $delta => $item): ?>
    <li><?php print render($item); ?></li>
  <?php endforeach; ?>
</ul>

Y listo! ya tenes control a todos los niveles del markup que va a entregar un field collection.

Chau!

Druplicon

Drupal 7 : Crear un template block–bean–type-ENTITY-TYPE.tpl.php para bundles del módulo Bean

Caso de uso. Creaste un bean del tipo “banner” y quieres que el block.tpl.php tenga un marcado específico para este tipo de contenidos. O lo que es lo mismo que decir que quieres tener un block–bean–type-banner.tpl.php

La solución es poner este preprocess:

<?php
/**
 * Implements hook_preprocess_block().
 */
function TU_THEME_O_MODULO_preprocess_block(&$vars) {
  // Añadimos theme suggestions por tipo de bean.
  if ($vars['block']->module == "bean") {
    /** @var $bean Bean */
    $bean = bean_load_delta($vars['block']->delta);
    $vars['theme_hook_suggestions'][] = 'block__bean__type_' . $bean->type;
  }
}

Y ya podés copiar block.tpl.php a tu theme, renombrarlo a block–bean–type-banner.tpl.php, personalizarlo y borrar el cache.

Chau!

Druplicon

Drupal 7: Hacer bypass a page.tpl.php y html.tpl.php programáticamente

¿Y si necesitaras entregar una página sin los CSS ni JS ni el HTML (cabeceras, footer, sidebars) que viene por defecto?

Solo hay que hacer dos cosas:

1. Añadir la propiedad ‘delivery callback’ a tu menu callback

<?php
$items['factura/%node'] = array(
  'title' => 'Entrega una factura',
  'page callback' => 'generar_factura',
  'delivery callback' => 'mi_delivery_page',
  'access callback' => TRUE,
);

2. Añadir esta función.

No hace falta modificarla. Si querés cambiarle el nombre no te olvides de modificar también el nombre en el delivery callback del menú.

<?php
/**
 * Delivery callback.
 *
 * Entrega el contenido así como viene sin pasar ni
 * por page.tpl.php ni por html.tpl.php
 *
 */
function mi_delivery_page($page_callback_result) {

  if (isset($page_callback_result) && is_null(drupal_get_http_header('Content-Type'))) {
    drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
  }

  global $language;
  drupal_add_http_header('Content-Language', $language->language);
  print $page_callback_result;
  drupal_page_footer();
}

Borra cache y listo.

Chau!