Blog

WAMP-Server

Cuando acabe los exámenes, a parte de ponerme con los del 2º cuatrimestre, tengo pensado aprovechar los ratos muertos en los que no esté currando ni de fiesta en enredar un poco con PHP. Ya había tenido algún contacto, pero no me acuerdo de na, y a pesar de que en algunos sitios digan que no es una comunidad muy buena, siempre he pensado que las cosas siempre se pueden hacer bien independientemente de la herramienta que uses. Al fin y al cabo en el trabajo tengo que mantener un programa escrito en VB… Es verdad que algunos lenguajes te lo facilitan, o se aseguran de que no lo puedas hacer tremendamente mal.

Pues la cosa es que después de pasar media hora intentando configurar el php para que acceda al mysql, me ha dado por mirar si existía alguna forma mas fácil (al estilo linux/ubunt) ya que ahora tengo puesto windows, que de momento sólo quiero hacer pruebas, ya llegará el momento si publico algo de meterse en los entresijos de la configuración. El primer resultado de google al respecto ha sido lo que estaba buscando: WampServer. Te instala la última versión de Apache, PHP y MySql. Además puedes ponerle luego la versión que quieras para simular el entorno de producción. Todo controlado desde un tray icon.

Lo único que a primera vista le falta es un configurador de la seguridad. Si quieres poner contraseñas lo tienes que hacer a mano, lo que para hacer unas pruebas y unos ejemplos está bien, pero si quieres montar un entorno de preproducción hay que simular las condiciones en todo lo posible (aunque entonces tendrás también un entorno de producción y sabrás configurarlo sin problemas).

Muy útil para el que quiera empezar a enredar con php en windows, sin pegarse con nadie en 5 minutos.

BlogRollUpdater

Para desestresarme de estudios, trabajo y otros «males», he creado BlogRollUpdater, un script que actualiza un «blog roll» (lista de enlaces a blogs o similares), ordenando sus elementos en función de la fecha de la última publicación. Podría haberlo hecho dentro de los scripts de la página, este en concreto en el que se encarga de cargar los enlaces, pero me pareció que en este caso no era algo tan «personal» y que le podría servir a cualquiera para su blog roll (sois libres de usarlo de ahí mismo, o copiarlo y modificarlo o hacer lo que os salga con ello). Podéis ver un ejemplo(puede que con el tiempo deje de estar ahí, en cuanto enrede con otra cosa), ademas de el propio blog roll de la pagina, si dais a actualizar veréis como los enlaces se cargan primero con la forma estándar que tenían (por orden del blog de enlaces) y luego se recolocan por fecha de actualización. A continuación el código, y alguna explicación de como hay que llamar a la función:

//Creado por lopez para cerocoma.blogspot.com
//puede ser usado por quien quiera para lo que quiera.
//Sería de agradecer que si lo usas, incluyeras en el blogroll el blog "0,",
//ya que das a entender que tiene algún contenido de interes. Sin embargo no tienes ninguna obligación de hacerlo

//BlogRollUpdater:
//Este objeto se encarga de actualizar el orden de los elementos li de un div en función de sus fechas de actualización
//espera que estos elementos tengan como primer enlace el de un blog
//y que el blog tenga algun feed. Si no tuviese feed (o no se encuentra) se considerará su fecha de actualización
//menor al de cualquier otra.
//
//English (soo bad :):
//This object update the li elements of a div element order using the date of the last entries.
//This object requires that the first link of the li elements is a blog link, and the blog has some feed.
//If a blog doesn't has feed or it is not in the google system the update date is less than others.

var elBlogRoll;
var nBlogs;
var nBlogsProcesados;
var blogs;

function BlogRollUpdater(divId,gFACargada,mostrarResumen,zIndex,tamResumen,styResumen){

 this.divId = divId;
 if(this.divId == undefined)
  this.divId = "BlogRoll";

 this.gFACargada = gFACargada;
 if(this.gFACargada == undefined)
  this.gFACargada = false;

 this.mostrarResumen = mostrarResumen;
 if(this.mostrarResumen == undefined)
  this.mostrarResumen = true;

 this.zIndex = zIndex;
 if(this.zIndex == undefined)
  this.zIndex = 1;

 this.tamResumen = tamResumen;
 if(this.tamResumen == undefined)
  this.tamResumen = 150;

 this.styResumen = styResumen;
 if(this.styResumen == undefined)
  this.styResumen = "background:#ccc;border:1px solid #c60;font-size:11px;text-indent:-15px;text-indent:0px;";

 this.actualizar = function (){
  elBlogRoll = this;
  nBlogs = 0;
  nBlogsProcesados = 0;
  blogs = new Array();

  if(this,gFACargada == false){
   this.gFACargada = true;
   google.load("feeds", "1");
   google.setOnLoadCallback(googleFeedApiInicializada(this));
   
  }else{
   googleFeedApiInicializada(this);
  }
 };
}

