Haciendo nuestra propia línea de tiempo de Twitter

Introducción

Twitter se ha convertido en un verdadero fenómeno social. Este es un logro increíble para un servicio tan simple. Pero como sabes, las grandes ideas no son necesariamente complejas.

Esta vez vamos a crear nuestra propia línea de tiempo similar a Twitter, donde puede ver y publicar sus tweets. Puede usar el código que proporcioné aquí para todo tipo de propósitos y estar seguro de que las posibilidades son infinitas. Así que toma los archivos de demostración ¡y empieza a aprender!

Creando la base de datos

Si desea ejecutar una demostración de trabajo en su propio sitio, deberá crear una tabla MySQL donde se almacenarán todos sus tweets. Puede ejecutar el siguiente código SQL a través de phpMyAdmin (el código también está disponible en table.sql en los archivos del tutorial):

tabla.sql

CREATE TABLE `demo_twitter_timeline` (
  `id` int(10) NOT NULL auto_increment,
  `tweet` varchar(140) collate utf8_unicode_ci NOT NULL default '',
  `dt` datetime NOT NULL default '0000-00-00 00:00:00',
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

La tabla puede estar en cualquier base de datos mysql. Solo recuerda actualizar los datos en connect.php .

El XHTML

Gracias a CSS y jQuery, nuestro código XHTML es bastante simple. Esto es lo que puedes ver en index.php en los archivos de demostración.

index.php

<div id="twitter-container">
<form id="tweetForm" action="submit.php" method="post">

<span class="counter">140</span>
<label for="inputField">What are you doing?</label>
<textarea name="inputField" id="inputField" tabindex="1"rows="2" cols="40"></textarea>
<input class="submitButton inact" name="submit" type="submit" value="update" />

<span class="latest"><strong>Latest: </strong><span id="lastTweet"><?=$lastTweet?></span></span>

<div class="clear"></div>
</form>

<h3 class="timeline">Timeline</h3>
<ul class="statuses"><?=$timeline?></ul>
</div>

Toda nuestra línea de tiempo se coloca dentro de un contenedor div con una identificación de twitter-container . Tiene algunos estilos interesantes adjuntos en el archivo CSS a continuación.

A continuación tenemos el formulario con una identificación de tweetForm . Este formulario se envía a través de AJAX, por lo que realmente no importa cuál sea la acción y enviar los atributos están establecidos en.

Dentro del formulario tenemos un elemento span especial. Actúa como el contador, que muestra el número actual de caracteres que se completan en el cuadro. Al igual que en Twitter, el límite aquí se establece en 140 caracteres.

Luego tenemos la etiqueta para el área de texto, el área de texto en sí y el botón de enviar, que está deshabilitado por defecto (esto se hace con jQuery y una clase CSS especial - inact , como verá más adelante).

Después de eso, tenemos nuestro último tweet y un div de compensación. Con ese div solucionamos una deficiencia interesante de CSS, como verá en un minuto.

Finalmente, está la línea de tiempo en sí, que contiene nuestros últimos tweets.

Las líneas 9 y 16 están resaltadas para mostrarle que estamos mostrando variables de PHP. Los explicaremos y generaremos la lista en un momento.

El CSS

Ya mencionamos que con el uso de CSS podemos reducir drásticamente la cantidad de código XHTML que escribimos. Una ventaja adicional es que es realmente fácil cambiar la apariencia de nuestros proyectos en cualquier momento, simplemente cambiando la hoja de estilo.

Ahora veamos qué hay en nuestro demo.css archivo.

demostración.css

/* Page styles */

body,h1,h2,h3,p,td,quote,small,form,input,ul,li,ol,label{
    margin:0px;
    padding:0px;
}

body{
    margin-top:20px;
    color:#51555C;
}

/* Form & timeline styles */

#twitter-container{
    -moz-border-radius:12px;
    -khtml-border-radius: 12px;
    -webkit-border-radius: 12px;
    border-radius:12px;

    border:6px solid #f5f5f5;

    padding:10px;
    width:600px;

    font-size:11px;
    font-family:'Lucida Grande',sans-serif;
    color:#333333;
}

label{
    font-size:20px;
    display:block;
}

.counter{
    color:#CCCCCC;
    float:right;
    font-family:Georgia,serif;
    font-size:32px;
    font-weight:bold;
    height:40px;
    overflow:hidden;
}

textarea{
    width:594px;
    height:38px;
    margin:5px 0 10px 0;

    border:1px solid #AAAAAA;
    padding: 4px 2px;

    font-family:'Lucida Grande',sans-serif;
    overflow:auto;
    font-size:14px;
}

.clear{
    clear:both;
}

