Modificar el contenido de cualquier widget de WordPress

widget

No entiendo por qué no hay un filter o un action cuando se trata de los contenidos de los widgets. A no ser que el autor los haya metido, no hay forma de modificar ni el form del widget ni su contenido.

Me he encontrado en la necesidad de tener que indicar en el footer el tamaño de columnas de Bootstrap para cada uno de los widgets de un sidebar, para lo cual es necesario modificar la salida de cada widget e incluir en el form un campo que indique el ancho y en el html del widget el código de Bootstrap para indicar el ancho de la columna:

En el function.php meteremos el siguiente código:

Primero modificaremos los datos del widget para que añada el nuevo campo que vamos a meter (widget_bootstrap_columns), milagrosamente sí hay un filtro para ello:

// Modificamos los datos enviados al guardar el widget
add_filter( 'widget_update_callback', 'sw_widget_update');
function sw_widget_update($instance, $new_instance, $old_instance, $obj ) {
  if (isset($_POST['widget_bootstrap_columns'])) $instance['widget_bootstrap_columns'] = $_POST['widget_bootstrap_columns'];
  return $instance;
}

Después vamos a modificar la variable global $wp_registered_widgets que contiene la llamada a la función widget del widget (la que escribe el HTML). Encapsularemos esta llamada dentro de una función anónima para realizar las tareas que sean necesarias, en mi caso dibujar un div con el class col-sm-N:

// Modifico el display del widget para meterle el código de bootstrap
add_action('dynamic_sidebar_before', 'sw_modify_widgets_display', 10, 2);
function sw_modify_widgets_display($index, $bool) {
  global $wp_registered_widgets;
  $sidebars_widgets = wp_get_sidebars_widgets();
  foreach ( (array) $sidebars_widgets[$index] as $id ) {
    if ( !isset($wp_registered_widgets[$id]) ) continue;
    $wp_registered_widgets[$id]['_callback'] = $wp_registered_widgets[$id]['callback'];
    $wp_registered_widgets[$id]['callback'] = function($args, $widget_args) use ($id) {
      global $wp_registered_widgets;
      // Recuperamos los datos del widget
      $instance = get_option($wp_registered_widgets[$id]['_callback'][0]->option_name);
      $bootstrap = isset($instance[$wp_registered_widgets[$id]['_callback'][0]->number]['widget_bootstrap_columns'])? $instance[$wp_registered_widgets[$id]['_callback'][0]->number]['widget_bootstrap_columns'] : false;
      // Pintamos el tamaño de la columna si así procede
      if ($bootstrap) echo '
'; call_user_func_array($wp_registered_widgets[$id]['_callback'], array($args, $widget_args)); if ($bootstrap) echo '
'; }; } }

Y por último haremos lo mismo para el formulario, modificar la variable global $wp_registered_widget_controls para encapsular la función form del widget y dibujar antes un input donde almacenemos el valor de widget_bootstrap_columns:

// Modifica el form de los widgets
add_action( 'load-widgets.php', 'sw_modificar_widgets_forms' );
function sw_modificar_widgets_forms() {
  global $wp_registered_widget_controls;
  foreach($wp_registered_widget_controls as $id=>$widget) {
    $wp_registered_widget_controls[$id]['_callback'] = $wp_registered_widget_controls[$id]['callback'];
    $wp_registered_widget_controls[$id]['callback'] = function($data) use ($id) {
      global $wp_registered_widget_controls;
      // Recuperamos los datos del widget para incluir el valor de widget_bootstrap_columns
      $instance = get_option($wp_registered_widget_controls[$id]['_callback'][0]->option_name);
      $widget_bootstrap_columns = isset($instance[$data['number']]) && isset($instance[$data['number']]['widget_bootstrap_columns'])? $instance[$data['number']]['widget_bootstrap_columns']:'';
      echo '<div  class="widget_added"><p>Número de columnas (col-sm-<input type="number" name="widget_bootstrap_columns" min="1" max="12" value="'.$widget_bootstrap_columns.'" />)</p><hr /></div>';
      call_user_func_array($wp_registered_widget_controls[$id]['_callback'], array($data));
    };
  }
}

Actualiza tu theme de WordPress desde SVN

Si trabajas realizando proyectos en WordPress en el que el código se aloja en un servidor usando SVN, una forma sencilla de actualizar tu theme con los últimos cambios es mediante este sencillo y no-seguro plugin.

El plugin añade una opción de menú al Settings del Dashboard de WP, el cual te servirá para indicar el servidor SVN, usuario, contraseña y el path que corresponde a la carpeta themes de WP. El plugin se conectará al repositorio SVN y recuperará las carpetas que se encuentran en el path anterior y comprobará si las carpetas corresponden a themes instalados, mostrarás una lista con los themes encontrados junto a un botón que permite actualizar todo el theme.

Digo que no es seguro porque no controlo los permisos de los usuarios ni otras cuestiones, pero si estás interesado en ello supongo que no te costará mucho meterle mano al código:

// Adding Admin menu
if ( is_admin() ){
  add_action('admin_menu', 'svn_update_menu');
  add_action( 'admin_init', 'svn_update_register_settings' );

}

function svn_update_register_settings() {
  register_setting( 'svn_up_option-group', 'svn_username' );
  register_setting( 'svn_up_option-group', 'svn_password' );
  register_setting( 'svn_up_option-group', 'svn_repository' );
  register_setting( 'svn_up_option-group', 'svn_theme_path' );
  if ($_GET['page'] == 'svn-update/svn-update.php') {
    require ('phpsvnclient/phpsvnclient.php');
    $phpsvnclient = new phpsvnclient(get_option('svn_repository'), get_option('svn_username'), get_option('svn_password'));
    $files = $phpsvnclient->getDirectoryFiles('/');
    $svn_themes = array();

    $themes = array();

    foreach(get_themes() as $k=>$t) $themes[$t['Template']] = array($k, $t["Template Dir"]);

    if (is_array($files)) {
      foreach($files as $f) {
        if ($f['type'] == 'directory') {
          if (isset($themes[$f['path']])) $svn_themes[$f['path']] = $themes[$f['path']];
        }
      }
    }
    update_option('svn_themes', $svn_themes);
    if (isset($_GET['svn_update'])) {
      $path = $_GET['svn_update'];
      $theme_path = WP_CONTENT_DIR . '/themes/';
      $files = $phpsvnclient->getDirectoryFiles($path);
      foreach($files as $f) {
        if ($f['type'] == 'file') {
          file_put_contents($theme_path.$f['path'], $phpsvnclient->getFile($f['path']));
        }
      }
      add_action('admin_notices', 'showSVNUpdated');
    }
  }
}

function showSVNUpdated() {
  echo '

Theme updated

'; } function svn_update_menu() { add_options_page('SVN Update Options', 'SVN Update', 8, __FILE__, 'svn_update_options'); } function svn_update_options() { ?>

SVN Update Options

Username
Password
Repository
Theme path

Available Themes

No themes found in SVN repository instaled in WordPress

'; } else { echo '
    '; foreach($themes as $k=>$t) { echo '
  • /'.$k.' => '.$t[0].' Update
  • '; } echo '
'; } ?>

Este plugin necesita la librería PHP SVN Client para acceder al SVN, tan solo hay que meterlo en el mismo directorio que el plugin. Le faltan muchas cosas al plugin, pero ni tengo ganas ni tengo tiempo.

Acceder a Streaming API de PHP

Para aquellos que necesiten realizar aplicaciones sobre Twitter que tiren de la Streaming API, por ejemplo monitorear o que se dice de una marca, les va a venir muy bien este código:

set_time_limit(0);
 
$query_data = array('track' => 'lo que quieres buscar');
$user = 'username';	// replace with your account
$pass = 'password';	// replace with your account
 
$fp = fsockopen("stream.twitter.com", 80, $errno, $errstr, 30);
if(!$fp){
	print "$errstr ($errno)\n";
} else {
	$request = "GET /1/statuses/filter.json?" . http_build_query($query_data) . " HTTP/1.1\r\n";
	$request .= "Host: stream.twitter.com\r\n";
	$request .= "Authorization: Basic " . base64_encode($user . ':' . $pass) . "\r\n\r\n";
	fwrite($fp, $request);
	while(!feof($fp)){
		$json = fgets($fp);
		$data = json_decode($json, true);
		if($data){
			//
			// Do something with the data!
			//
		}
	}
	fclose($fp);
}

El parámetro track se puede cambiar por follow (usando IDs numéricos) para saber que dice algún usuario y que dicen los que le mencionan o retuitean.

Vía / AF-Design

Añadir clase a la imagen destacada de WordPress según el tamaño

Hace bastante que no escribía, sobre todo por falta de tiempo, pero quería escribir sobre este pequeño truco de WordPress. Cuando añadimos una imagen destacada en un post de WordPress nos podemos encontrar con que la imagen ocupa todo el ancho (lo más normal) o que no ocupa tanto, con lo que nos encontramos que la imagen queda muy sola y el diseño queda feo.

Para solucionar esto, deberemos añadir un filtro, comprobar el tamaño de la imagen y el del ancho del contenido y en el caso de que creamos que es conveniente (por ejemplo la mitad del ancho del contenido) añadir una clase a la imagen:

function mi_post_thumbnail_html($html) {
  // Se suele recomendar definir la variable $content_width para ocasiones como esta
  global $content_width;
  preg_match('#width="([^"]+)"#', $html, $s);
  if (isset($s[1]) && $s[1] < $content_width/2) {
    preg_match('#class="([^"]+)"#', $html, $c);
    if (isset($c[1]) && strpos($c[1], "right") === FALSE) {
      return str_replace('class="'.$c[1].'"', 'class="'.$c[1].' right"', $html);
    }
  }
  return $html;
}
add_filter('post_thumbnail_html', 'mi_post_thumbnail_html');

Librería PHP para TwitPic

TwitPic es un servicio que se utiliza para subir fotos que luego publicas en Twitter. Si estás realizando una aplicación que tira de Twitter y quieres dar la oportunidad al usuario de subir sus fotos puedes hacer uso de esta aplicación y su API (es necesario darse de alta):

$twitpic = new TwitPic($api_key, $consumer_key, $consumer_secret, $oauth_token, $oauth_secret);
try {
  /*
  * Retrieves all images where the user is facetagged
  */
  $user = $twitpic->faces->show(array('user'=>'meltingice'));
  print_r($user->images);

  $media = $twitpic->media->show(array('id'=>1234));
  echo $media->message;

  $user = $twitpic->users->show(array('username'=>'meltingice'), array('process'=>false, 'format'=>'xml'));
  echo $user; // raw XML response data

  /*
  * Uploads an image to TwitPic
  */
  $resp = $twitpic->upload(array('media'=>'path/to/file.jpg', 'message'=>'This is an example'));
  print_r($resp);

  /*
  * Uploads an image to TwitPic AND posts a tweet
  * to Twitter.
  *
  * NOTE: this still uses v2 of the TwitPic API. This means that the code makes 2 separate
  * requests: one to TwitPic for the image, and one to Twitter for the tweet. Because of this,
  * understand this call may take a bit longer than simply uploading the image.
  */
  $resp = $twitpic->uploadAndPost(array('media'=>'path/to/file.jpg', 'message'=>'Another example'));
  print_r($resp);
  
} catch (TwitPicAPIException $e) {
  echo $e->getMessage();
}

TwitPic API for PHP

phpcassa: librería PHP para Cassandra

Aunque (afortunadamente) Cassandra ya no está tan de moda y en boca de todos, sigue siendo una herramienta a tener en cuenta a la hora de desarrollar un proyecto en el que se calcule un elevado nivel de datos. Para los que usamos PHP nos vendrá muy bien esta librería que nos facilitará el trabajo y que además es compatible con TimeUUID y SuperColumnFamily:

insert('1', array('email' => 'email@correo.com', 'password' => 'test'));
// Querying:
$users->get('1'); // array('email' => 'email@correo.com', 'password' => 'test')
$users->multiget(array('1', '2')); // array('1' => array('email' => 'email@correo.com', 'password' => 'test'))
// Removing:
$users->remove('1'); // removes whole object
$users->remove('1', 'password'); // removes 'password' field
// Other:
$users->get_count('1'); // counts the number of columns in user 1 (in this case 2)
$users->get_range('1', '10'); // gets all users between '1' and '10'
?>

phpcassa

Crear thumbs con WordPress

Si antes hablaba de una librería que crea thumbs con PHP, ahora comento cómo hacerlo con WordPress, ya que a veces es necesario subir ficheros cuando WP se utiliza para algo más que un simple blog:

// uploadfile tiene el path del fichero subido
// el cuarto parámetro es para que haga crop
$thumb = image_resize($uploadfile, $width, $height, true); 
$upload = wp_upload_dir();
$thumburl = str_replace($upload['path'], $upload['url'],  $thumb );

PHP Thumbnailer: librería para crear thumbs de imágenes

Si hay algo que odio a la hora de desarrollar una aplicación es tener que hacer thumbs de las imágenes, sobre todo para los perfiles: que si el thumb es cuadrado y la imagen rectangular, que si se debe recortar o meter bordes, … PHP Thumbnailer nos facilita enormemente esta tarea:

Hacer resize:

try {
     $thumb = PhpThumbFactory::create('/path/to/image.jpg');
} catch (Exception $e) {
     // error
}
$thumb->resize(100, 100);
$thumb->show();

Hacer resize y crop si es rectangular:

$thumb->adaptiveResize(175, 175);
$thumb->save('/path/to/new_image.jpg');

PHP Thumbnailer

Subir y exportar documentos usando Google Docs y PHP

Uno de los problemas con los que nos podemos encontrar es tener un documento en un formato y tener que exportarlo en otro formato. La exportación se puede realizar utilizando Google Docs, quizás un tanto rebuscada la solución, aunque quizás no tanto.

Os paso un script que sube el fichero a Google Docs dentro de una carpeta y acto seguido lo exporta a otro formato, en este caso subo un PPT y lo convierto en PDF (me hubiese encantado que fuera a HTML pero no acepta esa opción).

// Datos de login a la API de Google
$clientlogin_url = "https://www.google.com/accounts/ClientLogin";
$clientlogin_post = array(
    "accountType" => "GOOGLE",
    "Email" => "miemail@gmail.com",
    "Passwd" => "mipassword",
    "service" => "writely",
    "source" => "WPDOCS"
);

// Inicializamos el CURL
$curl = curl_init($clientlogin_url);

// Obtenemos el string de autenticación
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $clientlogin_post);
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($curl);
preg_match("/Auth=([a-z0-9_\-]+)/i", $response, $matches);
$auth = $matches[1];

// Cabeceras de autenticación
$headers = array(
    "Authorization: GoogleLogin auth=" . $auth,
    "GData-Version: 3.0",
);

// Recuperamos los ficheros y carpetas que tenemos en Google Docs para no crear dos veces la misma carpeta
curl_setopt($curl, CURLOPT_URL, "http://docs.google.com/feeds/default/private/full?showfolders=true");
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_POST, false);
$listado = curl_exec($curl);
$nombre_carpeta = 'WPDOCS';

// Si no se ha creado la carpeta, la creamos
if (strpos($listado, ''.$nombre_carpeta.'') === FALSE) {
  // Make the request
  $h = array_merge($headers,array('Content-Type: application/atom+xml'));
  $xml = ''.$nombre_carpeta.'';
  curl_setopt($curl, CURLOPT_URL, "http://docs.google.com/feeds/default/private/full");
  curl_setopt($curl, CURLOPT_HTTPHEADER, $h);
  curl_setopt($curl, CURLOPT_POSTFIELDS, $xml);
  curl_setopt($curl, CURLOPT_POST, true);

  $response = curl_exec($curl);
  $response = simplexml_load_string($response);
  $id_folder = $response->id;
} else {
  // Recuperamos la ID de la carpeta creada anteriormente
  preg_match("#$nombre_carpetaid;

// Limpiamos los IDs de los ficheros devueltos por Google, solo nos interesa del %3A para adelante
preg_match('/%3A(.+)/', $id_doc, $m);
$id_doc = $m[1];
preg_match('/%3A(.+)/', $id_folder, $m);
$id_folder = $m[1];

// Lo movemos a la carpeta
$h = array_merge($headers,array('Content-Type: application/atom+xml'));
$data = 'https://docs.google.com/feeds/default/private/full/document%3A'.$id_doc.'';
curl_setopt($curl, CURLOPT_URL, "http://docs.google.com/feeds/default/private/full/folder%3A".$id_folder);
curl_setopt($curl, CURLOPT_HTTPHEADER, $h);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_POST, true);
$response = curl_exec($curl);

//header('Content-type: text/xml');
//echo $response;
// Parse the response

// Exportamos a HTML
curl_setopt($curl, CURLOPT_URL, "http://docs.google.com/feeds/download/presentations/Export?docID=$id_doc&exportFormat=pdf");
curl_setopt($curl, CURLOPT_HTTPHEADER, $h);
curl_setopt($curl, CURLOPT_POST, false);
header('Content-type: application/pdf');
echo curl_exec($curl);

curl_close($curl);

Vía / Google Docs API: Client Login with PHP and Curl

Librería PHP para Google Buzz

La verdad es que no tengo ni idea del éxito/uso de Google Buzz, dicen que es un fracaso, pero claro, a saber lo que es un fracaso comparado con otras redes sociales (no Twitter o Facebook, claro), por lo que no está mal saber que existe una librería y que te permite trabajar con Google Buzz.


// Include the library files
require_once "google-api-php-client/src/apiClient.php";
require_once "google-api-php-client/src/contrib/apiBuzzService.php";

// Create the apiClient and Buzz service classes:
$apiClient = new apiClient();
$buzz = new apiBuzzService($apiClient);

// Add the OAuth authentication flow to your app:
// If a oauth token was stored in the session, use that- and otherwise go through the oauth dance
session_start();
if (isset($_SESSION['auth_token'])) {
  $apiClient->setAccessToken($_SESSION['auth_token']);
} else {
  // In a real application this would be stored in a database, and not in the session!
  $_SESSION['auth_token'] = $apiClient->authenticate();
}

// Make an API call
$activities = $buzz->listActivities('@consumption', '@me');

// And echo the returned activities
echo "
Activities:\n" . print_r($activities, true) . "

";

buzz-php-client