function googleFeedApiInicializada(){
 if(elBlogRoll.gFACargada == false){
  elBlogRoll.actualizar();
 }else{
  var container = document.getElementById(elBlogRoll.divId);
  sacarElementos(container);
 }
}

function sacarElementos(contenedor){
 var elems = contenedor.getElementsByTagName("li");
 nBlogs = elems.length;

 for each(var e in elems){
  var a = e.getElementsByTagName("a");
  var dir = a[0].getAttribute("href");
  
  //crear objeto Blog
  google.feeds.lookupFeed(dir,new Blog(e.innerHTML).setFeedUri);
 }
}

//Este objeto me demuestra la poca idea que tengo de javascript (persistencia de objetos, this, etc...)
//parece que en el array blogs no se cargan elementos de este tipo, si no elementos que tienen como padre a un Blog
//Seguro que hay alguna manera de hacerlo bien, pasando siempre el mismo objeto sin tener que crear uno cada vez...
//En concreto que los elementos almacenados en blogs son del tipo setEntry y del setFeedUri (donde se hacen lso push)
//seguramente hay alguna palabra reservada para indicar al padre de un objeto que en este caso si sería del tipo Blog

//Blog:
//Este objeto almacena los datos necesarios para el proceso de una entrada del blogroll
//
//This object has the necesary data to process an entry of the blogroll
function Blog(contenido){
 this.contenido = contenido;

 this.setEntry = function (result){
  this.contenido = contenido;
  if(result.feed != undefined)
   this.entry = result.feed.entries[0];
  nBlogsProcesados = nBlogsProcesados + 1;
  blogs.push(this);
  if(nBlogsProcesados == nBlogs){
   blogsProcesados();
  }
 }

 this.setFeedUri = function (result){
  if(result.url == null){
   nBlogsProcesados = nBlogsProcesados + 1;
   this.contenido = contenido;
   blogs.push(this);
  }else{
   var feed = new google.feeds.Feed(result.url);
   feed.setNumEntries(1);
   feed.load(new Blog(contenido).setEntry);
  }
 }

}

function blogsProcesados(){
 for(var i = 0; i < blogs.length; i++){
  for(var j = i+1; j < blogs.length; j++){
   if(blogs[i].entry == undefined){
    //no se ha encontrado la url del feed o no se ha cargado por algún error, se considera la fecha menor que las demás
    var b = blogs[i];
    blogs[i] = blogs[j];
    blogs[j] = b;
   }else{
    if(blogs[j].entry != undefined){
     //hay dos fechas para comparar
     if(0<((new Date(blogs[j].entry.publishedDate).getTime()) - (new Date(blogs[i].entry.publishedDate).getTime()))){
      //j es posterior a i
      var b = blogs[i];
      blogs[i] = blogs[j];
      blogs[j] = b;  
     }
    }//si no j no tiene fecha y se considera menor que i
   }
  }
 }

 var cadenaHTML = "<ul>";
 var indice = 0;

 for each(var b in blogs){

  if(elBlogRoll.mostrarResumen == true && b.entry != undefined){
   cadenaHTML = cadenaHTML + "\n<li><div  onMouseOver='mostrarResumenBlog(" + indice + ")' onMouseOut='ocultarResumenBlog(" + indice + ")'>";
  }else{
   cadenaHTML = cadenaHTML + "\n<li>";
  }

  cadenaHTML = cadenaHTML + b.contenido;

  if(elBlogRoll.mostrarResumen == true && b.entry != undefined){
   cadenaHTML = cadenaHTML + "</div><div id='resumenBlog" + indice + "' style='z-index:" + elBlogRoll.zIndex + ";position:absolute;visibility:hidden;" + elBlogRoll.styResumen + "'><span style='text-indent:15px;font-weight:bold;'>" + b.entry.title + "</span><br /><span style='font-style:italic;'>" + limpiar(b.entry.content, elBlogRoll.tamResumen) + "</span></div></li>";
  }else{
   cadenaHTML = cadenaHTML + "</li>";
  }

  indice = indice + 1;
 }
 cadenaHTML = cadenaHTML + "</ul>";

 var container = document.getElementById(elBlogRoll.divId); 
 container.innerHTML = cadenaHTML;

}