.submitButton{
    color:#666666;
    font-size:14px;
    height:32px;
    width:115px;

    -moz-border-radius:6px;
    -khtml-border-radius: 6px;
    -webkit-border-radius: 6px;
    border-radius:6px;

    border:1px solid #cccccc;
    background:url(img/button_bg.gif) repeat-x #f5f5f5;

    cursor:pointer;
    float:right;
}

.submitButton:hover{
    background-position:bottom;
    border-color:#dddddd;
    color:#333333;
}

.inact,.inact:hover{
    background:#f5f5f5;
    border:1px solid #eeeeee;
    color:#aaaaaa;
    cursor:auto;
}

.latest{
    color: #666666;
}

ul.statuses{
    margin:10px 0;
}

ul.statuses li {
    position:relative;
    border-bottom:1px dashed #D2DADA;
    padding:15px 15px 15px 10px;
    list-style:none;
    font-size:14px;
}

ul.statuses li:first-child{
    border-top:1px dashed #D2DADA;
}

ul.statuses li:hover {
    background-color:#F7F7F7;
}

h3.timeline{
    margin-top:20px;
    color:#999999;
    font-size:20px;
    font-weight:normal;
}

div.tweetTxt{
    float:left;
    width:498px;
    overflow:hidden;
}

ul.statuses a img.avatar{
    float:left;
    margin-right:10px;
    border:1px solid #446600;
}
div.date{
    line-height:18px;
    font-size:12px;
    color:#999999;
}

li a, li a:visited {
    color:#007bc4;
    text-decoration:none;
    outline:none;
}

li a:hover{
    text-decoration:underline;
}

Comenzamos definiendo los estilos de página. Primero reiniciamos nuestra página (anulando el margen y el relleno de algunos de los elementos de la página, que difieren por defecto en los diferentes navegadores). Después, en la línea 8, establecemos un margen superior para el cuerpo y un color de fuente para todo el texto de la página.

Las líneas 16 a 19 es donde redondeamos el div, que contiene nuestro formulario y línea de tiempo. Hasta hace poco, no tenía que crear manualmente gráficos de esquinas redondeadas e insertar elementos div adicionales para cada esquina. Pero las versiones recientes de Firefox y Safari pueden hacerlo con CSS puro.

Desafortunadamente, este método no es compatible con otros navegadores. Otra pequeña desventaja de la técnica es que debe orientar cada navegador con propiedades CSS específicas del navegador, como -moz-border-radius , porque las esquinas redondeadas no forman parte de la especificación CSS actual. Pero en caso de que se incluya en una especificación futura, incluimos la propiedad que debería admitirse directamente:border-radius , lo que da como resultado las 4 líneas de código antes mencionadas.

El CSS es bastante sencillo hasta la línea 59. Este es un importante truco de CSS (llamado clearfix) que mencioné anteriormente. Cuando un div contiene elementos flotantes, su altura no se amplía a la altura de sus elementos secundarios. Para ello se inserta otro div que tiene la propiedad CSS clear:both . Esto lo obliga a pasar a una nueva línea, debajo de los elementos flotantes y, por lo tanto, expande la altura de su elemento principal.

Línea 63 es donde diseñamos nuestro botón de envío. Aquí usamos la propiedad de borde redondeado nuevamente, que también funciona en los botones, como puede ver usted mismo. Otra cosa importante a tener en cuenta es que definimos un gráfico de fondo para el botón. Es exactamente el doble de la altura del botón. En su estado normal, la parte superior de la imagen funciona como fondo y, al pasar el mouse, la parte inferior. Esto es lo que hacemos en la línea 82.

En la línea 87 está el inactivo clase. Esta clase se asigna al botón solo cuando está deshabilitado (en la carga de la página inicial o cuando el área de texto está vacía) para evitar que el usuario lo envíe. También tiene un normal definido y :hover estado, pero son absolutamente lo mismo. Esto se hace para detener la otra acción :hover, definida en la línea 81 que afecta al botón. En otras palabras, configuramos un nuevo :hover clase para sobrescribir la anterior.

Las líneas 102 a 116 definen los estilos de los elementos de la línea de tiempo. La línea de tiempo no es más que una lista desordenada. Lo interesante a tener en cuenta aquí es cómo abordamos solo el primer elemento li con :first-child selector y asígnele un borde superior.

El código jQuery

Una vez más, elegí jQuery por sus métodos ágiles y simples que hacen más trabajo con menos líneas de código.

secuencia de comandos.js

$(document).ready(function(){

    $('#inputField').bind("blur focus keydown keypress keyup", function(){recount();});
    $('input.submitButton').attr('disabled','disabled');

    $('#tweetForm').submit(function(e){

        tweet();
        e.preventDefault();

    });

});

