Php – Inotify – al estilo dropbox…

Aquí les dejo un ejemplo básico del uso de inotify con php.
Con inotify podemos realmente monitorizar una carpeta y saber
por medio de los eventos que es lo que esta sucediendo, en el script
de abajo les dejo un mini ejemplo pero muy claro para que puedan usarlo
en diversas aplicaciones.


//sudo pecl install -f inotify

$notificador = inotify_init();

$filePath = 'data/';

inotify_add_watch($notificador, $filePath, IN_ALL_EVENTS);

    while (1) {
        $eventos = inotify_read($notificador);
         foreach($eventos as $evento) {
              switch($evento['mask']) {
                case IN_CLOSE_WRITE:
                    //este es el evento mas
                    //usado, porque nos dice cuando
                    // el archivo termino la carga
                    // y esta libre.
                    sync($evento['name']);
                    break;
                case IN_MODIFY:
		    //algo
                    break;
                case IN_ACCESS:
		    //algo
                    break;
                case IN_CREATE:
		   //algo
                    break;
                case IN_MOVED_TO:
		   //algo
		    break;
                case IN_MOVED_FROM:
		   //algo
		   break;
                case IN_DELETE:
                   //algo
		   break;
                case IN_DELETE_SELF:
		  //algo
                   break;
                case IN_MOVE_SELF:
		  //algo
		   break;
            }
        }
    }

    function sync($file)
    {
        echo "Enviando el archivo -".$file."n";

        //rsync $OPT $BDIR $BSERVER::$USER/current
        //ftpClass::send('$file','$dir');

        sleep(5);
        echo 'Esperando otro archivo !!!' ."n";
    }
Posted in Uncategorized

jQuery and Javascript Performance Tips – Best Practices

1) En un loop no acceda a la propiedad length de un array, en su lugar debe cachearla en una variable:

var myLength = myArray.length;
for (var i = 0; i < myLength; i++) {
// codigo…
}


2) Agregue contenido Afuera del loop:

Tocar el DOM tiene un costo en cuanto al rendimiento, si tiene que hacerlo hagalo cuando finalice el loop, entonces solo accederá al DOM una sola vez.

// Esto es incorrecto
$.each(myArray, function(i, item) {
var newListItem = ‘<li>’ + item + ‘</li>’;
$(‘#content’).append(newListItem);
});

// Mejor: haga esto
var frag = document.createDocumentFragment();
$.each(myArray, function(i, item) {
var newListItem = ‘<li>’ + item + ‘</li>’;
frag.appendChild(newListItem);
});
$(‘#content’)[0].appendChild(frag);

// o tambien esto
var myHtml = ”;
$.each(myArray, function(i, item) {
html += ‘<li>’ + item + ‘</li>’;
});
$(‘#content’).html(myHtml);

3) Mantenga el codigo Limpio:

No repita el código, si lo está repitiendo lo está haciendo mal.

//Mal
if ($eventfade.data(‘currently’) != ‘showing’) {
$eventfade.stop();
}
if ($eventhover.data(‘currently’) != ‘showing’) {
$eventhover.stop();
}
if ($spans.data(‘currently’) != ‘showing’) {
$spans.stop();
}

//Correcto
var $elems = [$eventfade, $eventhover, $spans];
$.each($elems, function(i,elem) {
if (elem.data(‘currently’) != ‘showing’) {
elem.stop();
}
});

4) Tenga cuidado con las funciones anonimas:

Las funciones anónimas se suelen volver un dolor de cabeza. Son difíciles de debuguear, mantener, testear y reusar. En su lugar puede usar un objeto literal para organizar y nombrar sus handlers y callbacks.

// Incorrecto
$(document).ready(function() {

$(‘#magic’).click(function(e) {
$(‘#yayeffects’).slideUp(function() {
// …
});
});

$(‘#happiness’).load(url + ‘ #unicorns’, function() {
// …
});
});

