6. desarrollo del widget de cita...

30
DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes 40 6. Desarrollo del Widget de Cita Médica 6.1 Esquema de funcionamiento de la aplicación El widget de cita médica basa su funcionamiento en la realización de peticiones por medio del objeto XMLHttpRequest de JavaScript a un servidor intermedio. Este servidor es el que, a su vez, se encarga de realizar peticiones al servidor de InterS@S haciendo uso de la librería libcurl de PHP. El servidor intermedio procesará la respuesta ofrecida por el servidor de InterS@S con ayuda de la clase Simple HTML DOM parser, obteniendo así los datos necesarios para el widget de cita médica. Finalmente, el servidor intermedio se encargará de presentar estos datos en formato JSON, el formato de datos más adecuado para ser consumido por los widgets de Yahoo! Connected TV. Ilustración 17: Esquema de funcionamiento de la aplicación. 6.2 Código del Widget de Cita Médica (JavaScript) 6.2.1 Implementación de las vistas Como ya se comentó en el apartado 4.2.4, el código de cada uno de los archivos cargados por init.js desde la carpeta Javascript/views/ extiende alguna de las clases base del KONtx Framework que implementan los distintos tipos de vistas existentes. Por tanto, es en estos archivos donde se construye y define el aspecto y funcionalidad de las distintas vistas del widget.

Upload: others

Post on 30-Jun-2020

5 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

40

6. Desarrollo del Widget de Cita Médica

6.1 Esquema de funcionamiento de la aplicación

El widget de cita médica basa su funcionamiento en la realización de peticiones por medio

del objeto XMLHttpRequest de JavaScript a un servidor intermedio. Este servidor es el que,

a su vez, se encarga de realizar peticiones al servidor de InterS@S haciendo uso de la librería

libcurl de PHP. El servidor intermedio procesará la respuesta ofrecida por el servidor de

InterS@S con ayuda de la clase Simple HTML DOM parser, obteniendo así los datos

necesarios para el widget de cita médica. Finalmente, el servidor intermedio se encargará

de presentar estos datos en formato JSON, el formato de datos más adecuado para ser

consumido por los widgets de Yahoo! Connected TV.

Ilustración 17: Esquema de funcionamiento de la aplicación.

6.2 Código del Widget de Cita Médica (JavaScript)

6.2.1 Implementación de las vistas

Como ya se comentó en el apartado 4.2.4, el código de cada uno de los archivos cargados

por init.js desde la carpeta Javascript/views/ extiende alguna de las clases base

del KONtx Framework que implementan los distintos tipos de vistas existentes. Por tanto, es

en estos archivos donde se construye y define el aspecto y funcionalidad de las distintas

vistas del widget.

Page 2: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

41

En efecto, en las primeras líneas del archivo init.js del Widget de Cita Médica, además

de cargar el KONtx Framework y otros archivos necesarios para nuestro widget se cargan los

catorce archivos contenidos en la carpeta JavaScript/views/ que corresponden a cada

una de las vistas del Widget de Cita Médica:

include("Framework/kontx/1.1/src/all.js");

include("Javascript/core/API.js");

include("Javascript/views/MainView.js");

include("Javascript/views/VistaSinUsuarios.js");

include("Javascript/views/VistaMostrarUsuarios.js");

include("Javascript/views/VistaBorrarUsuario.js");

include("Javascript/views/VistaIntroducirDatos.js");

include("Javascript/views/VistaEsperar.js");

include("Javascript/views/VistaMostrarDias.js");

include("Javascript/views/VistaMostrarHoras.js");

include("Javascript/views/VistaCitaAsignada.js");

include("Javascript/views/VistaInformacion.js");

include("Javascript/views/VistaInformacionError.js");

include("Javascript/views/VistaOtroDia.js");

include("Javascript/views/AboutView.js");

include("Javascript/views/SnippetView.js");

El Widget de Cita Médica consta de catorce vistas, de las cuales trece son vistas de barra

lateral por lo que en su definición extienden la clase base KONtx.system.SidebarView.

A continuación y a modo de ejemplo se citan las primeras líneas del código del archivo

VistaCitaAsignada.js, en el que se define la clase VistaCitaAsignada.