function recount()
{
    var maxlen=140;
    var current = maxlen-$('#inputField').val().length;
    $('.counter').html(current);

    if(current<0 || current==maxlen)
    {
        $('.counter').css('color','#D40D12');
        $('input.submitButton').attr('disabled','disabled').addClass('inact');
    }
    else
        $('input.submitButton').removeAttr('disabled').removeClass('inact');

    if(current<10)
        $('.counter').css('color','#D40D12');

    else if(current<20)
        $('.counter').css('color','#5C0002');

    else
        $('.counter').css('color','#cccccc');

}

function tweet()
{
    var submitData = $('#tweetForm').serialize();

    $('.counter').html('<img style="padding:12px" src="img/ajax_load.gif" alt="loading" width="16" height="16" />');

    $.ajax({
        type: "POST",
        url: "submit.php",
        data: submitData,
        dataType: "html",
        success: function(msg){

            if(parseInt(msg)!=0)
            {
                $('ul.statuses li:first-child').before(msg);
                $("ul.statuses:empty").append(msg);

                $('#lastTweet').html($('#inputField').val());

                $('#inputField').val('');
                recount();
            }
        }

    });

}

Podemos dividir este código en tres caminos importantes. El que se ejecuta después de cargar la página (línea 1). El recuento() función, que llena nuestro contador con el número de caracteres restantes, y el tweet() función que maneja la comunicación AJAX y la subsiguiente actualización de la página para incluir el nuevo tweet en la línea de tiempo.

En la primera parte, en la línea 3, verá que vinculamos la función contar() a una serie de eventos que pueden ocurrir en el área de texto. Esto se debe a que cualquiera de esos eventos por sí solo no puede garantizar actualizaciones lo suficientemente rápidas en el contador.

En la siguiente línea, deshabilitamos el botón de envío; no necesitamos que el usuario pueda enviar un formulario vacío.

Posteriormente enlazamos el onsubmit evento del formulario a la función tweet(), evitando que el formulario real se envíe en la línea 9.

En el recuento función hay una serie de cosas que vale la pena mencionar. En las líneas 17-19 calculamos los caracteres restantes y en las líneas 21-36, según lo cerca que estemos del número máximo, establecemos el color del contador.

También decidimos si debemos deshabilitar el botón (si no hay texto en el área de texto o estamos por encima del límite) y habilitarlo en caso contrario. La deshabilitación/habilitación del botón se produce configurando el atributo deshabilitado y asignando nuestra clase CSS personalizada - inact , que elimina el cursor de la mano y cambia su color a gris claro.

El tuit La función es donde ocurre la magia. Serializamos el formulario en el submitData variable y reemplace el contador con una animación gif giratoria.

Después de esto, los datos se envían a submit.php y según el valor de retorno, inserta el tweet recibido en la línea de tiempo en las líneas 55 y 56.

¿Qué hacen realmente esas dos líneas de código? La línea 55 usa el mismo :first-child selector como en nuestra hoja de estilo anterior. Esto significa que insertará el tweet formateado que recibe antes del primer elemento. Entonces, ¿por qué necesitamos la segunda línea? Bueno, si no hemos publicado ningún tweet, el :first-child no encontrará ningún elemento. Por eso usamos :empty selector. Solo una de esas dos líneas puede insertar el elemento al mismo tiempo, eliminando así la necesidad de verificar manualmente si hay elementos en la línea de tiempo.

Después de insertar nuestro tweet recién creado, vaciamos el área de texto y contamos los caracteres restantes.

El PHP

Nuestro código PHP gestiona la inserción de datos en la base de datos MySQL y el formato de nuestros tweets y línea de tiempo.

enviar.php

define('INCLUDE_CHECK',1);
require "functions.php";
require "connect.php";

if(ini_get('magic_quotes_gpc'))
$_POST['inputField']=stripslashes($_POST['inputField']);

$_POST['inputField'] = mysql_real_escape_string(strip_tags($_POST['inputField']),$link);

if(mb_strlen($_POST['inputField']) < 1 || mb_strlen($_POST['inputField'])>140)
die("0");

mysql_query("INSERT INTO demo_twitter_timeline SET tweet='".$_POST['inputField']."',dt=NOW()");

if(mysql_affected_rows($link)!=1)
die("0");

echo formatTweet($_POST['inputField'],time());

Primero comprobamos si magic_quotes_gpc Está establecido. Esto está habilitado en algunos hosts y lo que hace es escapar de los datos entrantes automáticamente, lo que se considera una mala práctica. Es por eso que si está activado, eliminamos el código escapado y podemos continuar normalmente con nuestro script.