// Correcto
var PI = {
onReady : function() {
$(‘#magic’).click(PI.candyMtn);
$(‘#happiness’).load(PI.url + ‘ #unicorns’, PI.unicornCb);
},
candyMtn : function(e) {
$(‘#yayeffects’).slideUp(PI.slideCb);
},
slideCb : function() { … },
unicornCb : function() { … }
};

$(document).ready(PI.onReady);

5) Optimice los selectores:

La optimización de los selectores es menos importante de lo que solia ser. La mayoría de los browsers implementan document.querySelectorAll() y la carga de la selección pasa de jQuery hacia el navegador. Sinembargo siguen habiendo algunas cosas para tener en cuenta.

a) Empezar la selección co un ID es siempre lo mejor

// rápido
$(‘#container div.robotarm’);

// más rapido
$(‘#container’).find(‘div.robotarm’);

La opción $.fn.find es más rápida por que la primer selección es hecha sin ir atravez del “Sizzle selector engine”. Las selecciones que son hechas unicamente atravez del ID son manejadas usando el método document.getElementById(), que es extremadamente rápido ya que es nativo del browser.

b) Especificidad de la selección

Sea más específico en la derecha del selector y menos especifico en la izquierda.

// Sin optimizar
$(‘div.data .gonzalez’);

// optimizado
$(‘.data td.gonzalez’);

En el selector de la derecha si es posible use tag.class y en la izquierda solo el tag o solo la clase.También debe evitar especificar demasiado:

$(‘.data table.attendees td.gonzalez’);

// esto es mejor, si es posible debe eliminar el los selectores del medio
$(‘.data td.gonzalez’);

c) Evite utilizar el selector universal

$(‘.buttons > *’); // extremadamente lento
$(‘.buttons’).children(); // mucho mejor
$(‘.gender :radio’); // selección universal implicita
$(‘.gender *:radio’); // selección universal explicita
$(‘.gender input:radio’); // Mucho mejor

Posted in Uncategorized

Symfony2: Introducción

Introducción a los conceptos básicos de Symfony2 y como crear una simple aplicación, en este caso, un weblog.

Posted in Uncategorized

PHP Performance Validation Tips

I’ll start by sharing this blog-posts

Posted in Uncategorized

Great article on Moblie Development

Hey, a great coleague of my has sent me this great article about mobile development.

http://www.alistapart.com/articles/return-of-the-mobile-stylesheet


Tagged with:
Posted in Estandares

Symfony PHP -Publicar direccion de email – tecnicas anti-spam

Quien no ha transformado las direcciones de email a imagenes para evitar que nos registren los spammers?

A continuación listo algunas alternativas para evitar este problema:

1. Utilizacion de javascript

2. Emails en forma de imágenes.

3. Instanciar un formulario de contáctenos con los nombre de los titulares/destinos y manejar el envio internamente.

Tagged with: , ,
Posted in Estandares, HTML - CSS - Javascript, Snippets

PHP Symfony Geo-Location by ZIP and Google Map

Proveemos a continuacion una clase que les facilitara la Geo Localizacion en base a un codigo posta (ZIP) americano. Esperamos les sirva.

class GeoLocationGM
{

	public static function getLocationByZipcode( $zipcode = null )
	{
		//$key = "";
		$key = sfConfig::get('sf_google_key');
		$result = null;

		if ( $zipcode ) {

			$result = array();

			$zipcode = trim( $zipcode );
			$uri 	 = "http://maps.google.com/maps/geo?q=".$zipcode."&output=xml&key=".$key."&oe=utf8";
			$xml_str = file_get_contents($uri);

			if( !empty( $xml_str ) ){
				$xml = new DOMDocument();
				$xml->loadXML($xml_str);

				$result['code'] 					= @$xml->getElementsByTagName('code')->item(0)->nodeValue;
				$result['address'] 					= @$xml->getElementsByTagName('address')->item(0)->nodeValue;
				$result['CountryNameCode'] 			= @$xml->getElementsByTagName('CountryNameCode')->item(0)->nodeValue;
				$result['CountryName'] 				= @$xml->getElementsByTagName('CountryName')->item(0)->nodeValue;
				$result['AdministrativeAreaName'] 	= @$xml->getElementsByTagName('AdministrativeAreaName')->item(0)->nodeValue;
				$result['SubAdministrativeAreaName']= @$xml->getElementsByTagName('SubAdministrativeAreaName')->item(0)->nodeValue;
				$result['LocalityName'] 			= @$xml->getElementsByTagName('LocalityName')->item(0)->nodeValue;
				$result['PostalCodeNumber'] 		= @$xml->getElementsByTagName('PostalCodeNumber')->item(0)->nodeValue;

			}

		}

		return $result;
	}
}
Tagged with: , ,
Posted in APIs, Frameworks, Snippets, Symfony

ubuntu apache ssl

Aqui le dejo la forma mas facil de activar ssl en apache.

ejecutar estos comandos:

1 – sudo apt-get install ssl-cert

2 – sudo mkdir /etc/apache2/ssl

3 – sudo /usr/sbin/make-ssl-cert /usr/share/ssl-cert/ssleay.cnf /etc/apache2/ssl/apache.pem

4 – sudo ln -s /etc/apache2/mods-available/ssl.load /etc/apache2/mods-enabled/

5 – sudo gedit /etc/apache2/sites-enabled/000-default

6 – copien esto :

<VirtualHost *:443>

   ServerName myServerName
   DocumentRoot "/var/www/project/web"
   DirectoryIndex index.php

   AllowOverride All
   Allow from All

   SSLEngine On
   SSLCertificateFile /etc/apache2/ssl/apache.pem

</VirtualHost>

6 – sudo service apache2 restart

Listo !!! ya deberias poder usarlo sobre https://serverName

Posted in Snippets

Implementando un conjunto anidado de datos en Symfony (Nested Set)

Todos los programadores de cualquier lenguaje, alguna vez han tenido que implementar una estructura de arbol para un menu, un arbol de categorias, de productos, etc. El problema radica al llevar los arboles a una base de datos, donde el standar es lo que se llama un conjunto anidado o nested set. Afortunadamente doctrine a incorporado el comportamiento a su core, pero symfony aun no se entiende muy bien con el mismo.
En este post vamos a implementar la funcionalidad de doctrine nested set en un modulo generate-admin de symfony para manejar un arbol de categorias simple.
Para ello vamos a agregar el comportamiento en “actAs” en el esquema

# /config/doctrine/schema.yml
Category:
  actAs:
    NestedSet:
      hasManyRoots: true
      rootColumnName: root_id
    columns:
      name: string(255)

Aunque la única columna que se especifica es el nombre, por defecto Doctrine creará un campo de clave primaria denominado id, y los campos LFT, RGT, LEVEL y ROOT_ID (opcional), que se utilizan para el conjunto anidado. Para una explicación completa de exactamente cómo funciona el conjunto anidado, se puede hacer click aqui.

Vamos a tomar en cuenta que ya se ha creado un modulo categoría con generate-admin, y si como podra ver en el index de dicho modulo se muestran todos los campos antes descriptos.

Sin embargo, cuando se crea un nuevo elemento, o al modificar o eliminar cualquiera de los existentes, Symfony lo tratará como a cualquier otro registro, que puede provocar un comportamiento inesperado. Para preservar la estructura cuando se modifica un conjunto anidado, es necesario utilizar primero getNode () en todos los registros existentes, o uno de varios métodos proporcionados por Doctrine cuando se crea un nuevo registro.

El primer paso es la implementación el metdo configure() en CategoryForm.class.php para poder eliminar los campos generados automáticamente y reemplazarlos con un menú desplegable para seleccionar los padres de la categoría.

class CategoryForm extends BaseCategoryForm
{
  public function configure()
  {
    // create a new widget to represent this category's "parent"
    $this->setWidget('parent', new sfWidgetFormDoctrineChoiceNestedSet(array(
      'model'     => 'Category',
      'add_empty' => true,
    )));

    // if the current category has a parent, make it the default choice
    if ($this->getObject()->getNode()->hasParent())
    {
      $this->setDefault('parent', $this->getObject()->getNode()->getParent()->getId());
    }

    // only allow the user to change the name of the category, and its parent
    $this->useFields(array(
      'name',
      'parent',
    ));
    // set labels of fields
    $this->widgetSchema->setLabels(array(
      'name'   => 'Category',
      'parent' => 'Parent category',
    ));
    // set a validator for parent which prevents a category being moved to one of its own descendants
    $this->setValidator('parent', new sfValidatorDoctrineChoiceNestedSet(array(
      'required' => false,
      'model'    => 'Category',
      'node'     => $this->getObject(),
    )));
    $this->getValidator('parent')->setMessage('node', 'A category cannot be made a descendent of itself.');
  }
}

como verán se ha implementado un nuevo widget, sfWidgetFormDoctrine, y el validador, sfValidatorDoctrineChoiceNestedSet. Son similares a sfWidgetFormDoctrineChoice y sfValidatorDoctrineChoice, modificados para funcionar con un conjunto anidado. Esencialmente, sfWidgetFormDoctrineChoiceNestedSet extiende de sfWidgetFormDoctrineChoice. Y sfValidatorDoctrineChoiceNestedSet añade una validación adicional por asegurarse de que el valor seleccionado de “padre” no es un descendiente del registro actual.
Cuando se desea sobreescribir alguna parte del guardado del formulario, el mejor lugar para hacerlo es por lo general en el método doSave. Este método se llama después de la validación del mismo, pero antes de que el formulario sea sealmente guardado. Por lo tanto, aqui es exactamente donde tenemos que reemplazar llamada para guardar de Symfony.

class CategoryForm extendsBaseCategoryForm
{
  ...

  /**
   * Updates and saves the current object. Overrides the parent method
   * by treating the record as a node in the nested set and updating
   * the tree accordingly.
   *
   * @param Doctrine_Connection $con An optional connection parameter
   */
  public function doSave($con = null)
  {
    // save the record itself
    parent::doSave($con);
    // if a parent has been specified, add/move this node to be the child of that node
    if ($this->getValue('parent'))
    {
      $parent = Doctrine::getTable('Category')->findOneById($this->getValue('parent'));
      if ($this->isNew())
      {
        $this->getObject()->getNode()->insertAsLastChildOf($parent);
      }
      else
      {
        $this->getObject()->getNode()->moveAsLastChildOf($parent);
      }
    }
    // if no parent was selected, add/move this node to be a new root in the tree
    else
    {
      $categoryTree = Doctrine::getTable('Category')->getTree();
      if ($this->isNew())
      {
        $categoryTree->createRoot($this->getObject());
      }
      else
      {
        $this->getObject()->getNode()->makeRoot($this->getObject()->getId());
      }
    }
  }
}

Despues agregamos el codigo personalizado del widget sfWidgetFormDoctrineChoiceNestedSet y el validador sfValidatorDoctrineChoiceNestedSet


// /lib/widget/sfWidgetFormDoctrineChoiceNestedSet.class.php
class sfWidgetFormDoctrineChoiceNestedSet extends sfWidgetFormDoctrineChoice
{
  public function getChoices()
  {
    $choices = array();
    if (false !== $this->getOption('add_empty'))
    {
      $choices[''] = true === $this->getOption('add_empty') ? '' : $this->getOption('add_empty');
    }

    if (null === $this->getOption('table_method'))
    {
      $query = null === $this->getOption('query') ? Doctrine_Core::getTable($this->getOption('model'))->createQuery() : $this->getOption('query');
      $query->addOrderBy('root_id asc')
            ->addOrderBy('lft asc');
      $objects = $query->execute();
    }
    else
    {
      $tableMethod = $this->getOption('table_method');
      $results = Doctrine_Core::getTable($this->getOption('model'))->$tableMethod();

      if ($results instanceof Doctrine_Query)
      {
        $objects = $results->execute();
      }
      else if ($results instanceof Doctrine_Collection)
      {
        $objects = $results;
      }
      else if ($results instanceof Doctrine_Record)
      {
        $objects = new Doctrine_Collection($this->getOption('model'));
        $objects[] = $results;
      }
      else
      {
        $objects = array();
      }
    }

    $method = $this->getOption('method');
    $keyMethod = $this->getOption('key_method');

    foreach ($objects as $object)
    {
      $choices[$object->$keyMethod()] = str_repeat(' ', ($object['level'] * 4)) . $object->$method();
    }

    return $choices;
  }
}
// /lib/validator/sfValidatorDoctrineChoiceNestedSet.class.php
class sfValidatorDoctrineChoiceNestedSet extends sfValidatorBase
{
  /**
   * Configures the validator.
   * Available options:
   *   model: The model class (required)
   *   node:   The node being moved (required)
   *
   * @see sfValidatorBase
   */
  protected function configure($options = array(), $messages = array())
  {
    $this->addRequiredOption('model');
    $this->addRequiredOption('node');

    $this->addMessage('node', 'A node cannot be set as a child of itself.');
  }

  protected function doClean($value)
  {
    if (isset($value) && !$value)
    {
      unset($value);
    }
    else
    {
      $targetNode = Doctrine::getTable($this->getOption('model'))->find($value)->getNode();
      if ($targetNode->isDescendantOfOrEqualTo($this->getOption('node')))
      {
        throw new sfValidatorError($this, 'node', array('value' => $value));
      }

      return $value;
    }
  }
}

Después de implementar el widget personalizado y su validador, debemos ser capaces de agregar y editar registros correctamente. Lo que aún queda es la eliminación de registros existentes correctamente, ya que no se maneja desde dentro el marco del CategoryForm. Además, voy a mostrar cómo hacer algunos pequeños cambios de formato a la página de índice, por lo que la jerarquía se muestra en una forma mucho más presentable y comprensible.

Posted in Uncategorized

HTML / CSS – Mejores Practicas

1)- Cuando Usar ID y cuando usar Class?