function mostrarResumenBlog(i){
 var container = document.getElementById("resumenBlog" + i);
 container.style.visibility = "visible";
}


function ocultarResumenBlog(i){
 var container = document.getElementById("resumenBlog" + i);
 container.style.visibility = "hidden";
}

function limpiar(cadena, l){
 var c = "";
 var i = 0;
 var f = 0;
 cadena.replace("\n"," ");
 cadena.replace("\t"," ");
 while(i > -1 && i < l){
  i = cadena.indexOf("<");
  f = cadena.indexOf(">");

  if(i > -1 && f > -1)
   cadena = cadena.replace(cadena.substring(i, f + 1), "");  
 }

 c = cadena.substring(0,l);
 if(cadena.length > l)
  c = c + "...";

 return c;
}

Para variar el código es bastante malucho (y mi ingles :P), me he dado cuenta de que se (aun) menos javascript de lo que yo creía.

Para llamar a la función hay que haber añadido la clave del Google Feeds API, la cual se pone en una etiqueta script como veis en el ejemplo que os dan, o como veis aquí. Además hay que incluir el script, y hacer la llamada. Todo junto quedaría así:

<script type="text/javascript" src="https://www.google.com/jsapi?key=VUESTRAAPIKEY"></script>

<script type="text/javascript" src="https://archivos.cerocoma.googlepages.com/BlogRollUpdater.js">
new BlogRollUpdater().actualizar();</script>

Se pueden pasar algunos parametros a la creación del objeto BlogRollUpdater, serían los siguientes en orden:

  1. divId: es el identificador de la etiqueta div que contiene el blog roll. Por defecto busca «BlogRoll».
  2. gFACargada: indica si la Ajax Feeds API ya ha sido cargada, por ejemplo en mi caso como ya la tenía cargada le indico el valor true. Si se indica el valor false (valor por defecto) hay que tener en cuenta que la llamada ha de realizarse durante la carga de la página, esto es debido al modo en que se cargan las apis de google.
  3. mostrarResumen: indica si hay que mostrar un resumen de la primera entrada del blog, al pasar el ratón por encima del enlace. Si se pone a false, los demás valores no importan. Por defecto a true.
  4. zIndex: es el valor del z-index que queremos en el div que se muestra el resumen. En mi caso con un 1 (valor por defecto) es suficiente para que se muestre por encima del sidebar, sin embargo podría ser necesario cambiarlo según el caso.
  5. tamResumen: es el tamaño del contenido de la entrada del resumen. Hay que tener en cuenta que se omiten las etiquetas html. El valor por defecto es 150.
  6. styResumen: es el stilo que se le pondrá al div en el que se muestra el resumen (salvo el z-index y el visibility que no son opcionales). El valor por defecto es de nuevo el que yo necesitaba: «background:#ccc;border:1px solid #c60;font-size:11px;text-indent:-15px;text-indent:0px;»

Con todo esto, mi llamada es (mas o menos) así:

<script type="text/javascript" src="https://www.google.com/jsapi?key=ABQIAAAAm8jjlSilwL_ngAOmGoVVJRTBirHsSigxVb89Gx4cAc1hSJZCexSRlLoIdwdR82Sjtdy_9-cXY0fKwA"></script>

<script type="text/javascript" src="https://archivos.cerocoma.googlepages.com/BlogRollUpdater.js"></script>

[...codigo de carga de los enlaces...]

new BlogRollUpdater('enlaces',true).actualizar();

Mientras lo hacía me he dado cuenta de que necesito algunas cosas, así que si teneis alguna sugerencia de estas (o de cualquier otra cosa) os lo agradecería.

  • Un editor de javascript que al menos compruebe la sintaxis, y si hace sugerencias sobre nombres de variables mejor, que la mayoría de los errores que he tenido me los quitaría.
  • Un texto completo y detallado sobre javascript y el dom, no tengo ni idea de los prototipos ni esas cosas del javascript y deben tener mucha potencia…
  • Algo sobre css, aunque de esto no estoy muy seguro, que no soy muy de mirar lo bonito de las cosas, mientras funcionen me valen, y eso que se que la mitad de lo que vale algo es lo que ves en el primer vistazo, pero es que me puede.