Escapamos los datos, hacemos una verificación de la longitud de $_POST['inputField'] e inserte la fila en nuestra base de datos. Hacemos eco de un tweet formateado usando formatTweet (más sobre eso en un minuto), que se devuelve al tweet() función jQuery como la variable msg .

funciones.php

if(!defined('INCLUDE_CHECK')) die('You are not allowed to execute this file directly');

function relativeTime($dt,$precision=2)
{
    $times=array(   365*24*60*60    => "year",
                30*24*60*60     => "month",
                7*24*60*60      => "week",
                24*60*60        => "day",
                60*60           => "hour",
                60              => "minute",
                1               => "second");

    $passed=time()-$dt;

    if($passed<5)
    {
        $output='less than 5 seconds ago';
    }
    else
    {
        $output=array();
        $exit=0;
        foreach($times as $period=>$name)
        {
            if($exit>=$precision || ($exit>0 && $period<60))   break;
            $result = floor($passed/$period);

            if($result>0)
            {
                $output[]=$result.' '.$name.($result==1?'':'s');
                $passed-=$result*$period;
                $exit++;
            }

            else if($exit>0) $exit++;

        }
        $output=implode(' and ',$output).' ago';
    }

    return $output;
}

function formatTweet($tweet,$dt)
{
    if(is_string($dt)) $dt=strtotime($dt);

    $tweet=htmlspecialchars(stripslashes($tweet));

    return'
    <li><a href="#"><img class="avatar" src="img/avatar.jpg" width="48" height="48" alt="avatar" /></a>
    <div class="tweetTxt">
    <strong><a href="#">demo</a></strong> '. preg_replace('/((?:http|https|ftp):\/\/(?:[A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?[^\s\"\']+)/i','<a href="$1" rel="nofollow" target="blank">$1</a>',$tweet).'
    <div class="date">'.relativeTime($dt).'</div>
    </div>
    <div class="clear"></div>
    </li>';
}

Aquí puedes ver 2 funciones. El primero - relativeTime() es una función que hice hace un tiempo, que muestra el período relativo que ha pasado desde un momento determinado (admite tanto una marca de tiempo de Unix como una cadena de fecha de mysql como parámetro).

El otro está hecho especialmente para este tutorial. Da formato y devuelve un tweet usando solo el texto del tweet y una variable de tiempo. Si planea realizar cambios en el ejemplo, este es el lugar para comenzar.

El formato de tuit La función no es nada especial:primero decidimos si debemos convertir el parámetro de tiempo dado de una cadena de datos mysql en una marca de tiempo. Después de esto, prevenimos posibles vulnerabilidades mediante el uso de htmlspecialchars y luego devolver un tweet formateado. Una cosa interesante a tener en cuenta aquí es la línea 53. Con preg_replace función convertimos los enlaces que se incluyen en el tweet en hipervínculos reales, completos con un objetivo y nofollow atributo.

Ahora veamos cómo se genera realmente nuestra línea de tiempo.

index.php

define('INCLUDE_CHECK',1);

require "functions.php";
require "connect.php";

// remove tweets older than 1 hour to prevent spam
mysql_query("DELETE FROM demo_twitter_timeline WHERE id>1 AND dt<SUBTIME(NOW(),'0 1:0:0')");

//fetch the timeline
$q = mysql_query("SELECT * FROM demo_twitter_timeline ORDER BY ID DESC");

$timeline='';
while($row=mysql_fetch_assoc($q))
{
    $timeline.=formatTweet($row['tweet'],$row['dt']);
}

// fetch the latest tweet
$lastTweet = '';
list($lastTweet) = mysql_fetch_array(mysql_query("SELECT tweet FROM demo_twitter_timeline ORDER BY id DESC LIMIT 1"));

if(!$lastTweet) $lastTweet = "You don't have any tweets yet!";

Este código se coloca antes de cualquier código XHTML en la página. A los efectos de la demostración, configuré los tweets para que se eliminen automáticamente después de una hora. Puede eliminar esta línea para mantener los tweets indefinidamente.

El propósito principal de este código es generar la $línea de tiempo y $últimoTweet variables, que se incluyen en nuestro código XHTML al principio del tutorial.

Con esto, nuestra propia línea de tiempo de Twitter está completa.

Conclusión

Hoy usamos PHP, MySQL, jQuery, CSS y XHTML para crear nuestra propia línea de tiempo similar a Twitter. Las posibilidades son infinitas:puede convertir este ejemplo en un libro de visitas, un tweet comunitario en su sitio, un shoutbox o incluso el próximo twitter.