Este es un error muy común entre los principiantes que no entienden la diferencia entre ellos. Básicamente debe haber un único elemento en la página que tenga el mismo ID, mientras que una misma CLASE puede ser usada por varios elementos dentro de la misma página.

Incorrecto:

<div id="my-id"></div>

<div id="my-id"></div>

Como podemos ver este ejemplo está mal, se repite el ID dentro del mismo documento, mientras que en el siguiente se puede observar el correcto uso de las Clases.

Correcto:

<div class="my-class"></div>

<div class="my-class"></div>

<div class="my-class"></div>

De cualquier manera no hay que aplicarle clases a todas las colleciones de elementos. Si puedes encapsular una colección de elementos dentro de otro, ya no necesitaras definir clases a cada uno de los elementos.

Incorrecto:

<div class="letra-grande"></div>

<div class="letra-grande"></div>

<div class="letra-grande"></div>

<div class="letra-grande"></div>

<div class="letra-grande"></div>
.letra-grande { Font-size: 16px; }

Correcto:

<div class="letra-grande">
    <div></div>
    <div></div>
    <div></div>
</div>
.letra-grande { Font-size: 16px; }

2)- Sopa de DIVs

Esto sucede cuando existen demasiados DIVs en el documento para lograr algún efecto. La sopa de DIVs crea serios problemas: las páginas cargan más lento, el parseo del DOM es más lento, más CSS necesita ser escrito, y el código vuelve menos mantenible.