Pues lo dicho, cualquier sugerencia sobre estas cosas o cualquier otra será de agradecer.

PD: Se me olvidaba una apreciación, si el enlace no tiene un feed asociado, o no está en la BBDD de google asociado a esa dirección, se verá relegado al final, como le pasa en mi blog roll a fuckowski, supongo que por el reciente cambio del nombre de dominio.

PPD: Hay que tener en cuenta que la API trabaja con al cache que tiene de los feeds en sus servidores, por lo que feeds poco actualizados podrían no ver afectada su posición en el blog roll hasta unas horas despues de la última publicación. Es el caso del enlace Atom que es del feed de este sitio, y que mientras escribo esto sigue bastante abajo.

PPPD: Al cesar lo que es del cesar, he de decir que la idea la saque de feevy, pero pense que sería mas simple no tener que dar de alta los blogs…

EDITO (12-01-07): Me he dado cuenta de que en IE no rula, parece un problema de crear objetos desde otros scripts al contenedor del tipo. He probado a poner una función para crear el objeto, pero tampoco funciona…. Además en /. me han dicho que en konqueror tampoco rula, y me han aclarado algo que evitará que el código sea tan malo, así que tendré que hacer una nueva versión cuando pueda arreglar estas cosas (si es que soy capaz :P) cuando tenga tiempo, que llevo dos días sin empollar y el lunes se me acaban las vacaciones.

EDITO (13-01-07): El error de konqueror (no lo he probado expresamente, pero también se producía en firefox, solo que continuaba la ejecución) creo que esta solucionado. El problema era que el objeto devuelto por el getElementsByTagName al aplicarle el for each hacia una pasada mas despues de los elementos li con lapropiedad length, lo que provocaba el error, ya que length no tiene funciones…, hay que verlo para entenderlo, he guardado el script viejo (el que esta mal), por si alguien quiere verlo.