var VistaCitaAsignada = new KONtx.Class({

ClassName: 'MiVistaCitaAsignada',

Extends: KONtx.system.SidebarView,

createView: function() {

...

La vista número catorce es la vista de Snippet del widget y extiende la clase base

KONtx.system.SnippetView. En este caso, el archivo VistaSnippet.js empieza de la

siguiente manera:

var VistaSnippet = new KONtx.Class({

ClassName: 'MiVistaSnippet',

Extends: KONtx.system.SnippetView,

...

En la ilustración 17 se muestran las doce vistas de barra lateral diseñadas específicamente

para el Widget de Cita Médica. En ellas se contemplan todos los posibles casos que pueden

darse durante el proceso de petición de cita con el médico a través del widget.

Page 3: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

42

Ilustración 18: Vistas del Widget de Cita Médica.

La inicialización de las vistas es implementada llamando al método

KONtx.application.init (Object config). El objeto config incluye la propiedad

views que consiste en un array con todas las vistas que se van a inicializar, estando cada

una definida por el id de la vista, una clase de vista, y un parámetro de datos opcional.

Page 4: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

43

//Inicialización de las vistas

KONtx.application.init({

views: [

{ id: 'view-Main', viewClass: MainView },

{ id: 'view-IntroducirDatos', viewClass: VistaIntroducirDatos },

{ id: 'view-Esperar', viewClass: VistaEsperar },

{ id: 'view-MostrarDias', viewClass: VistaMostrarDias },

{ id: 'view-MostrarHoras', viewClass: VistaMostrarHoras },

{ id: 'view-CitaAsignada', viewClass: VistaCitaAsignada },

{ id: 'view-Informacion', viewClass: VistaInformacion },

{ id: 'view-InformacionError', viewClass: VistaInformacionError },

{ id: 'view-OtroDia', viewClass: VistaOtroDia },

{ id: 'view-About', viewClass: AboutView },

{ id: 'snippet-1', viewClass: VistaSnippet, data: { message1: "Versión 2.7",

message2: "Cita Médica" , message3: "Servicio Andaluz de Salud"} },

],

defaultViewId: 'view-Main',

settingsViewId: 'view-About',

});

Dentro del objeto config también se especifica el id de la vista por defecto (la vista que se

despliega al activar el Snippet) así como la vista que se mostrará al seleccionar el botón

verde (Settings) ubicado en la barra de herramientas global que se encuentra en la parte

inferior de las vistas de barra lateral del widget.

Ilustración 19: Aspecto de la vista view-About.

Como ya se comentó anteriormente, la forma de crear una nueva vista es extender la clase

base correspondiente sobrescribiendo los métodos createView() y updateView() de

dicha clase.

Es en el código del método createView() donde se añaden los controles que definen la

estructura de la vista. En el Widget de Cita Médica los controles añadidos a cada vista se

guardan en la estructura this.controls (donde this representa a la vista en cuestión).

Page 5: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

44

Haciendo esto se consigue que, si la vista se oculta, cuando vuelva a mostrarse ofrezca un

contexto consistente al usuario, reanudándose con los mismos datos, foco y estado que

presentaba cuando se ocultó. Un ejemplo del uso de la estructura this.controls en el

Widget de Cita Médica se encuentra en el siguiente fragmento de código:

var VistaMostrarUsuarios = new KONtx.Class({

ClassName: 'MiVistaMostrarUsuarios',

Extends: KONtx.system.SidebarView,

createView: function() {

this.controls.backButton = new KONtx.control.BackButton({

label:'Usuarios Guardados',

guid: "back_button",

}).appendTo(this);

this.controls.texto0 = new KONtx.element.Text({

data: "Escoja el usuario para el que desea pedir cita.",

wrap:true,

styles: {

vOffset: this.controls.backButton.outerHeight+20,

hOffset: 5,

width: this.width-5,

fontSize: KONtx.utility.scale(16),

color: '#FFFFFF'

}

}).appendTo(this);

this.controls.botonusuario1 = new KONtx.control.TextButton({

label: 'Usuario1',

guid: "botonusuario1",

styles: {

vOffset: this.controls.texto0.outerHeight+20,

},

events: {

onSelect: function(event) {

datosusuario1json=currentAppConfig.get('usuario1');

datosusuario1=JSON.parse(datosusuario1json);

KONtx.messages.store('nuss', datosusuario1.nuss);

KONtx.messages.store('dia', datosusuario1.dia);

KONtx.messages.store('mes', datosusuario1.mes);

KONtx.messages.store('anio', datosusuario1.anio);

KONtx.messages.store('dni', datosusuario1.dni);

KONtx.application.addViewConfig({ id: 'view-Esperar',

viewClass: VistaEsperar });

KONtx.application.loadView('view-Esperar');

}

}

}).appendTo(this);

this.controls.botonusuario1.hide();

...

En aquellos casos en los que el widget ha requerido que los datos mostrados en la vista se

actualicen cuando ésta se cargue se ha implementado una llamada XMLHttpRequest en el

método updateView() de dicha vista y se ha usado la respuesta para actualizar los textos

o datos mostrados. Así ocurre en el método updateView() de la clase VistaMostrarDias (el

código íntegro del archivo VistaMostrarDias.js puede consultarse en el Anexo I).

Page 6: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

45

En este punto cabe preguntarse cómo se pueden recuperar datos presentes en una

determinada vista cuando estamos en otra vista diferente. En el Widget de Cita Médica este

problema se soluciona recurriendo al Centro de Mensajes del KONtx Framework.

El Centro de Mensajes del KONtx Framework proporciona una tabla basada en claves para el

almacenamiento de mensajes, combinada con eventos broadcast que se disparan cuando se

añaden datos nuevos o se actualizan los ya existentes en la tabla. Esta tabla puede

describirse como un buzón de almacenamiento en el que se guardan los mensajes de un

widget individual.

Para realizar el paso de mensajes entre las diferentes vistas del Widget de Cita Médica se

utilizan los métodos KONtx.messages.store(key, value), para almacenar el array,

objeto o tipo primitivo value asociado a la clave key, y KONtx.messages.fetch(key)

para recuperarlo posteriormente.

6.2.2 Implementación de diálogos

En el desarrollo del Widget de Cita Médica se han utilizado distintos diálogos con objeto de

ofrecer información al usuario durante el proceso de petición de cita.

Ilustración 20: Diálogo de alerta de dato no válido y diálogo de confirmación de cancelación de cita.

En la vista en la que el usuario introduce sus datos se han implementado diálogos de alerta

que se muestran si el número de la seguridad social, la fecha de nacimiento o el D.N.I.

introducidos no superan la validación pertinente, en cuyo caso no se llega a realizar la

petición al servidor intermedio y en consecuencia tampoco al servidor de InterS@S. La

Page 7: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

46

validación de los datos la realizan una serie de funciones incluidas en el archivo

Javascript/core/API.js que devuelven un valor diferente según se haya detectado o

no un error. Si hay error se mostrará al usuario el diálogo que corresponda para facilitarle la

corrección del mismo. A modo de ejemplo, a continuación se cita un fragmento del código

de detección de errores incluido en el archivo API.js:

function mayor_edad(edad,dia1,mes1,anio1){

hoy=new Date(getRealTime()*1000);

log(hoy);

var dia=hoy.getDay();

var mes=hoy.getMonth()+1;

var anio=hoy.getFullYear();

if ( (anio-anio1)>edad )

{ return true; }

if ( (anio-anio1)==edad && (mes-mes1)>0 )

{ return true; }

if ( (anio-anio1)==edad && (mes-mes1)==0 ){

if ((dia1-dia)<=0 )

/*Devolvemos true porque la fecha de nacimiento introducida corresponde con un

usuario mayor de la edad pasada como parámetro y en la vista View-

IntroducirDatos se muestra un alert si no se ha introducido el dni*/

{ return true; }

}

/*Devolvemos false en caso contrario y no se mostrará un alert aunque no se haya

introducido dni ya que no es obligatiorio introducirlo en el caso de menores de

edad*/

return(false);

}

Por su parte, el código incluido en el evento OnSelect del botón Solicitar/Consultar Cita de

la vista en la que el usuario introduce sus datos es el encargado de comprobar los datos

introducidos y de mostrar la alerta que corresponda:

this.controls.pedircita = new KONtx.control.TextButton({

label: 'Solicitar/Consultar Cita',

guid: "pedircita",

styles: {

vOffset: this.height-this.controls.backButton.outerHeight,

},

events: {

onSelect: function(event) {

KONtx.messages.store ('nuss', auxiliar2.controls.numeross.getValue());

KONtx.messages.store ('dia', auxiliar2.controls.dia.getValue());

KONtx.messages.store ('mes', auxiliar2.controls.mes.getValue());

KONtx.messages.store ('anio', auxiliar2.controls.anio.getValue());

KONtx.messages.store ('dni', auxiliar2.controls.dni.getValue());

var nuss=Quita_blancos(KONtx.messages.fetch('nuss'));

var dia1=Quita_blancos(KONtx.messages.fetch('dia'));

var mes1=Quita_blancos(KONtx.messages.fetch('mes'));

var anio1=Quita_blancos(KONtx.messages.fetch('anio'));

var dni=Quita_blancos(KONtx.messages.fetch('dni'));

//Si todos los campos se han rellenado se carga la vista MostrarDias

if(nuss==''){alerta1_1.show();}

Page 8: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

47

else{

if(dia1==''||mes1==''||anio1==''){alerta1_2.show();}

else{

if (validar_ss(nuss)==false){alerta2.show();} //Número de la seguridad

social incorrecto

else{//Comprobamos la validez de la fecha de nacimiento

contenido=dia1+'/'+mes1+'/'+anio1;

switch (comprueba_fecha(contenido)){

/*

case 1:

alerta3.show();//Carácter no válido en la fecha de nacimiento

break;

case 2:

alerta4.show();//Fecha de nacimiento no válida

break; */

case 3:

alerta5.show();//Año incorrecto en la fecha de nacimiento

break;

case 4:

alerta6.show();//Mes incorrecto en la fecha de nacimiento

break;

case 5:

alerta7.show();//Día incorrecto en la fecha de nacimiento

break;

default:

//La fecha es correcta. Comprobamos el dni.

if((dni=='')&&(mayor_edad(14,dia1,mes1,anio1)==true)){

alerta8.show();//Debe introducir su dni

}

else{

/*Se ha introducido el dni. Comprobamos que es correcto. El

widget sólo admite el dni, si permitiésemos otros documentos

esta comprobación se realizaría sólo para el dni.*/

if

(((nif(dni)==false)&&(mayor_edad(14,dia1,mes1,anio1)==true))||

((nif(dni)==false)&&(mayor_edad(14,dia1,mes1,anio1)==false)&&(

dni!=''))){alerta9.show();}//El dni introducido no es válido

else{

KONtx.application.addViewConfig({ id: 'view-Esperar',

viewClass: VistaEsperar });

KONtx.application.loadView('view-Esperar');

}

}

}

}

}

}

}

}

}).appendTo(this);

Cada una de las alertas se define indicando título, texto, los botones que incluye y las

funciones callback para cada uno de ellos. A modo de ejemplo se citará a continuación el

fragmento de código que implementa el diálogo a desplegar en caso de que el número de la

seguridad social introducido no sea válido.

var alerta2= new KONtx.dialogs.Alert({

title: 'Dato no válido',

message: 'El número de la seguridad social introducido no es correcto.',

focusOnCompletion: this.element.focusedView,

Page 9: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

48

buttons:[

{ label: 'Aceptar'}

]

});

6.2.3 Uso del objeto XMLHttpRequest de JavaScript

El Widget de Cita Médica hace uso intensivo del objeto XMLHttpRequest (XHR) de

JavaScript. El objeto XHR permite hacer peticiones HTTP y HTTPS a un servidor web y

capturar su respuesta de forma similar a como lo harían un navegador o un cliente web de

línea de comandos. Para los datos transferidos puede utilizarse cualquier formato basado en

texto, lo que incluye: texto plano, XML, JSON, HTML y codificaciones particulares

específicas.

El uso más extendido del objeto XHR es entregar contenido dinámico, proporcionando

actualizaciones asíncronas de una página web en el navegador del usuario, mediante

tecnologías construidas sobre él como por ejemplo AJAX. En este caso existe una limitación

importante (conocida como política del mismo origen o en inglés cross-domain restriction)

por la cual los programas ejecutados desde un dominio no pueden afectar a los programas y

datos de otro dominio. Es decir, el código JavaScript debe residir en el mismo dominio que

el recurso al que trata de acceder.

Esta restricción evidentemente no existe en la plataforma Yahoo! Connected TV que

permite que el código JavaScript de los widgets que reside en el televisor del usuario acceda

mediante el objeto XMLHttpRequest a contenido web almacenado en cualquier servidor.

Con un objeto XMLHttpRequest se puede hacer una petición a una URL pero lo que reside

en esa URL no tiene que ser en principio algo demasiado útil para nuestros propósitos. Así,

si necesitamos unos datos específicos, ¿qué sentido tendría obtener una página de HTML

entera que después nuestro código tendría que analizar hasta obtener el dato que

necesitamos? Resulta evidente que, en este caso, la respuesta debería ser pequeña y

fácilmente analizable12. Podemos referirnos a este tipo de recursos, que tienen una entrada

y salida relativamente simple y que funcionan más como la llamada a una función que como

una aplicación web convencional, como servicios web o simplemente servicios. Hay muchas

formas de implementar servicios web pero atendiendo a la definición propuesta, basada en

lo que es en esencia un servicio web, se puede considerar que el servidor intermedio del

esquema del apartado 6.1 (ilustración 16) aloja una colección de servicios web relacionados

12 Esta característica del recurso adquiere especial relevancia en el caso de desarrollo de aplicaciones para dispositivos con recursos limitados como ocurre en el desarrollo del Widget de Cita Médica, ya que en estos casos interesa liberar al dispositivo de toda la carga computacional que sea posible.

Page 10: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

49

(una web API) que se encarga de proporcionar contenido a las distintas vistas del Widget de

Cita Médica a lo largo del proceso de petición de cita en InterS@S.

La forma de realizar una petición a un recurso web con el objeto XMLHttpRequest puede

resumirse en los siguientes pasos:

1. Se crea una nueva instancia de XMLHttpRequest.

var xhr = new XMLHttpRequest();

2. Se prepara la llamada al recurso web con el método open.

xhr.open("POST", url, true);

El primer parámetro es el método que se usará en la petición, que puede ser GET o

POST. En el método GET los parámetros de la petición se pasan en la URL de la

petición mientras que en el método POST se incluyen en el cuerpo de la misma. Por

motivos de seguridad, en el Widget de Cita Médica todas las llamadas se realizan con

el método POST. De esta forma los parámetros de la petición no viajarán expuestos

en la url.

El segundo parámetro es una cadena con la URL del recurso al que estamos llamando

(en el Widget de Cita Médica sería la url del archivo php alojado en el servidor

intermedio que contiene el código del servicio web correspondiente).

La opción "true" indica que la llamada es asíncrona y que, por tanto, el código del

widget seguirá ejecutándose mientras no llegue la respuesta. Un valor "false" en este

tercer parámetro, que recibe el nombre de asynchronous flag, se correspondería con

una llamada síncrona, en la que el código deja de ejecutarse al enviar la llamada,

quedando a la espera de una respuesta. Para validar un widget y hacerlo público en

su Galería de widgets, Yahoo! exige que todas las llamadas que se realicen sean

asíncronas, directriz que se ha seguido en el desarrollo del Widget de Cita Médica.

3. Se programa la captura de la respuesta:

xhr.onreadystatechange = function () {

if (xhr.readyState === 4) {

if (xhr.status === 200) {

log("!!!!!!!!!!La petición xhr se completó http: "+xhr.status);

var respuesta=xhr.responseText;

...

KONtx.application.setNetworkRequestFailed(false);

} else {

log("!!!!!!!!!!La petición xhr falló http: "+xhr.status);

KONtx.application.setNetworkRequestFailed(true);

}

}

};

Page 11: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

50

Para saber si la respuesta está lista, el código anterior comprueba la propiedad

readyState del objeto xhr, la cual refleja en todo momento en qué punto de su

ciclo de vida se encuentra la llamada. Tras instanciar el objeto XHR su valor es 0.

Después de llamar al método open pasa a ser 1. El ciclo de vida de la llamada

continúa hasta que la respuesta del servidor es recibida completamente, momento

en el cual el valor de la propiedad readyState pasa a ser 4.

Hay que esperar a tener un readyState de 4 para obtener la respuesta del

servidor, algo que se comprueba cada vez que se produce el evento

readystatechange (cada vez que readyState cambia de valor). Es posible, tal

como se hace en el código de ejemplo, declarar una función callback en la propiedad

onreadystatechange de XMLHttpRequest. Esta función se encargará básicamente

de comprobar que la respuesta ha llegado completamente (readyState=4) y que

el código HTTP de la misma es 200 (respuesta estándar para peticiones HTTP

exitosas). De esta forma no sólo nos aseguramos de que se ha recibido

completamente la respuesta sino que además tenemos constancia de que el servidor

ha logrado procesar correctamente la petición. En caso de que la comprobación sea

positiva, se recupera la respuesta del servidor con ayuda de la propiedad

responseText y se realizan las acciones oportunas.

4. Se envía la petición al servidor llamando al método send() de XHR.

xhr.send(params);

El argumento que se pasa al método send() es un string con los parámetros que se

enviarán por POST. En caso de que la llamada se realizase por el método GET se

pasaría el objeto null como argumento.

Una posible inicialización de la variable params antes de llamar al método send()

podría ser:

var params="param1=1&param2=2"

Es posible realizar las llamadas escribiendo las líneas de código descritas cada vez que lo

necesitemos, no obstante, en el Widget de Cita Médica se ha optado por implementar una

función que encapsula estos pasos y algunos otros relacionados con el manejo de los

estados de red y que se describen en el siguiente apartado.

Page 12: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

51

6.2.4 Comprobación del estado de la red antes de las peticiones XHR

Los widgets de TV deben detectar los cambios producidos en la disponibilidad de la red y

dar soporte tanto al funcionamiento en modo online como en modo offline de forma

adecuada.

Para conseguirlo es necesario realizar una comprobación del estado de red antes de cada

llamada a un recurso web basada en el objeto XMLHttpRequest.

Un widget no debe nunca iniciar una petición XHR cuando la red física está caída. En el

Widget de Cita Médica se ha cumplimentado este requisito por medio del método

KONtx.application.isPhysicalNetworkDown().

Dentro del archivo Content>Javascript>core>API.js del Widget de Cita Médica se

encuentra la siguiente función encargada de implementar las peticiones al servidor

intermedio a través del objeto XMLHttpRequest:

function funcion_xmlhttprequest(o){

if (KONtx.application.isPhysicalNetworkDown()) {

KONtx.utility.WaitIndicator.off();

log("Network down, no se permiten peticiones xhr!");

return;

}

var xhr = new XMLHttpRequest();

xhr.autoRedirect = true;

xhr.open(o.metodo, o.url, true);

xhr.onreadystatechange = function () {

if (xhr.readyState === 4) {

if (xhr.status === 200) {

log("!!!!!!!!!!!!!!La petición xhr tuvo éxito http: "+xhr.status);

o.success(xhr);

KONtx.application.setNetworkRequestFailed(false);

} else {

log("!!!!!!!!!!!!!!La petición xhr falló http: "+xhr.status);

o.error();

KONtx.application.setNetworkRequestFailed(true);

}

}

};

xhr.send(o.params);

}

Si se revisa el código de la función anterior comprobamos que, antes de crear el nuevo

objeto XMLHttpRequest y para comprobar que la red física está funcionando, se llama al

método KONtx.application.isPhysicalNetworkDown(). Esto plantea una dificultad,

y es que esta función no está presente en versiones antiguas del KONtx Framework. Para

que el Widget de Cita Médica mantenga compatibilidad hacia atrás se ha incluido el

siguiente parche en el archivo init.js:

Page 13: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

52

//En primer lugar comprobamos que existe la función y si no es así la definimos

if (typeof KONtx.application.isPhysicalNetworkDown === "undefined") {

(function () {

var original = KONtx.application.setNetworkRequestFailed,

setDownByUser = false;

delete KONtx.application.setNetworkRequestFailed;

KONtx.application.setNetworkRequestFailed = function (status) {

original(status);

setDownByUser = status;

};

KONtx.application.isPhysicalNetworkDown = function () {

if (KONtx.application.getNetworkDownStatus) {

return !setDownByUser;

}

return false;

};

}());

}

El archivo API.js se incluye en el código del widget con la siguiente línea de código del

archivo init.js:

include("Javascript/core/API.js");

De esta forma, cada vez que desde el widget se realiza una petición al servidor intermedio

se instancia un objeto usando notación literal de objetos. Este objeto contiene las

propiedades de la petición y se pasa como parámetro a la función

funcion_xmlhttprequest(o).

A continuación se cita un ejemplo extraído del Widget de Cita Médica:

peticion1={

metodo:"POST",

url:'http://www.s414468704.mialojamiento.es/proyecto/servidor_intermedio5.php',

params:'id_us='+nuss+'&dia='+dia+'&mes='+mes+'&anio='+anio+'&dni='+dni+'&origen=

1',

success: function(requestObj){

/*al no usar var delante de datosjson2 nos estamos refiriendo a la variable

global datosjson2*/

datosjson_web=requestObj.responseText;

datosjson=datosjson_web.split("SEPARADOR");

/*En el primer JSON van los datos de la cita existente previamente o los que

hay que pasar a la siguiente llamada, además de los días, para obtener las

horas disponibles. Lo guardamos en Objeto0.*/

Objeto0 = JSON.parse(datosjson[0]);

Objeto1 = JSON.parse(datosjson[1]);

/*Comprobamos que no hay una cita ya asignada*/

if (Objeto0.enlace_cancelar=="no hay cita que cancelar"){

/*Comprobamos que hemos conseguido ingresar a InterS@S*/

if (Objeto0.comprueba_acceso=="ok"){

KONtx.messages.store ('cookies_sesion', Objeto0.sesion);

KONtx.messages.store ('tipocita', Objeto0.tipocita);

KONtx.messages.store ('actividad_agenda', Objeto0.actividad_agenda);

KONtx.messages.store ('modalidad_agenda', Objeto0.modalidad_agenda);

Page 14: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

53

KONtx.messages.store ('codigo_agenda', Objeto0.codigo_agenda);

KONtx.messages.store ('codigo_profesional', Objeto0.codigo_profesional

);

KONtx.messages.store ('domicilio_usuario', Objeto0.domicilio_usuario);

KONtx.messages.store ('nuss_usuario', Objeto0.nuss_usuario);

KONtx.messages.store ('nombre_usuario', Objeto0.nombre_usuario);

KONtx.messages.store ('telefonos_usuario', Objeto0.telefonos_usuario);

KONtx.messages.store ('desplazado', Objeto0.desplazado);

KONtx.messages.store ('tarea_desc', Objeto0.tarea_desc);

KONtx.messages.store ('centro_desc', Objeto0.centro_desc);

KONtx.messages.store ('centro_codigo', Objeto0.centro_codigo);

KONtx.messages.store ('nombre_profesional',

Objeto0.nombre_profesional);

KONtx.messages.store ('Objeto0', Objeto0);

KONtx.messages.store ('Objeto1', Objeto1);

/*Ponemos flag a 1 para que en la vista mostrarDias se busquen las

horas disponibles.*/

KONtx.messages.store ('flag', '1');

KONtx.application.loadView('view-MostrarDias',null,false);

}

else{

KONtx.utility.WaitIndicator.off();

KONtx.messages.store ('informacion', Objeto0.comprueba_acceso);

/*El tercer parámetro del método loadView() es "Boolean

noSaveCurrentViewInHistory". Si no se pone el tercer parámetro por

defecto es false. Si lo ponemos a true no se guardará la vista actual

en la historia (No volvemos a esta vista al seleccionar el backbutton

en la vista que estamos cargando).*/

KONtx.application.loadView('view-InformacionError',null,true);

}

}

//Por el contrario, si hay una cita asignada previamente

else{

KONtx.utility.WaitIndicator.off();

KONtx.messages.store ('cookies_sesion', Objeto0.sesion);

KONtx.messages.store ('enlace_cancelar', Objeto0.enlace_cancelar);

KONtx.messages.store('fecha_cita',Objeto0.fecha_cita);

KONtx.messages.store('hora_cita',Objeto0.hora_cita);

KONtx.messages.store('orden',Objeto0.orden);

KONtx.messages.store('centro',Objeto0.centro);

KONtx.messages.store('direccion',Objeto0.direccion);

KONtx.messages.store('ubicacion',Objeto0.ubicacion);

KONtx.messages.store('profesional',Objeto0.profesional);

KONtx.messages.store('nombre_usuario',Objeto0.nombre_usuario);

KONtx.messages.store('fecha_solicitud',Objeto0.fecha_solicitud);

KONtx.application.loadView('view-CitaAsignada');

}

},

error: function(){

KONtx.utility.WaitIndicator.off();

log("Error en la conexión");

}

}

Una vez definido el objeto peticion1 ya sólo hay que pasarlo como parámetro de la función

funcion_xmlhttprequest(o), la cual se encargará de la instanciación del objeto xhr y de

comprobar el estado de la red antes de realizar la llamada.

funcion_xmlhttprequest(peticion1);

KONtx.utility.WaitIndicator.up();

Page 15: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

54

Tras llamar a la función se llama al método KONtx.utility.WaitIndicator.up()que

se encarga de mostrar la animación de espera, la cual se mantendrá hasta que se reciba la

respuesta del servidor y se realicen las acciones oportunas según dicha respuesta.

Es la función funcion_xmlhttprequest(o) la que se encarga de gestionar los estados

de la red en cada llamada por lo que, una vez definida dicha función, basta con llamarla cada

vez que se quiera hacer una petición al servidor intermedio. Cuando la petición falla es el

propio código de funcion_xmlhttprequest(o) el que llama al método

KONtx.application.setNetworkRequestFailed (true), método que se encarga

de mostrar un diálogo de alerta y sacar al widget de la vista lateral en la que nos

encontremos para volver al dock, además de mostrar iconos informativos de NoNetwork.

Ilustración 21: Diálogo de alerta e icono informativo del estado NoNetwork.

En el caso del Widget de Cita Médica, no obstante, se planteaba el problema de que, una

vez que una petición fallaba y desde funcion_xmlhttprequest(o) se llamaba a

KONtx.application.setNetworkRequestFailed (true), aún en el caso de que

hubiese desaparecido la causa que en un principio había provocado el fracaso en la petición,

el widget no conseguía recuperarse del estado de NoNetwork. La causa es que el Widget de

Cita Médica no realiza ninguna petición XMLHttpRequest en su vista principal (la que se

carga al lanzar el widget desde su Snippet en el dock) ya que el usuario debe primero

introducir sus datos o elegir un usuario de la vista de Usuarios Guardados, por lo que no

existía llamada a KONtx.application.setNetworkRequestFailed(false) y nunca

se llegaba a salir del estado de NoNetwork. En consecuencia, al lanzar el widget desde el

Page 16: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

55

dock se desplegaba la vista lateral principal pero inmediatamente se mostraba el diálogo de

alerta para volver al dock tras seleccionar el botón aceptar.

Para solucionar este inconveniente se ha optado por realizar llamadas XHR a un archivo php

de contenido trivial al dispararse los eventos OnApplicationStartup y

OnActivateSnippet, con la finalidad de comprobar el estado de la red cuando se inicia

la plataforma y cuando se activa el Snippet. La forma de conseguir esto es suscribir el widget

a dichos eventos del Host haciendo uso del método subscribeTo(), además de definir

los manejadores de eventos asociados a los mismos. El código utilizado, que se encuentra

en el archivo init.js, es el siguiente:

//Definimos el objeto peticion0

peticion0={

metodo:"POST",

url:'http://www.s414468704.mialojamiento.es/proyecto/holamundo.php',

params:"param1=1&param2=2",

success: function(requestObj){

var datosjson =requestObj.responseText;

log(datosjson);

var Objeto = JSON.parse(datosjson);

log ("Comprobando red en onApplicationStartup: "+Objeto.mensaje);

},

error: function(){

log("Error en la conexión");

}

};

var EventHandlers = (function () {

return {

//Definición de la función callback para el evento OnApplicationStartup

onApplicationStartup: function () {

//Petición al servidor intermedio para comprobar el estado de la conexión

funcion_xmlhttprequest(peticion0);

},

onActivateSnippet:function () {

funcion_xmlhttprequest(peticion0);

}

}

}());

EventHandlers.onApplicationStartup.subscribeTo(KONtx.application,

"onApplicationStartup", EventHandlers);

EventHandlers.onActivateSnippet.subscribeTo(KONtx.application, "onActivateSnippet",

EventHandlers);

Page 17: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

56

El testeo en el caso de estado de red "desconectado" es parte del QA13 del widget y del

proceso de aprobación de Yahoo! previo a la inclusión de dicho widget en la Galería de

Widgets en entornos de producción.

En este sentido, hay que asegurarse de que el widget se comporta adecuadamente en los

siguientes estados de "desconexión":

1. Pérdida de conectividad manteniendo la dirección IP. Para probar esta situación

basta con desconectar el cable WAN del router, testear el comportamiento del

widget, volver a conectar el cable y testear de nuevo.

2. Pérdida de conectividad y pérdida de la dirección IP. Para probar esta situación basta

con desconectar el cable LAN del router, testear el comportamiento del widget,

volver a conectar el cable y testear de nuevo.

Durante la fase de desarrollo del Widget de Cita Médica se testeó el comportamiento del

widget deteniendo y reiniciando un servidor proxy instalado para este propósito en la

máquina virtual que alberga al simulador. El servidor proxy elegido fue Polipo. Para instalar

Polipo se abrió una ventana de consola y se escribió:

sudo apt-get install polipo

A continuación se especificó el valor de la variable de entorno KF_PROXY en el archivo

$HOME/TVWidgets/Konfabulator.env incluyendo la siguiente línea:

KF_PROXY=http://localhost:8123 Konfabulator

De esta forma, cada vez que se lanzaba el simulador éste se conectaba a la red través de

Polipo. Para iniciar y detener Polipo durante las pruebas se utilizaron los siguientes

comandos:

sudo /etc/init.d/polipo start

sudo /etc/init.d/polipo stop

6.2.5 JSON Parser

La respuesta del servidor intermedio es siempre un string que contiene datos en formato

JSON. Como ya se ha comentado, el formato JSON es muy adecuado para ser consumido por

una aplicación JavaScript.

13 QA: Quality Assurance. En español puede traducirse como certificación de la calidad.

Page 18: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

57

Al ser JSON un subconjunto de la notación literal de objetos de JavaScript los datos en este

formato pueden convertirse en objetos JavaScript simplemente pasándolos como

argumento a la función eval(). La función eval() de JavaScript es muy rápida, pero

puede compilar y ejecutar cualquier programa JavaScript, lo que acarrea problemas de

seguridad. Para defenderse de posibles amenazas se debe usar un analizador JSON en lugar

de eval().

El Widget Engine de Yahoo! Connected TV pone a disposición del desarrollador de widgets

un analizador JSON. Este analizador JSON es entre tres y cuatro veces más rápido que la

versión JavaScript y está presente en todos los widgets por defecto, por lo que no es

necesario ningún paso previo para utilizarlo.

La interfaz JSON del Widget Engine es la siguiente:

interface JSON {

variant parse( string inString );

string stringify( variant inValue );

};

Los métodos son los mismos que los del analizador JavaScript, excepto que no existen

parámetros adicionales.

Otra diferencia respecto al analizador JavaScript es que el analizador JSON del Widget

Engine no utiliza la función toJSON().

Existe una instancia global de la clase llamada JSON, por lo que su uso es igual que el del

analizador JavaScript. Por ejemplo:

var v = JSON.parse(inString);

var s = JSON.stringify(inValue);

El analizador es estricto, no usa expresiones regulares y respeta la gramática de JSON

descrita en <http://www.json.org/> con la salvedad de que acepta comillas simples o dobles

tal como ocurre en el estándar JavaScript.

En el Widget de Cita Médica se usa el analizador JSON del Widget Engine en múltiples

ocasiones para convertir en objetos JavaScript los datos en formato JSON procedentes del

servidor intermedio. Un ejemplo puede ser el siguiente fragmento de código extraido de

CitaAsignada.js, archivo en el que se implementa la clase VistaCitaAsignada:

var datosjson=requestObj.responseText;

/*Usamos el analizador JSON sobre los datos obtenidos*/

var Objeto = JSON.parse(datosjson);

KONtx.messages.store('informacion',Objeto.informacion);

KONtx.application.setNetworkRequestFailed(false);

/*Para que una vez borrada la cita no podamos volver a esta vista con el

backButton, pasamos el valor true en el tercer parámetro del método loadView()*/

Page 19: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

58

KONtx.application.loadView('view-Informacion',null,true);

6.2.6 Gestión de usuarios guardados y almacenamiento persistente

Hay tres tipos de almacenamiento disponibles para perfiles y widgets:

Store currentAppConfig: Almacena datos para el widget únicamente. Los datos

almacenados no cambian si se cambia de perfil, pero son diferentes en cada widget.

Store currentAppData: Almacena datos asociados con el widget y el perfil. Los

datos almacenados aquí son diferentes en cada perfil y en cada widget.

Store currentProfileData: Almacena datos para un perfil únicamente. Los

datos son privados para un perfil, pero están disponibles para todos los widgets y

pueden ser sobrescritos por otro widget si se utiliza la misma clave.

Los widgets y los perfiles pueden almacenar cadenas JSON. Por tanto, si se quiere almacenar

un objeto JavaScript se pasa antes como parámetro del método JSON.stringify(). Si

por el contrario lo que se quiere es recuperar un valor almacenado habrá que hacer uso del

método JSON.parse() para obtener de nuevo el objeto JavaScript.

La información almacenada en el objeto persistente se mantiene después de salir del

widget, lanzar otro widget o cambiar de perfil ya que el almacenamiento es independiente

del ciclo de vida del widget. Sólo la desistalación del widget borra todos los datos asociados

al widget. De igual forma borrar un perfil elimina todos los datos asociados a dicho perfil.

Cada elemento en el objeto Store es un par clave-valor. Cada instancia del objeto Store es

un conjunto de pares clave-valor con una estructura open-ended, es decir, que permite su

extensión en cualquier momento sin modificar los datos existentes. El modelo de datos

puede describirse como una colección de pares "clave,valor" en formato texto. Las claves

son únicas sólo en cada objeto Store.

Los métodos disponibles son los mismos para los tres tipos de almacenamiento:

Boolean delete (String key)

Borra el par clave-valor especificado por key. El parámetro key es una cadena de

texto que identifica de manera única el par que debe borrarse. Devuelve true si la

operación se completa con éxito o si no se encuentra la clave key.

String get (String key)

Devuelve el valor del par clave-valor cuya clave es key. Devuelve null si la clave no

existe en el objeto Store.

Page 20: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

59

Array getIDs()

Devuelve un array de cadenas que contiene todas las claves existentes para el

objeto. Si no existe ninguna clave, el método devuelve un array vacío.

Boolean set (String key, String value)

Establece un valor asociado con una clave única. El parámetro key es la cadena que

servirá de clave única y que se asociará a la cadena value, el valor almacenado. Como

se mencionó anteriormente, si el valor es un objeto JavaScript antes de usar el

método set() previamente habría que llamar a JSON.stringify() para

convertirlo en una cadena JSON.

Este almacenamiento persistente ofrecido por la plataforma es la base del código que

permite al usuario del Widget de Cita Médica ingresar al sistema seleccionando su nombre

de la lista de usuarios guardados que ofrece la vista correspondiente.

Ilustración 22: Vista view-MostrarUsuarios.

Los datos de un usuario se almacenan en el objeto currentAppConfig en el momento en el

que dicho usuario ha completado con éxito una consulta o una petición de cita. Así se evita

la posibilidad de almacenar en el widget datos erróneos. Para evitar duplicidades, antes de

almacenar los datos del usuario se comprueba si dichos datos ya se guardaron en una

ocasión anterior. Asimismo, el último usuario que completa con éxito una petición o

consulta de cita aparecerá automáticamente en el lugar más alto en la vista de usuarios

guardados. El fragmento de código que se cita a continuación, y que forma parte del

método updateview() de la clase VistaCitaAsignada (definida en el archivo

Page 21: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

60

VistaCitaAsignada.js) del Widget de Cita Médica, implementa toda la funcionalidad descrita

anteriormente.

//Guardamos el usuario para el que se ha pedido o consultado cita con éxito.

var matrizclaves=currentAppConfig.getIDs();

var numerousuarios=matrizclaves.length;

log(numerousuarios);

var coincidencia=0;

if (numerousuarios>0){

for (i=1;i<=numerousuarios;i++){

eval("var datosusuario"+i+"=currentAppConfig.get('usuario"+i+"');");

eval("var auxiliar=JSON.parse(datosusuario"+i+");");

if (auxiliar.nuss==KONtx.messages.fetch('nuss')){

coincidencia=i;}

}

if (coincidencia>0){

/*Este switch podría sustituirse por una expresión más compacta usando

otro bucle for y el valor de la variable coincidencia*/

log("HAY COINCIDENCIA!!");

switch (coincidencia){

case 1:

break;

case 2:

currentAppConfig.set('usuario2',datosusuario1);

break;

case 3:

currentAppConfig.set('usuario2',datosusuario1);

currentAppConfig.set('usuario3',datosusuario2);

break;

case 4:

currentAppConfig.set('usuario2',datosusuario1);

currentAppConfig.set('usuario3',datosusuario2);

currentAppConfig.set('usuario4',datosusuario3);

break;

case 5:

currentAppConfig.set('usuario2',datosusuario1);

currentAppConfig.set('usuario3',datosusuario2);

currentAppConfig.set('usuario4',datosusuario3);

currentAppConfig.set('usuario5',datosusuario4);

break;

case 6:

currentAppConfig.set('usuario2',datosusuario1);

currentAppConfig.set('usuario3',datosusuario2);

currentAppConfig.set('usuario4',datosusuario3);

currentAppConfig.set('usuario5',datosusuario4);

currentAppConfig.set('usuario6',datosusuario5);

break;

case 7:

currentAppConfig.set('usuario2',datosusuario1);

currentAppConfig.set('usuario3',datosusuario2);

currentAppConfig.set('usuario4',datosusuario3);

currentAppConfig.set('usuario5',datosusuario4);

currentAppConfig.set('usuario6',datosusuario5);

currentAppConfig.set('usuario7',datosusuario6);

break;

default:

/*Esto no ocurrirá nunca porque ya hemos comprobado que hay

coincidencia (coincidencia>0)*/

}

}

Page 22: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

61

/*Si el usuario no ha sido guardado previamente se desplazan todas las

cadenas json guardadas (datos de usuarios previos) una posición dentro de

los datos persistentes.*/

else{

for (i=numerousuarios;i>0;i--){

j=i+1;

eval("currentAppConfig.set('usuario"+j+"',datosusuario"+i+");");

}

if (numerousuarios==7){

currentAppConfig.delete('usuario8');

}

}

}

/*Creamos el objeto datosusuarionuevo. Con dos replace y dos expresiones regulares

eliminamos los espacios del principio y del final de la cadena.*/

var nombre =

KONtx.messages.fetch('nombre_usuario').replace(/^\s+/g,'').replace(/\s+$/g,'');

/*Almacenamos los datos del nuevo usuario asociados a la clave 'usuario1'*/

var usuariojson="{'nombre' : '"+nombre+"', 'nuss': '"

+KONtx.messages.fetch('nuss')+ "', 'dia': '" +KONtx.messages.fetch('dia')+ "',

'mes': '" +KONtx.messages.fetch('mes')+ "', 'anio': '"

+KONtx.messages.fetch('anio')+ "', 'dni': '" +KONtx.messages.fetch('dni')+ "'}";

log(usuariojson); //comprobamos que estamos obteniendo la cadena esperada

/*Guardamos los datos del usuario en formato json asociados a la clave "usuario1"

(el último usuario será el que aparezca en el primer botón de usuarios guardados).

currentAppConfig.set() devuelve true si se consigue realizar la operación con

éxito.*/

funciona=currentAppConfig.set("usuario1", usuariojson);

/*Si el método set se ejecutó con éxito la siguiente línea muestra FUNCIONA=true en

la consola de comandos.*/

log("FUNCIONA="+funciona);

/*Las siguientes líneas muestran en consola el string en formato json que guardamos

en usuariojson. Así comprobamos que se almacenan los datos que necesitamos.*/

prueba2=currentAppConfig.get("usuario1");

log(prueba2);

/*Borramos la "ViewHistory" para que si se pulsa el botón volver vayamos a la vista

principal que es lo que nos interesa*/

KONtx.application.clearViewHistory();

Al usar el objeto currentAppConfig los usuarios guardados se asocian al widget pero son

independientes del perfil. Para que los usuarios guardados fuesen diferentes en función del

perfil bastaría con sustituir currentAppConfig por currentAppData.

Se ha explicado cómo se guarda un usuario en el almacenamiento persistente del widget,

pero también es necesario algo de código para extraer los datos almacenados y mostrar el

nombre de cada usuario guardado en la vista view-MostrarUsuarios. Desde esta vista el

usuario puede seleccionar uno de los usuarios guardados, evitándose así la molestia de

Page 23: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

62

tener que volver a introducir todos sus datos de nuevo para pedir una cita. El fragmento de

código que se cita a continuación es el encargado de esta funcionalidad del widget y se

encuentra en el archivo VistaMostrarUsuarios.js, en la definición del método

updateview() de la clase VistaMostrarUsuarios.

updateView: function() {

this.controls.botonusuario1.hide();

this.controls.botonusuario2.hide();

this.controls.botonusuario3.hide();

this.controls.botonusuario4.hide();

this.controls.botonusuario5.hide();

this.controls.botonusuario6.hide();

this.controls.botonusuario7.hide();

var matrizclaves = currentAppConfig.getIDs();

var numerousuarios=matrizclaves.length;

for (i=numerousuarios;i>0;i--){

eval("datosusuariojson=currentAppConfig.get('usuario"+i+"');");

datosusuario=JSON.parse(datosusuariojson);

eval("this.controls.botonusuario"+i+".setText(datosusuario.nombre);");

eval("this.controls.botonusuario"+i+".show();");

}

}

Como puede apreciarse, en un primer momento y haciendo uso del método hide(), se

ocultan todos los botones que se habían incluido en la vista en la llamada al método

createview(). Tras esto se entra en un bucle for que en cada iteración extrae del objeto

currentAppConfig los datos (en formato JSON) del usuario correspondiente, los

convierte en objeto JavaScript con ayuda de JSON.parse(), escribe el nombre del usuario

en el botón adecuado y, finalmente, muestra dicho botón usando el método show(). Esto

ocurre hasta que el contador, inicializado con el número de usuarios, alcanza el valor 0. De

esta forma se consigue que el usuario vea tantos botones en la vista como usuarios hay

guardados en currentAppConfig.

La vista view-MostrarUsuarios incluye, además, un botón para gestionar usuarios guardados

que, al ser seleccionado, hace que se cargue la vista view-BorrarUsuario. Esta vista muestra

de nuevo los usuarios guardados pero al seleccionar uno de ellos lo que hacemos en este

caso es borrar sus datos de currentAppConfig. El código encargado de esta acción es el

que se encuentra en la función callback del evento onSelect de cada botón. A modo de

ejemplo, se incluye a continuación el código del botón correspondiente al usuario1

almacenado en currentAppConfig.

this.controls.botonusuario1 = new KONtx.control.TextButton({

label: 'Usuario1',

guid: "botonusuario1",

styles: {

vOffset: this.controls.texto0.outerHeight+20,

},

Page 24: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

63

events: {

onSelect: function(event) {

var matrizclaves = currentAppConfig.getIDs();

var numerousuarios=matrizclaves.length;

if (numerousuarios>1){

for (i=2;i<=numerousuarios;i++){

var datosusuariojson;

log("datosusuariojson=currentAppConfig.get('usuario"+i+"');");

eval("datosusuariojson=currentAppConfig.get('usuario"+i+"');");

log (datosusuariojson);

var j=i-1;

log("currentAppConfig.set('usuario"+j+"',datosusuariojson);");

eval("currentAppConfig.set('usuario"+j+"',datosusuariojson);");

}

log("currentAppConfig.delete('usuario"+numerousuarios+"');");

eval("currentAppConfig.delete('usuario"+numerousuarios+"');");

auxiliar.updateView();//auxiliar representa a la vista actual

}

else{

eval("currentAppConfig.delete('usuario"+numerousuarios+"');");

KONtx.application.addViewConfig({ id: 'view-SinUsuarios', viewClass:

VistaSinUsuarios });

KONtx.application.loadView('view-SinUsuarios');

}

}

}

}).appendTo(this);

Ilustración 23: Vista view-BorrarUsuario.

Page 25: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

64

6.3 Web API en el servidor intermedio (PHP)

6.3.1 Peticiones al servidor de InterS@S con l ibcurl

Libcurl es una librería creada por Daniel Stenberg que permite conectar y comunicarse con

servidores utilizando diferentes tipos de protocolos. Actualmente libcurl soporta los

protocolos HTTP, HTTPS, FTP, FTPS, Gopher, Telnet, DICT, FILE, IMAP, IMAPS, LDAP, LDAPS,

POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet y TFTP. Libcurl también soporta

certificados SSL, HTTP POST, HTTP PUT, subida de ficheros usando FTP (también se puede

hacer con la extensión FTP de PHP), formularios HTTP, proxies, cookies, y autenticaciones

usuario+contraseña.

A continuación se describirá la forma en que la librería se utiliza en la web API a la que

realiza sus peticiones el Widget de Cita Médica.

PHP incorpora libcurl a partir de su versión 4.0.2. Por ello, el código de la web API

funcionará en cualquier servidor con una versión de PHP instalada igual o posterior.

La inicialización de una sesión cURL se realiza de la siguiente forma:

$curl = curl_init();

Tras la inicialización ya podemos especificar todas las opciones de la sesión cURL.

La primera opción de la sesión especifica la url de destino:

curl_setopt ($curl, CURLOPT_URL, $url);

Para obtener el resultado en una cadena de texto se asigna un 1 en la opción

CURLOPT_RETURNTRANSFER de libcurl.

curl_setopt ($curl, CURLOPT_RETURNTRANSFER, 1);

También es necesario especificar el contenido de la cabecera "User-Agent: " que se utilizará

en la petición HTTP:

curl_setopt ($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1;

en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1');

Si queremos que la respuesta incluya la cabecera HTTP:

curl_setopt ($curl, CURLOPT_HEADER, 1);

Aunque normalmente libcurl detecta de forma automática la versión SSL instalada en el

servidor al que se realiza la petición, en el caso del servidor de InterS@S resultó

Page 26: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

65

imprescindible especificar dicha versión como SSL 3.0 para que la petición se completara

con éxito.

curl_setopt ($curl, CURLOPT_SSLVERSION, 3);

Con la siguiente opción se comprueba que el certificado de servidor es válido. Esto equivale

a comprobar que la CA (Autoridad Certificadora) que lo firma está entre la CA reconocidas.

curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, true);

Para que la comprobación del certificado que hace libcurl fuera positiva hubo que seguir

una serie de pasos. Como el certificado de servidor de InterS@S está firmado por la Fábrica

Nacional de Moneda y Timbre fue necesario descargarse su certificado raiz

(FNMTClase2CA-FNMT.crt), exportarlo como certificado X.509 con cadena (PEM), y

subirlo al servidor intermedio. A continuación se especificó la ruta de este certificado raiz en

la siguiente opción de libcurl que sirve para especificar el certificado raiz de la CA que firma

el certificado de servidor al que estamos tratando de acceder vía HTTPS.

curl_setopt ($curl, CURLOPT_CAINFO, getcwd().'/FNMTClase2CA-FNMT.crt');

Con la siguiente opción se especifica que hay que comprobar el common name (CN) del

certificado de servidor:

curl_setopt ($curl, CURLOPT_SSL_VERIFYHOST, 2);

Habilitamos el paso de parámetros por POST:

curl_setopt ($curl, CURLOPT_POST, 1);

La variable $campos es la que contiene los parámetros de la petición a InterS@S que son

enviados haciendo uso del método POST:

curl_setopt ($curl, CURLOPT_POSTFIELDS, $campos);

Una vez especificadas todas las opciones de libcurl que necesitamos, podemos ejecutar la

sesión CURL.

$codigofuente = curl_exec ($curl);

En el ejemplo la respuesta se almacena en la variable $codigofuente, tras lo cual se

procedería a su análisis.

En todo momento, antes de abrir una sesión cURL nueva se ha tomado la precaución de

cerrar la sesión anterior.

curl_close ($curl);

Page 27: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

66

6.3.2 Mantenimiento de la ses ión

Uno de los puntos más delicados a la hora implementar con éxito el Widget de Cita Médica

era el mantenimiento de la sesión en el servidor de InterS@S en las sucesivas peticiones que

se le harían desde el servidor intermedio. La solución pasa por extraer la cookie de sesión de

la cabecera de la primera respuesta del servidor de InterS@S y enviarla al widget como un

dato más dentro de la estructura de datos JSON. Este dato deberá ser reenviado por el

widget en cada una de las peticiones al servidor intermedio de tal modo que con dicho dato

pueda especificarse la opción CURLOPT_COOKIE de la petición de libcurl al servidor de

InterS@S.

curl_setopt($curl3, CURLOPT_COOKIE, $cookies_sesion);

La variable $cookies_sesion contendrá las cookies obtenidas tras el análisis de la cabecera

HTTP de la petición libcurl anterior o bien es uno de los parámetros pasados por POST desde

el widget.

Una cookie, también llamada HTTP cookie, web cookie, o browser cookie, sirve para el envío

de información de estado desde un servidor al navegador del usuario y para devolver la

información de estado al servidor original. La información de estado puede ser usada para

autenticación, identificación de una sesión de usuario, preferencias de usuario, contenidos

de un carrito de la compra y, en definitiva, para cualquier cosa que pueda lograrse a través

del almacenamiento de datos en formato texto en la computadora del usuario. Las cookies

forman parte de la cabecera HTTP enviadas al cliente por el servidor web precediendo al

contenido de la página solicitada.

Las cookies dentro de la cabecera HTTP van precedidas de la directiva Set-Cookie. A

continuación aparece el name=value de la cookie que el servidor quiere que almacene el

navegador. Tras un ";" pueden aparecer una serie de atributos de la cookie.

Según se ha explicado, la cabecera HTTP enviada por un servidor cualquiera tiene una

estructura similar a la del siguiente ejemplo:

HTTP/1.1 200 OK

Set-Cookie: name=value

Set-Cookie: name2=value2; Expires=Wed, 09 Jun 2021 10:18:14 GMT

Content-type: text/html; charset=UTF-8

(contenido de la página)

Lo que interesa para poder establecer la opción CURLOPT_COOKIE de la siguiente petición

CURL que se realice, y mantener de esta forma la sesión, son los pares name=value de dicha

cabecera.

Page 28: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

67

El código empleado en el archivo servidorintermedio5.php de la web API del Widget

de Cita Médica para obtener las cookies de sesión a partir de la respuesta en formato texto

del servidor es el que se cita a continuación. En él se analiza $codigofuente2 (respuesta

del servidor de InterS@S tras ejecutar las sesión $curl2) para quedarnos con las cookies

que aparecen en su cabecera HTTP.

$start = strpos($codigofuente2, "Set-Cookie");

$end = strpos($codigofuente2, "Content-Type");

$parts = split("Set-Cookie: ",substr($codigofuente2, $start, $end-$start));

$cookies = array();

foreach ($parts as $co){

$cd = split(";",$co);

if (!empty($cd[0]))

/*Se añade un elemento a la matriz de tamaño indeterminado $cookies[]*/

$cookies[] = $cd[0];

}

$cookies_sesion=implode(";",$cookies);

Finalmente, como ya se ha comentado, una vez que tenemos $cookies_sesion sólo resta

especificar la opción CURLOPT_COOKIE en la siguiente sesión CURL ($curl3):

curl_setopt($curl3, CURLOPT_COOKIE, $cookies_sesion);

Cabe señalar que libcurl tiene una opción que permite guardar de forma automática las

cookies enviadas en respuesta a una petición en un archivo del servidor creado a tal efecto,

pero no se logró que esta técnica funcionase en el hosting en el que se encuentra la web API

del Widget de Cita Médica, por lo que se decidió implementar esta solución alternativa.

6.3.3 Parseando la respuesta: HTML DOM parser

De todo el documento HTML que obtenemos al ejecutar una sesión CURL en el servidor

intermedio, sólo nos van a interesar ciertos datos, que serán los que finalmente se

codificarán en formato JSON para ser consumidos por el Widget de Cita Médica.

Existen varias formas de realizar este análisis de la respuesta del servidor de InterS@S. Se

pueden usar expresiones regulares combinadas con funciones PHP de manipulación de

cadenas (como explode, implode, y otras) pero todas estas soluciones requieren mucho

código para obtener un simple dato y son susceptibles de presentar problemas con

determinados carácteres. Para evitar estos inconvenientes, en el Widget de Cita Médica se

optó por recurrir a una librería PHP que permite manipular HTML de manera muy sencilla y

que resultó ser la solución ideal. El nombre de la librería en cuestión es Simple HTML Dom

parser, y con ella se pueden buscar etiquetas usando selectores como en jQuery y extraer su

contenido.

Page 29: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

68

Simple HTML DOM parser está escrita en PHP 5+ y para usarla simplemente hay que

descargarla de <http://sourceforge.net/projects/simplehtmldom/files/> y después incluirla

en nuestro desarrollo, tal como se hace con cualquier otra librería. No está limitada

únicamente a HTML válido, en el sentido de que puede trabajar con código HTML que no ha

pasado la validación W3C, y con ella es posible encontrar elementos por ids, classes,

etiquetas, etc.

Una vez incluida, ya es posible crear un nuevo objeto DOM:

$html = new simple_html_dom();

Tras esto, ya es posible cargar HTML en dicho objeto, ya sea desde una URL, desde una

cadena, o desde un archivo. En el caso que nos ocupa la carga se ha realizado siempre desde

la cadena de texto obtenida al ejecutar una sesión CURL.

$html->load($codigofuente);

A continuación podemos buscar el contenido o las propiedades de las etiquetas que nos

interesen. El siguiente es un ejemplo extraído de servidorintemedio5.php:

$matrizdatos['enlace_cancelar']=$html->find('a[class=enlace_cancelar_cita]',0)-

>href;

Como puede apreciarse es posible encontrar elementos utilizando selectores y elegir el

atributo de ese elemento que nos interesa.

Además de los atributos normales, como podría ser el caso del atributo href de un ancla,

existen cuatro atributos especiales que serían:

tag: devuelve el nombre de la etiqueta.

innertext: devuelve el contenido HTML de un elemento.

outertext: devuelve el HTML exterior a un elemento.

plaintext: devuelve texto plano (sin etiquetas HTML).

En servidorintermedio5.php se utiliza el atributo especial innertext. A continuación se

cita un ejemplo de este uso:

$matrizdatos['fecha_solicitud']=utf8_encode($html-

>find('span[class=letra_pequena_fecha]',0)->innertext);

Simple HTML DOM parser es una poderosa herramienta. No obstante, a la hora de usarla,

hay que tener cuidado con cierto derroche de memoria que puede producir en el servidor y

Page 30: 6. Desarrollo del Widget de Cita Médicabibing.us.es/proyectos/abreproy/12030/fichero/Memoria%2FCapítulo… · En el desarrollo del Widget de Cita Médica se han utilizado distintos

DESARROLLO DE UN WIDGET DE YAHOO! PARA TV PARA LA SOLICITUD DE CITA MÉDICA Proyecto Fin de Carrera María León Bujes

69

que puede ocasionar que se ralentice, o incluso que deje de funcionar unos minutos, si se

cargan demasiados objetos DOM simultáneamente. Es por ello que al escribir el código de la

web API alojada en el servidor intermedio se ha tenido especial cuidado en eliminar cada

objeto DOM antes de cargar uno nuevo, haciendo uso del método clear():

$html->clear();

6.3.4 Producción de una cadena JSON: json_enconde()

En el apartado 6.3 se explicaron las bondades del formato JSON a la hora de ser consumido

por una aplicación escrita en JavaScript.

Para crear una cadena que exprese un objeto u otro tipo de variable en formato JSON, en

PHP se dispone de una función nativa llamada json_encode(), que recibe lo que

deseamos convertir en notación JSON y devuelve una cadena de texto con el JSON

producido.

La función json_encode() permite convertir a JSON cualquier cosa que necesitemos, ya

sea una cadena, una variable numérica, un array -normal o asociativo- u objetos con todo

tipo de datos dentro.

Centrándonos en el caso de la web API alojada en el servidor intermedio a la que realiza

peticiones el Widget de Cita Médica, la producción de la cadena JSON se realiza a partir de

los datos obtenidos de InterS@S y es implementada de la siguiente forma:

1. Los datos que nos interesan (extraidos de la respuesta del servidor de InterS@S con

ayuda de la librería Simple HTML DOM parser) son almacenados en un array

asociativo.

2. El array asociativo resultante se pasa como parámetro a la función json_encode().

3. Se ejecuta print($json) donde $json es el valor devuelto por la función

json_encode($array) en el paso anterior.

Hay ejemplos del proceso anterior en prácticamente todos los archivos de la web API

alojada en el servidor intermedio (ver Anexo II).