La principal causa de la sopa de DIVs es un mal entendido básico de como funcionan las hojas de estilo. Por ejemplo, un diseñador desea crear 3 divs anidados y aplicar un estilo particular para cada DIV, digamos background-color en el primero, marginpadding en el segundo, y font-size en el tercero. Lo que el diseñador no entiende es que a menos que esta estructura sea absolutamente necesaria, todas estas propiedades pueden ser combinadas y usar un solo DIV.

Incorrecto:

<div style="background: #ccc;">
    <div style="margin:10px; padding:5px">
        <div style="font-size:12px">...</div>
    </div>
</div>

Correcto:

<div id="post-25">...</div>
#post-25 {
    background: #ccc;
    margin: 10px;
    padding: 5px;
    font-size: 12px;
}

Otra razón de la sopa de DIVs aparece por el deseo de usar las etiquetas DIV en lugar de etiquetas mas apropiadas como H1/H2/H3 ó UL/OL y LI. Esta practica debera ser evitada ya que crea problemas con exploradores antiguos ó exploradores movibles, escribamos webs mas semanticas.

3)-Ignorar los atajos

Leer esta lista es trabajo duro, pero escribirlo y mantenerlo es un doble inconveniente. Diseñadores CSS inmediatamente deberian aprender como reducir lo escrito necesario para darle estilo a los elementos.