Lo de Internet Explorer tambien era por eso, parece que hacia una pasada previa, y como los for each no le gustaban, como que descartaba todo el script, o algo así, no se muy bien, porque me daba distintos errores cada vez. Unas veces «se esperaba ‘(‘» y otras «‘BlogRollUpdater’ no está definido’, la cosa es que quitando los for each se ha solucionado y ya funciona.

Con esto creo (supongo) que el script funciona con todos los navegadores, lo de arreglar el script para que sea mas legible, mas correcto lo dejo para mas adelante.

Siempre hay un modo mas simple…

Hoy va la cosa de como hay que pararse a pensar antes de actuar, es algo que no siempre hago.

A través de fresqui, me encontré una página en la que puedes ver mapas de la tierra en «tiempo real». Esta, en realidad «roba» su funcionalidad a otra, en cuyo dominio se pueden encontrar varias cosas interesantes.

Cuando lo vi me dije que estaría bien tenerlo de fondo de escritorio a pesar de la resolución, y ya me estaba preparando para hacer algún programa/script que la descargara periódicamente. En linux sería fácil, ya que el wget suele estar por defecto. Sin embargo en windows lo único, que yo sepa, con lo que se podría hacer eso por linea de comandos es telnet (seguro que hay alguna cosilla, pero no la conoceré). La cosa es que ya cuando estaba poniéndome a ello, me acordé de que en el escritorio de windows se pueden incrustar trozos de paginas, creo que desde el 95. Echando un vistazo, vi que también se podían establecer como fondo de escritorio paginas html, así que eso hice (está ajustado a mi escritorio 1280×800 con la barra de windows con la altura por defecto):

<html><head></head><body bgcolor="black" alink="black" link="black"><a href="javascript:location.reload(true);"><img src="https://www.fourmilab.ch/cgi-bin/uncgi/Earth?img=learth.evif&dynimg=y&opt=-p" width="1250" height="740" /></a></body></html>

Esto es mucho mas simple que lo que me proponía en un principio, pero realmente tiene la suficiente funcionalidad, y el resultado es similar al que esperaba. Además seguro que en unos días me he aburrido de ello.

Pequeñas actualizaciones

Como veréis he realizado algunos cambios en la pagina, siguiendo con lo que empecé algún tiempo atrás. Para que veáis que no dejo todo sin acabar, sólo lo pospongo hasta que me vuelva a apetecer enredarme con ello (y si no me vuelve a apetecer, pos que le voy a hacer yo :P).

Modifiqué lo de las frases, para poder ir cambiando de una a otra, y los enlaces que tenía estáticos en la pagina, los he modificado para que también se carguen de un blog aparte, para no tener que modificar, cada vez que añada o quite uno, la plantilla. Lo último ha sido un bloque de sugerencias donde mostrar enlaces curiosos, pero que no son para tanto como para ponerles un enlace permanente (estas sugerencias también se pueden ir cambiando con el botón). Si pasáis por encima del enlace que os aparezca, podéis ver una descripción de lo que es en un globo, que he hecho con mis limitados conocimientos de css y javascript.

Ya sé que esta barra lateral se ve bastante mal en IE, pero es que no me apetece nada pegarme con él para que funcione. Además, hay cosas que se que no funcionarán nunca, como lo de las imágenes embebidas en el código.

Bueno, pos lo dicho, ahora hay 3 blogs relacionados con este, que le aportan pequeños contenidos que van mas allá de las entradas: frases, enlaces, enlaces curiosos. Además si se accede a alguno de estos blogs sin poner «?noredir» en la url, se redirige automáticamente a cerocoma, indicándole de donde viene, para que así esta pagina pueda mostrar lo que se quería ver en dichos blogs. Por ejemplo, si se accede a esta pagina añadiendo «?frase=0&enlace=0&enlacecurioso=0» se mostrarán la última frase, la última sugerencia, y el último de los enlaces se verá resaltado.

Aquí dejo el código, por si a alguien le puede aportar ideas, o como ejemplo de uso de la AJAX feeds api de google (el código podría ser mejor, pero solo lo toco hasta que funciona ;):

     var propiedades;var cargandoImg = '<img alt="Cargando" src="data:image/gif;base64,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" width="66" height="66" />';var cambiarImg = '<img title="Cambiar" src="data:image/gif;base64,R0lGODlhHgAeANUzAObm5uLi4uPj4+fn593d3eXl5eHh4dzc3ODg4PHx8dPT09DQ0Ojo6PT09NLS0t7e3vLy8t/f3/f39+rq6tra2vDw8PPz89XV1dbW1u3t7evr69vb29nZ2dfX1/X19e7u7tjY2Onp6fb29uzs7Pj4+Pn5+fr6+u/v7/v7+/39/fz8/OTk5NTU1NHR0c/Pz8HBwX9/f////5ubm////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAADMALAAAAAAeAB4AAAb/wJlsSCwaj0hhLKVCmUokicjTsEASlU9mpJkwBoCVIGB4wWQxmBMqpVqxWq4XLCbDzOh1dFq9ZrddXwAFYwYID3gxT3tufnGBYIRkhwSJbHxvFSeAc4MrkxEEB4lREo1YRJCeoKIbiRKmfQlGEyGRn4ahBxsUryKyR7arQ7q8HIm/b0i3AUW7FBwgiQ0NV8vDRtAgHdN+RsIFYkgy2xiJ3kXg4uMyGBeJWCdGDJ4C7EQsiZofRfThArkeECjSwR2LfGdinNBSpF5AUUUMslCQSIsGI2IeHjBy4aACB4kydDEyBkEEgQcoGPHooEWiEbUYGDGJUmURli1cJow5AAnEcSMTW7ZYkCjEl3D3bn7MucBFoqN1ksoIytSF04R0CkW4R3Wo1atowmhFeaTjUq9fE9UxhNJYObNCFzRNm7AQpWfRCsJlOpcumklttentKver3zR33Q4+W9iwizsJX0ieTLmyZcswZsDYzLmz58+gZwQBADs=" width="12" height="12" />';function procesarQueryString(){ if(propiedades==undefined){  propiedades = new Array();  var cadena = document.URL;  if(cadena.indexOf("?")>-1){   cadena = cadena.substring(cadena.indexOf("?")+1,cadena.length);   var pares = cadena.split("&");   for(var i=0;i<pares.length;i++){    var par = pares[i].split("=");    propiedades[par[0]] = par[1];   }  } }}function separarCampos(valor){ var campos = valor.split('|-|'); return campos;}//Fusilada de http://www.desarrolloweb.com/articulos/763.php function aleatorio(inferior,superior){  numPosibilidades = superior  - inferior;  aleat = Math.random() * numPosibilidades;  aleat = Math.round(aleat);  return parseInt(inferior) + aleat; }var feedFrases;var feedSugerencias;function eliminarElemento(vector, indice){ var auxiliar = new Array(); var i; var j=0; for(i=0;i<vector.length;i++){  if(indice!=i){   auxiliar[j++] = vector[i];  } } return auxiliar;}function mostrarFrase(){ var texto = "Todo conocer depende de la estructura que conoce."; var container = document.getElementById("citaAleatoria"); var boton = ' <div align="right"><a href="JavaScript:modificarFrase();">' + cambiarImg + '</a></div> '; procesarQueryString(); var i = 0; if(propiedades["frase"] != undefined){  i = propiedades["frase"];  if(i<0){   i = 0;  }  if(i>feedFrases.length){   i = feedFrases.length-1;  }   }else{  i = aleatorio(0,feedFrases.length-1); } container.innerHTML = boton + feedFrases[i]; feedFrases = eliminarElemento(feedFrases,i); if(feedFrases.length==0){  feedFrases = undefined; }}function mostrarDescSugerencia(){ var container = document.getElementById("descSugerencia"); container.style.visibility = "visible";}function ocultarDescSugerencia(){ var container = document.getElementById("descSugerencia"); container.style.visibility = "hidden";}function mostrarSugerencia(){ var texto = "Hoy no tengo sugerencias ;)"; var container = document.getElementById("sugerencia"); var boton = ' <div align="right"><a href="JavaScript:modificarSugerencia();">' + cambiarImg + '</a></div> '; procesarQueryString(); var i = 0; if(propiedades["enlacecurioso"] != undefined){  i = propiedades["enlacecurioso"];  if(i<0){   i = 0;  }  if(i>feedSugerencias.length){   i = feedSugerencias.length-1;  }   }else{  i = aleatorio(0,feedSugerencias.length-1); } var campos = separarCampos(feedSugerencias[i].content); container.innerHTML = boton + '<div onmouseover="mostrarDescSugerencia()" onmouseout="ocultarDescSugerencia()"><a href="' + campos[0] + '">' + feedSugerencias[i].title + '</a><div id="descSugerencia" style="z-index:1;position:absolute;background:#ccc;border:1px solid #c60;font-size:11px;visibility:hidden;">' + campos[1] + '</div></div>'; feedSugerencias = eliminarElemento(feedSugerencias,i); if(feedSugerencias.length==0){  feedSugerencias = undefined; }}function cargaFraseAleatoria(result) { //Valor por defecto por si ocurre un error que no se quede en blanco if (!result.error) {  feedFrases= new Array();  for(i=0;i<result.feed.entries.length;i++){   feedFrases[i] = result.feed.entries[i].content;  } } mostrarFrase();}function cargaSugerencias(result) { //Valor por defecto por si ocurre un error que no se quede en blanco if (!result.error) {  feedSugerencias =result.feed.entries; } mostrarSugerencia();}function inicializarFrase(){ var container = document.getElementById("citaAleatoria"); container.innerHTML = '<div align="center">' + cargandoImg + '</div>'; var feed = new google.feeds.Feed("http://frases-cerocoma.blogspot.com/feeds/posts/default?start-index=1&max-results=100000"); feed.setNumEntries(10000); feed.load(cargaFraseAleatoria);}function cargaEnlaces(result) { if (!result.error) {  var container = document.getElementById("enlaces");  procesarQueryString();  var i = -1;  if(propiedades["enlace"] != undefined){   i = propiedades["enlace"];  }  var j = 0;  var cadena = "</ul>";  for(j=0;j<result.feed.entries.length;j++){   if(i==j){    cadena = '<li><a href = "' + result.feed.entries[j].content + '"><span style="color:#c60;"><b><u>' + result.feed.entries[j].title +'</u></b></span></a></li>' + cadena;   }else{    cadena = '<li><a href = "' + result.feed.entries[j].content + '">' + result.feed.entries[j].title +'</a></li>' + cadena;   }  }  cadena = "<ul>" + cadena;  container.innerHTML = cadena; }}function inicializarEnlaces(){ var container = document.getElementById("enlaces"); container.innerHTML = '<div align="left">' + cargandoImg + '</div>'; var feed = new google.feeds.Feed("http://enlaces-cerocoma.blogspot.com/feeds/posts/default?start-index=1&max-results=100000"); feed.setNumEntries(10000); feed.load(cargaEnlaces);}function inicializarSugerencias(){ var container = document.getElementById("sugerencia"); container.innerHTML = '<div align="left">' + cargandoImg + '</div>'; var feed = new google.feeds.Feed("http://enlacescuriosos-cerocoma.blogspot.com/feeds/posts/default?start-index=1&max-results=100000"); feed.setNumEntries(10000); feed.load(cargaSugerencias);}function initialize() { inicializarFrase(); inicializarEnlaces(); inicializarSugerencias();}function modificarFrase(){ propiedades["frase"]=undefined; if(feedFrases == undefined){  inicializarFrase(); }else{  mostrarFrase(feedFrases); }}function modificarSugerencia(){ propiedades["enlacecurioso"]=undefined; if(feedSugerencias == undefined){  inicializarSugerencias(); }else{  mostrarSugerencia(feedSugerencias); }} google.load("feeds", "1"); google.setOnLoadCallback(initialize);

Calculadora en Visual Basic 6

Como (supongo que debido a la entrada sobre VB) bastantes venis buscando una calculadora en Visual Basic y me he encontrado un video de W0Lf099 en el que hace una calculadora, os la dejo para que encontreis lo que buscais. No es que sea muy funcional (no es como una calculadora de mano, ni como la de windows), y alguna de las cosas que dice no es de lo mas exacta (que facil es criticar sin hacer nada ;), pero enseña el uso de controles básicos, y puede ser un buen ejemplo para empezar.

Dura algo menos de 10 minutos:

Canales de usuarios de Youtube en Miro

Para añadir un canal en Miro con los videos de un usuario de youtube, solo hay que ñadir un canal con una url del tipo:

http://www.youtube.com/rss/user/nombreDeUsuario/videos.rss

Por ejemplo, podeis añadir estos en español: muchachadanui, rtve. O este en el que no importa el idioma.

Si sabeis de algun otro canal bueno en castellano no dudeis en dejarlo en un comentario, o enviarme un email.

AddIn’s para MSN Messenger

No se como algo tan sencillo puede acabar resultando tan complicado…

Hace bastante tiempo estuve enredando con losAddIn’s de MSN Messenger. A raíz de lo de las voces, estuve mirando por System y me encontré System.Speech, y pensé que sería sencillísimo hacer uno que leyese lo que te escriben (iluso de mi). Lo que es hacerlo es de lo más sencillo, pero después que funcione no lo es tanto…

Empecemos por el principio. Un AddIn es lo que toda la vida se ha llamado plugin. En nuestro caso, se puede crear una dll que cumpla determinadas condiciones para cargar en el MSN Messenger y así añadirle funcionalidad.

HABILITAR ADDIN’S PARA MSN MESSENGER

Para habilitar los add in debemos añadir en el registro de windows un valor de tipo DWORD con el nombre AddInFeatureEnabled en la clave HKEY_CURRENT_USER\Sofgtware\Microsoft\MSNMessenger\ y asignarle un 1. En este archivo hay un .reg que hace esto automáticamente. Hecho esto, al reiniciar el messenger en Herramientas\Opciones encontraremos (creo que solo es a partir de la version 8) un nuevo apartado llamado Accesorios en el que se puede indicar la dll del accesorio que queremos añadir (por ejemplo la que está en el mismo rar). Con esto el AddIn queda agregado pero no activo, pero no nos adelantemos.

ESTRUCTURA DE UN ADDIN PARA MSN MESSENGER

Un add-in es una dll que tiene como nombre EspacioDeNombres.Clase.dll. Esta dll deberá tener una referencia a MessengerClient.dll (por lo tanto deberá estar junto a esta para ejecutarse correctamente), en todos los sitios que he leido indican que hay que añadir también el espacio de nombres Microsoft.Messenger, pero supongo que si lo antepones a cualquier miembro de ese espacio no es necesario. El proyecto tiene que tener un espacio de nombres, que deberá se el mismo que se indique en la primera parte del nombre de la dll. Además debe existir una clase (cuyo nombre es la segunda parte del nombre de la dll) que implemente la interfaz IMessengerAddIn cuyo único requisito es que exista un metodo initialize:

Sub Initialize(ByVal ElMessengerClient As MessengerClient) Implements IMessengerAddIn.Initialize

En este caso ElMessengerClient es el objeto que lanza los eventos que podemos controlar, y sobre el que podemos realizar acciones como enviar mensajes, etc. por este motivo es recomendable que nos le guardemos. Algunos eventos que pueden interesarnos son:

'Se dispara cuando llega un mensaje
Private Sub MiClienteMSN_IncomingTextMessage(ByVal sender As Object, ByVal e As Microsoft.Messenger.IncomingTextMessageEventArgs) Handles MiClienteMSN.IncomingTextMessage
...

'Se dispara cuando se aprieta el botón de configuración del accesorio
Private Sub MiClienteMSN_ShowOptionsDialog(ByVal sender As Object, ByVal e As System.EventArgs) Handles MiClienteMSN.ShowOptionsDialog
...

USO DE UN ADDIN PARA MSN MESSENGER

Es tan simple como colocar la dll adecuada donde queramos, pero asegurandonos de que tiene a su lado la dll MessengerClient.dll que encontraremos en el directorio de intalación del messenger. Por ejemplo la podemos colocar en (directorio por defecto) c:\Archivos de programa\MSN Messenger\. Una vez hecho esto y teniendo habilitados los addin como hemos indicado antes, abrimos el messenger y vamos a herramientas>opciones>accesorios y pulsamos en [Agregar a Messenger] exploramos hasta el lugar en el que la hemos puesto, la seleccionamos y aceptamos. Si este AddIn implementa un manejador de ShowOptionsDialog se habilitará el botón [Configuracion] a traves del cual normalmente se podrá configurar el AddIn. Una vez hecho esto esta añadido, pero no activo, podremos indicar ahí que se active automaticamente con el cambio de estado, o bien, activarlo manualmente desde la venta principal del messenger en la «flecha» que sale junto al nombre.

ACCESO DE UN ADDIN A LOS RECURSOS DEL SISTEMA

Hasta aquí la parte sencilla, con esto podremos hacer un bonito robot con el que hablen nuestros amigos cuando no estemos, y poco mas…. Al parecer los AddIn se ejecutan en una «caja de arena», un entorno seguro para que no pueda hacer nada que comprometa la seguridad sin que el usuario sea consciente de ello. Esto provoca que no podamos acceder a los recursos del sistema, por lo tanto nos condena a no hacer practicamente nada.

Por ejemplo no podemos crear un objetoSpeechSynthesizer para hacer que el sistema nos lea los mensajes que recibimos. Si hacemos esto en uno de los manejadores de eventos (o el procedimiento Initialize) directamente obtendremos un error 8013150A, o si lo hacemos en una función que se llame desde uno de los manejaremos nos dirá que«Ese ensamblado no permite llamadores de confianza parcial».

LLegados a este punto te puedes volver loco a mirar por el msdn, y todo el sitio de microsoft, yo al menos no he encontrado nada de nada. Asombrosamente la solución la encontré en un sitio de microsoft, pero es algo como «alternativo», y para colmo a esa pagina llegue a traves de unos blogs chinos o japoneses o lo que sea (salían caracteres que no se leer). Lo que nos interesa empiza a partir de «Add-ins run under Code Access Security….» básicamente dice que lo que hay que hacer es firmar el componente, y añadirlo a la cache de ensamblados global. Lo de firmarlo se puede hacer mediante las propiedades del proyecto, y lo de añadirlo a la cache los que tengamos intalado algún visual studio podemos usar el gacutil (segun donde tengamos las cosas y que versiones tengamos):

"C:\Archivos de programa\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" /i  "C:\Archivos de programa\MSN Messenger\LectorWLMAddIn.AddIn.dll"

Si todo ha ido bien nos dirá «Ensamblado agregado correctamente a la caché.». LLegado a este punto si podremos acceder a los recursos del sistema (entrada/salida, web, etc.).

Si nos damos cuenta esto es de lo mas simple, es una manera de que el usuario diga que dicha dll es de confianza y que puede hacer lo que quiera, pero no, no se les ocurre decirlo donde se habla de la API ni nada por el estilo. Con lo facil que habría sido decir algo como (perdonar mi ingles): «For security your AddIn will execute in a sandbox that not permit acces to IO system and other system resources. If you want this you must sign the assemby and add it to the global assembly cache (GAC)», vamos digo yo, y así no me habría matado mirando y probando cosas como compartición de objetos, etc., etc., etc. Perdonar la exaltación, pero es que hasta que lo encontré he dado mil vueltas…

Bueno, a parte del archivo que os he indicado antes que contiene la dll y el .reg para habilitarlo, he colgado el proyecto, por si alguien quiere echarle un vistazo, o incluso extenderlo. A partir de ahi supongo que se podrían configurar para que contactos queremos tenerlo activado y para cuales no, o incluso que voces queremos usar para cada uno de estos.

Aquí os dejo algunos enlaces donde hay mas ejemplos: [1] [2] [3] [4] [5]