El más importante patrón es que para los widths. Se vera repeteido este patrón en otras propiedades, asi que es importante aprenderlo. Debera ser de la siguiente manera: arriba, derecha, abajo, izquierda.

Incorrecto:

border-top-width:1px;
border-left-width:1px;
border-right-width:1px:
border-bottom-width:1px

Correcto:

border-width: 1px 1px 1px 1px;

Notaras en el ejemplo anterior, el ancho de todos los bordes es el mismo. Existe un atajo para esta situacion tambien:

border-width: 1px;

4)- Estilos en linea

Otro error muy común es setear, a los elementos del documento html, los estilos dentro de su etiqueta. Esto es una muy mala práctica ya que se hace muy dificil de mantener nuestro código. Además no podríamos cambiar los estilos que ya están de finidos en el elemento desde una hoja externa ya que el último valor que reconoce el navegador es el que está seteado en el objeto.

Incorrecto:

<div style="float:left; width:250px;">
    <h2 style="color: #f00">...</h2>
    <p style="padding:5px">...</p>
</div>

Correcto:

<div class="class-1">
    <h2 class="color-rojo">...</h2>
    <p class="parrafos">...</p>
</div>
.class-1{
    float: left;
    width:250px;
}
.color-rojo{
    color:#f00;
}
.parrafos{
    padding:5px;
}

5)- Usar las Etíquetas que sean Semanticamente Correctas

Es muy común el uso incorrecto de las distintas etiquetas, deben usarse las que sean semanticamente correctas. Por ejemplo para poner un título no se debe usar un div y a este setearle el tipo y tamaño de letra que desee, lo que debo hacer es usar las etiquetas del tipo h (h1 a h6) dependiendo de su importancia.

Incorrecto:

<div style="color: #f00; font-size:16px">Mi Titulo</div>
<div style="padding:5px">...parrafo...</div>
<!--Menu-->
<div class="menu">
    <div><a href="">link 1</a></div>
    <div><a href="">Link 2</a></div>
    <div><a href="">Link 3</a></div>
</div>

Correcto:

<h1 class="titulo"style="color: #f00; font-size:16px">Mi Titulo</h1>
<p style="padding:5px">...parrafo...</div>
<!--Menu-->
<div class="menu">
    <li><a href="">link 1</a></li>
    <li><a href="">Link 2</a></li>
    <li><a href="">Link 3</a></li>
</div>
.titulo{
    color:#f00;
    font-size: 16px;
}
.parrafo{
    padding: 5px;
}
.menu li{
    list-style: none;
    margin:0;
    padding: 5px 0;
}
Tagged with: ,
Posted in Estandares, HTML - CSS - Javascript