Sistema de gestión de horarios basado en Google Calendar
Transcripción
Sistema de gestión de horarios basado en Google Calendar
Memoria del proyecto Sistema de gestión de horarios basado en Google Calendar Javier Garcés Niño Proyectos Informáticos de Sistemas Ingeniería Técnica en Informática de Sistemas Universidad Jaume I 7 de septiembre de 2012 Proyecto dirigido por: Raul Montoliu Colás Agradecimientos La presente memoria del proyecto ha sido redactada con una plantilla de LATEX creada por Sergio Barrachina Mir bajo la licencia Creative Commons AttributionNonCommercial-ShareAlike (CC BY-NC-SA): Resumen Este proyecto consiste en crear un sistema de reservas para organizar mejor el horario de tutorías de los profesores de manera que puedan conocer los alumnos que van a atender para, de esta forma, poder preparar con antelación las consultas en caso de necesitarlo. El objetivo principal del proyecto es que los alumnos puedan acceder a una página web para comprobar si existe alguna visita programada con el profesor. Los alumnos que accedan podrán reservar tiempo del horario de tutorías del profesor para realizar consultas, y en caso de acceder el propio profesor éste podrá hacer una administración básica de su calendario. Para ello se utilizará las API que ofrece Google para trabajar con su servicio llamado Calendar. Índice general Índice general I Índice de figuras 1 Introducción 1.1. Motivación del proyecto 1.2. Objetivos del proyecto . 1.3. Entorno y estado inicial 1.4. Herramientas . . . . . . III . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 2 3 3 2 Planificación y evaluación de recursos 2.1. Planificación . . . . . . . . . . . . . . 2.2. Identificación de tareas . . . . . . . . . 2.3. Seguimiento del proyecto . . . . . . . . 2.4. Evaluación de recursos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 9 10 14 19 3 Análisis y diseño del sistema 3.1. Análisis de requisitos . . . . 3.2. Diseño del sistema . . . . . 3.2.1. Diseño del sistema . 3.3. Funciones y tipos de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 21 23 23 27 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Desarrollo y resultados del proyecto 29 4.1. Desarrollo del proyecto . . . . . . . . . . . . . . . . . . . . . . . 29 4.2. Resultados del proyecto . . . . . . . . . . . . . . . . . . . . . . 33 5 Conclusiones y trabajo futuro 35 5.1. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 5.2. Trabajo futuro . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 Bibliografía 39 A Apéndices 41 A.1. Apéndice I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 A.2. Apéndice II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 i ii Índice general A.3. Apéndice III . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.4. Apéndice IV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 62 Índice de figuras 1.1. Interfaz de control de XAMPP . . . . . . . . . . . . . . . . . . . . 1.2. Ventana del editor avanzado de texto Kate . . . . . . . . . . . . . 1.3. Ventana de HttpRequester, el complemento de Mozilla Firefox que permite preparar y enviar peticiones HTTP . . . . . . . . . . . . . 1.4. Ventana del entorno de desarrollo en LATEX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 6 7 8 2.1. 2.2. 2.3. 2.4. 2.5. 2.6. 2.7. Lista de tareas planteadas para el desarrollo del proyecto Duración ideal de las tareas . . . . . . . . . . . . . . . . . Diagrama de Gantt ideal (realizado con MS Project 2010) Duración real de las tareas . . . . . . . . . . . . . . . . . Diagrama de Gantt real (realizado con MS Project 2010) Ejemplo de límites inpuestos por Google en sus servicios . Página para solicitar un incremento de la cuota . . . . . . . . . . . . . 10 14 15 17 18 19 19 3.1. 3.2. 3.3. 3.4. 3.5. Reservando tiempo de la tutoría del profesor . . . . . . . . . . . . Reserva efectuada, posibilidad del alumno de cancelar la reserva . Diagrama de flujo de la carga de la página del sistema de reservas Página de reservas desde el punto de vista de un alumno . . . . . . Diagrama simplificado de casos de uso . . . . . . . . . . . . . . . . 22 22 24 25 26 4.1. Para trabajar con información privada de una cuenta de Google el sistema de seguridad empleado pide autorización . . . . . . . . . . 4.2. Diagrama de flujo para cuando se da de alta el profesor en el sistema de reservas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3. Diagrama de flujo que muestra cómo opera el sistema de reservas internamente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.1. A.2. A.3. A.4. A.5. Google APIs Console: Creación de un nuevo proyecto . . . . . . . Google APIs Console: Página principal . . . . . . . . . . . . . . . . Google APIs Console: Lista de servicios de Google . . . . . . . . . Google APIs Console: Estado actual de la API de Google Calendar Google APIs Console: Términos de uso para permitir el uso de las API, 1º parte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii 30 31 32 41 42 42 43 44 iv Índice de figuras A.6. Google APIs Console: Términos de uso para permitir el uso de las API, 2º parte y última . . . . . . . . . . . . . . . . . . . . . . . . . A.7. Google APIs Console: Activación éxitosa de la API de Google Calendar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.8. Google APIs Console: Vista de la pestaña API Access (Acceso a la API) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.9. Google APIs Console: Configuración del nuevo proyecto . . . . . . A.10.Google APIs Console: Configuración de un Client ID para aplicaciones web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.11.Google APIs Console: Vista de la pestaña API Access (Acceso a la API) después de crear el primer ID de los 2 necesarios . . . . . . . A.12.Google APIs Console: Vista de la pestaña API Access (Acceso a la API) después de crear el primer ID de los 2 necesarios . . . . . . . A.13.Google APIs Console: Vista de la pestaña API Access (Acceso a la API) después de crear los 2 ID necesarios para el funcionamiento del sistema de reservas . . . . . . . . . . . . . . . . . . . . . . . . . A.14.Etapa inicial del proceso de alta del calendario del profesor en el sistema de reservas . . . . . . . . . . . . . . . . . . . . . . . . . . . A.15.Etapa final del proceso de alta del calendario del profesor en el sistema de reservas . . . . . . . . . . . . . . . . . . . . . . . . . . . A.16.Tutorial para crear reservas: Página inicial de pruebas del sistema de reservas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.17.Tutorial para crear reservas: Pantalla de autenticación de Google . A.18.Tutorial para crear reservas: Aviso para obligar la autenticación desde el servicio de la UJI . . . . . . . . . . . . . . . . . . . . . . . A.19.Tutorial para crear reservas: Autenticación de la cuenta de usuario desde la UJI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.20.Tutorial para crear reservas: Autorización de Google para el uso de datos privados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.21.Tutorial para crear reservas: Página de prueba de reservas desde una cuenta de usuario autenticada . . . . . . . . . . . . . . . . . . A.22.Tutorial para crear reservas: Mostrando reservas de la semana seleccionada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.23.Tutorial para crear reservas: Vista gráfica del calendario de tutorías. En rojo aparecen las reservas existentes . . . . . . . . . . . . A.24.Tutorial para crear reservas: Preparando la reserva . . . . . . . . . A.25.Tutorial para crear reservas: Mostrando reservas de la semana seleccionada tras la creación de una nueva reserva . . . . . . . . . . . A.26.Tutorial para crear reservas: Vista gráfica del calendario de tutorías después de reservar tiempo de tutorías del profesor . . . . . . . . . A.27.Tutorial para crear reservas: Mostrando reservas de la semana seleccionada (tras eliminación de reserva) . . . . . . . . . . . . . . . 44 45 45 46 46 47 48 49 50 51 52 53 53 54 55 56 57 58 59 60 60 61 Capítulo 1 Introducción Índice 1.1. 1.2. 1.3. 1.4. Motivación del proyecto Objetivos del proyecto . Entorno y estado inicial Herramientas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 3 3 Este proyecto se enfoca en la creación de una herramienta que permite organizar de una manera más eficiente el tiempo de los profesores para atender consultas de los alumnos y a su vez evitar que el alumno espere largos períodos de tiempo en caso de haber más alumnos esperando ser atendidos por ese profesor. Google Calendar, un calendario electrónico desarrollado por Google, tiene implementado en el cliente un sistema de citas que podría haber sido utilizado para el proyecto, desafortunadamente el sistema de Google es muy limitado debido principalmente a la ausencia de este sistema de citas en la API (Application Programming Interface) de Google, impidiendo la implementación y mejora del servicio por parte de programadores ajenos a Google. Es por ello necesario utilizar la API existente para replicar dicho sistema y adaptarlo a las necesidades del proyecto. 1.1. Motivación del proyecto Este proyecto se inspira en la migración de servicios que ha realizado la Universidad Jaume I hacia los servicios de Google. 1 2 Introducción Antes de la formalizar la propuesta del proyecto estaba planeado el diseño el sistema de reservas con herramientas más comunes como una base de datos para almacenamiento de todos los datos. Sin embargo, tras descubrir las posibilidades que el servicio de Google Calendar puede ofrecer al proyecto, la novedad que supone adoptar dicha tecnología para el proyectando y la posible experiencia adquirida para el futuro laboral se decidió aprendizaje del manejo de las API del calendario de Google. Se sabía que delegar partes del proyecto al servicio de calendarios de Google podría traer ventajas significativas, como la liberarización de recursos en el servidor local, donde se localiza el sistema de reservas. Además, si por cualquier incidencia el sistema de reservas se queda inoperativo los calendarios podrían seguir siendo accesibles por otros servicios que requieran esos datos. 1.2. Objetivos del proyecto Los objetivos que se desea cumplir durante la realización del proyecto son: Aprendizaje en el servicio de Google Calendar y la API asociada al servicio. Creación de una web que permita consultar horarios de tutorías de algún profesor. Diseño y creación de la infraestructura necesaria que permita realizar reservas de tiempo del horario de tutorías del profesor para hacerle consultas. Administración básica del profesor con su horario de tutorías. Durante el proceso de consulta de horarios de tutorías del profesor debe mostrarse al usuario que acceda a la página web del sistema de reservas un calendario donde aparezca las horas que han sido asignadas al profesor para atender consultas a los alumnos. Toda reserva que se haya realizado también aparecerá, permitiendo conocer al alumno las horas de tutoría libres que tiene el profesor. Después de la autenticación del alumno en el sistema de reservas este obtendrán nuevos controles en la página web. Estos controles podrán ser utilizados para formalizar reservas en el caso que se quiera realizar una visita al profesor en su horario de tutorías o cancelar sus propias reservas. Además, si el usuario autenticado es el propietario del calendario podrá administrar su horario de tutorías como es la cancelación de reservas de alumnos o reserva de su propio horario para inhabilitar las tutorías de un día en 1.3. Entorno y estado inicial concreto. Aunque inicialmente el sistema no ha sido desarrollado para ser parte de otros proyectos de mayor envergadura se ha seleccionado un diseño austero de la página web del sistema de reservas, posibilitando la reutilización o adaptación del sistema a otros proyectos. 1.3. Entorno y estado inicial Tras un largo período de tiempo de análisis, comprensión y aprendizaje del funcionamiento de las API de Google Calendar y del protocolo de seguridad OAuth 2.0 que soporta Google se ha procedido al planteamiento y posterior desarrollo del sistema de reservas. Inmediatamente después se preparó las herramientas necesarias para realizar varios códigos de ejemplo y analizar los resultados obtenidos por los ejemplos. En cuanto al planteamiento de trabajo con el servicio de Google inicialmente se planteó un nivel de dificultad de manejo similar al de una base de datos, con una página que administrase operaciones que los usuarios realizaran y una base de datos almacenase o retornara información. Pero debido a dificultades técnicas no previstas con Google Calendar, como la ausencia en la API de varias funciones existentes en la interfaz web de Google Calendar de gran valor para el proyecto o la forma de funcionamiento del protocolo de seguridad OAuth 2.0 empleado durante las pruebas ha sido necesario replantear la estructura y funcionamiento que tendría que tener el proyecto para que fuera posible su realización sin desviarse significativamente de la idea original. Se puede observar que no se puede implementar el proyecto con las facilidades que se había previsto inicialmente en el proyecto final, creando un retraso en el desarrollo pero el resultado se puede considerar que es similar al inicialmente propuesto. Faltaría explicar los recursos humanos requeridos para la elaboración del sistema de reservas pero en éste proyecto no es necesario realizar un estudio debido a que ya se conoce de antemano que se realizará por una única persona y que no variará durante todo el desarrollo. En caso de incumplir los plazos previstos en determinadas tareas esto significará que no será posible arreglar estos desfases introduciendo nuevos miembros al desarrollo de este proyecto. 1.4. Herramientas El proyecto se ha desarrollado y probado en 2 equipos, uno con el sistema operativo Windows 7 profesional y otro con Kubuntu 12.04 LTS (Long Term 3 4 Introducción Support), ambos de 64 bits. En cuanto a la implementación del proyecto, se ha utilizado el editor de texto avanzado de KDE, Kate, para la escritura del código fuente y la Google API PHP client library, la biblioteca oficial de Google para acceder a datos protegidos mediante la API de Google. Otras herramientas utilizadas en el proyecto han sido las siguientes: XAMPP Apache PHP Navegadores web (Mozilla Firefox v14.0.1, Google Chrome v21, Internet Explorer v9, Konqueror v4.8) HttpRequester Texmaker 1.4. Herramientas 5 XAMPP Es un servidor independiente de plataforma y de software libre, que contiene la base de datos MySQL, el servidor web Apache y los intérpretes para lenguajes de script PHP y Perl. Actúa como un servidor web libre, fácil de usar y capaz de interpretar páginas dinámicas. Se actualiza frecuentemente para incorporar las últimas versiones de Apache/MySQL/PHP y Perl [18] (ver Figura 1.1). Página web oficial: http://www.apachefriends.org/es/xampp.html Figura 1.1: Interfaz de control de XAMPP Apache Servidor Web de código abierto muy popular. Soporta gran cantidad de características, muchas de ellas implementadas como módulos compilades para darle nuevas funcionalidades al núcleo del servidor. Página web oficial: http://www.apache.org/ PHP PHP es un lenguaje de programación interpretado, diseñado originalmente para la creación de páginas web dinámicas pero actualmente puede ser utilizado desde una interfaz de línea de comandos o en la creación de otros tipos de programas incluyendo aplicaciones con interfaz gráfica [13]. Página web oficial: http://www.php.net 6 Introducción Kate Kate es un editor avanzado de texto para el entorno de escritorio KDE. Se ha utilizado esta herramienta por ser la que mejor conoce el proyectando para trabajar con código PHP (ver Figura 1.2). Página web oficial: http://kate-editor.org Figura 1.2: Ventana del editor avanzado de texto Kate 1.4. Herramientas HttpRequester Un complemento para Mozilla Firefox diseñado para realizar peticiones HTTP, observar las respuestas y mantener un historial de transacciones (ver Figura 1.3). Página web oficial: https://addons.mozilla.org/es/firefox/addon/httprequester Figura 1.3: Ventana de HttpRequester, el complemento de Mozilla Firefox que permite preparar y enviar peticiones HTTP 7 8 Introducción Textmaker Es un entorno de desarrollo de LATEX multiplataforma y de código abierto de fácil uso (ver Figura 1.4). Se ha empleado para la redacción de la memoria del proyecto. Página web oficial: http://www.xm1math.net/texmaker Figura 1.4: Ventana del entorno de desarrollo en LATEX Capítulo 2 Planificación y evaluación de recursos Índice 2.1. 2.2. 2.3. 2.4. 2.1. Planificación . . . . . . . Identificación de tareas . Seguimiento del proyecto Evaluación de recursos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 . 10 . 14 . 19 Planificación Con el objetivo de llevar un seguimiento de la duración del proyecto, se ha realizado la identificación de cada una de las tareas y subtareas, estableciendo un orden en el que se van a realizar. A cada tarea se le ha estimado un tiempo que debería ser suficiente para su completo desarrollo. Es necesario constatar que en la elaboración de las tareas no se han contabilizado fines de semana, y no hubo ningún tipo de progreso en el desarrollo del proyecto los días 28 de diciembre al 8 de enero por corresponder con la festividad de la navidad. 9 10 Planificación y evaluación de recursos 2.2. Identificación de tareas Durante el desarrollo del trabajo se estableció una lista de tareas que reflejarían con gran precisión las etapas de la elavoración del proyecto (ver Figura 2.1). Figura 2.1: Lista de tareas planteadas para el desarrollo del proyecto 2.2. Identificación de tareas A continuación se explica el objetivo de cada una de las tareas, por orden de aparición en la figura 2.1: Consultar al tutor: Se necesitaba la búsqueda de un tutor que quisiera aceptar un proyecto planteado por el alumno. Búsqueda de información y estudio del proyecto: Tras una serie de conversaciones con el tutor se acordó buscar información y estudiar la viabilidad del uso del servicio de Google Calendar en el sistema de reservas planteado en esta fase del desarrollo. Definición más detallada de objetivos: Se establecieron los objetivos definitivos con los que se pediría al tutor que aceptase el proyecto propuesto. Aprobación del inicio: En este punto se espera que el tutor acepte el proyecto propuesto y realice de alta el proyecto en la base de datos de la asignatura de Proyectos Informáticos de Sistemas. Instalación de herramientas a utilizar: Obtención, instalación y configuración de los elementos necesarios para desarrollar el proyecto. Familiarización con el entorno de trabajo: Tras la instalación y configuración de las herramientas se procede al aprendizaje del entorno de desarrollo con el que se va a realizar el proyecto. Esto es debido a la necesidad de descubrir si hacía falta más conocimientos para un correcto manejo de algunas de las herramientas que se iba a emplear. Separación de objetivos en varias etapas de desarrollo: Para abordar con menor complejidad los objetivos a cumplir durante el desarrollo del proyecto se procede a dividir las tareas en otras más simples con objetivos más simples. De esta forma se puede conocer con mayor facilidad el objetivo de cada una de las tareas. Planificación inicial: Se estable un orden inicial de las tareas a realizar durante la duración del proyecto y un tiempo acorde a la complejidad y magnitud de la tarea a realizar. Análisis del protocolo de seguridad OAuth 2.0 : Esta fase era importante para entender el funcionamiento del la seguridad que se iba a emplear en el proyecto. Tras la lectura de la documentación proporcionada por Google se realizaron una serie de pruebas para estudiar el comportamiento de esta tecnología. En este punto se pudo comprobar si habrían problemas de difícil solución que pudieran retrasar el desarrollo del proyecto más de lo inicialmente planificado. Análisis de la API de Google Calendar: Se procede a estudiar toda la documentación proporcionada por Google referente a la última versión 11 12 Planificación y evaluación de recursos disponible de la API de Google Calendar. Debido a la gran cantidad de documentación existente esta tarea tiene un mayor coste temporal que todas las tareas realizadas con anterioridad. Finalmente sólo se va ha utilizar un reducido subconjunto de acciones para el sistema de reservas planteado en el proyecto. Diseño del sistema de reservas: Como resultado de los análisis realizados hasta la fecha se procede a diseñar la interfaz que podría llegar a tener el sistema de reservas en cada uno de los escenarios establecidos por los objetivos del proyecto y la estructura interna del sistema de reservas. Programación: Una vez quede clara la estructura interna del sistema de reservas se procederá a implementar las llamadas a la API de Google Calendar para que pueda realizar consultas o cambios en el calendario del profesor solicitado. A la vez que se desarrolla nuevas funciones se irán incorporando a la interfaz web del sistema de reservas. Búsqueda y solución de errores: Se realizarán diversas pruebas para encontrar pequeños errores que no se hubieran previsto durante la implementación del código y se forzará escenarios problemáticos para comprobar que el sistema podría ser capaz de resolver los problemas, en caso negativo se procedería a corregir esos casos. Pruebas finales: Al finalizar la implementación del sistema de reservas se procede a comprobar todas y cada una de las características que ofrece el sistema para descartar posibles regresiones introducidas tras la corrección de errores. Organizar documentación: Se guardará cualquier tipo de información e imágenes de interés para poder realizar la memoria del proyecto. Redactar apartados de la memoria: Con la idea de documentar todo el proyecto, tanto la planificación de las tareas como el código empleado por el sistema de reservas se elabora la presente memoria. Revisión del tutor: Análisis de la memoria por parte del tutor encargado en este proyecto. Revisar memoria y otros trabajos: Tras la conversación realizada con el tutor sobre la revisión de la memoria se procede a realizar aquellos cambios que se consideren necesarios para mejorar la calidad de la memoria del proyecto. Entrega de la memoria: Se procederá al envío definitivo de la memoria del proyecto. 2.2. Identificación de tareas Exposición oral: Para finalizar el proyecto se elaborará una serie de diapositivas en las que se deberá describir el proyecto desarrollado y posterior exposición ante un jurado. Debido a que se trata de una actividad que ha de ocurrir posteriormente a la redacción y entrega de la memoria ha sido incluida en la lista de tareas para que se tenga constancia de su existencia. 13 14 Planificación y evaluación de recursos 2.3. Seguimiento del proyecto El proyecto se ha ido planificando idealmente para tener una duración de 136 días (ver Figura 2.2). Si las horas de trabajo dedicado en el proyecto fuera de 2 horas y 15 minutos diarias las horas necesarias para finalizar el proyecto requerirían unas 306 horas en total, siempre que se hablase de tiempo ideal. En la Figura 2.3 se muestra el diagrama de Gantt correspondiente a la duración ideal del proyecto. Figura 2.2: Duración ideal de las tareas 2.3. Seguimiento del proyecto Figura 2.3: Diagrama de Gantt ideal (realizado con MS Project 2010) 15 16 Planificación y evaluación de recursos En realidad la duración real ha terminado extendiéndose hasta los 223 días (ver Figura 2.4) debido a varios errores de la documentación de Google que han impedido finalizar la fase de programación en el tiempo establecido y a un período de inactividad por razones laborales de 3 meses aproximadamente, desde el 15 de marzo hasta el 24 de junio. Además, en la elaboración de la memoria se ha alargado su duración de los 18 días ideales a 26 días por causas ajenas al desarrollo del proyecto. En total, si se aplicase las 2 horas y 15 minutos de desarrollo al día utilizado en la planificación ideal del proyecto la cantidad de horas se dispara hasta las 501 horas en total. Debido a que el cálculo ha incluido los meses de inactividad, el cálculo se ha de realizar con los 136 días más los días de retraso ocurridos durante la fase de programación (28 días adicionales) y la de elaboración de la memoria (8 días adicionales): en total son 172 días de desarrollo real que multiplicado por las 2 horas y 15 minutos de trabajo en el proyecto deja un total de 387 horas de trabajo dedicado al proyecto, 81 horas con respecto a la planificación ideal del proyecto. En la Figura 2.5 se muestra el diagrama de Gantt correspondiente a la duración real del proyecto. 2.3. Seguimiento del proyecto Figura 2.4: Duración real de las tareas 17 18 Planificación y evaluación de recursos Figura 2.5: Diagrama de Gantt real (realizado con MS Project 2010) 2.4. Evaluación de recursos 2.4. Evaluación de recursos La mayoría de los servicios de Google son gratuitos mientras no se supere una serie de límites (ver Figura 2.6). En estos casos para seguir usando el servicio requiere mejorar el servicio pagando. Actualmente en Google Calendar se proporciona una cuota de 10.000 de peticiones al día, en caso de requerir más cuota hay que realizar una petición en la siguiente dirección: https://developers.google.com/google-apps/calendar/pricing La página de petición de incremento de cuota es similar a la figura 2.7). Figura 2.6: Ejemplo de límites inpuestos por Google en sus servicios Figura 2.7: Página para solicitar un incremento de la cuota 19 20 Planificación y evaluación de recursos La selección de herramientas basadas en software libre se debe primordialmente a la fiabilidad y la gratuidad para el desarrollador del proyecto. Además, cualquier equipo informático con acceso a las herramientas mencionadas en la sección 1.4 valdría para la elaboración del proyecto. Sin embargo, si se realizase el mismo proyecto contratando personal cualificado para seguir las mismas fases de desarrollo, sería posible contabilizar el coste necesario para su realización en base a las horas que han sido requeridas para la finalización del proyecto. Para un correcto cálculo se reutilizará los datos obtenidos en la sección 2.3, concretamente, el número de horas reales que han sido requeridas para su correcto desarrollo, 387 horas. Si el nuevo ingeniero técnico en informática cobrase 30 euros la hora obtendríamos el siguiente coste total por las 387 horas: Coste = horas_desarrollo ∗ coste/hora Coste = 387 horas ∗ 30 euros/hora Coste = 11,610 euros También se debe contabilizar el equipo informático necesario para el nuevo puesto de trabajo. Al no requerir gran potencia de cálculo para el desarrollo web y posteriores pruebas del proyecto con un servidor en la propia máquina cualquier equipo informático de bajo presupuesto puede cumplir con las expectativas cuyo rango de precios puede variar entre los 450 euros y los 600 euros. En el precio se incluye torre, monitor y periféricos esenciales en un ordenador de sobremesa como son el teclado y el ratón. Como el proyecto establece que sólo existe una persona desarrollando el proyecto sólo se requerirá una computadora. Por tanto, a los 11.610 euros del proyecto se le sumará un equipo informático de valorado entre 450 y 600 euros. Capítulo 3 Análisis y diseño del sistema Índice 3.1. Análisis de requisitos . . . . . . . . . . . . . . . . . . . . . 21 3.2. Diseño del sistema . . . . . . . . . . . . . . . . . . . . . . . 23 3.3. Funciones y tipos de datos . . . . . . . . . . . . . . . . . . 27 En este capítulo se explican algunos conceptos sobre la API de Google, así como funciones propias desarrolladas para este proyecto. 3.1. Análisis de requisitos Para el desarrollar el sistema de reservas es necesario conocer el tipo de acciones a la que se someterá dicho sistema sin profundizar en los detalles. A partir de ahí se diseña la interfaz con el que la página del sistema de reservas se comunicará con los servicios de Google. Se distinguen 2 grupos de acciones: Consulta de información Modificación del calendario Consulta de información Básicamente son peticiones al servicio de Google para obtener una serie de datos. Los tipos de datos devueltos dependen del tipo de consulta que se 21 22 Análisis y diseño del sistema realice, concretamente en el sistema de reservas trabajará básicamente con calendarios y eventos. Este tipo de acciones normalmente no las solicita directamente el usuario que accede al sistema, es decir, son funciones que forman parte de otras tareas. Es bastante frecuente que se haga una búsqueda de eventos en un calendario utilizando rangos de fechas como método para acotar los resultados que pudiera proporcionar la consulta. Modificación del calendario Son las acciones que más control tienen los usuarios que acceden al sistema de reservas y que dejan cambios permanentes en el calendario del profesor. Los más comunes son las órdenes de creación y cancelación de reservas. Estas acciones se describen a continuación: Creación de reservas: Se redactará una descripción para que el profesor conozca la razón de la reserva y seleccionará el tiempo de inicio y fin de la reserva. El horario de tutorías se divide en franjas de 30 minutos como unidad mínima para realizar una reserva pero se permite que el alumno pueda seleccionar varios huecos de 30 minutos contiguos. (ver Figura 3.1). Cancelación de reservas: Al cancelar un reserva creada por el propio usuario se devolverá las horas de la reserva para que otros usuarios puedan reservar esas horas (ver Figura 3.2) Figura 3.1: Reservando tiempo de la tutoría del profesor Figura 3.2: Reserva efectuada, posibilidad del alumno de cancelar la reserva 3.2. Diseño del sistema 3.2. Diseño del sistema 3.2.1. Diseño del sistema El proyecto consta de 3 partes diferenciadas: Página del sistema de reservas Administración del calendario del profesor Acciones del usuario autenticado Página del sistema de reservas Su diseño va a ser austero pero cumplirá con los objetivos propuestos. Debe contener un botón para autenticarse con una cuenta de Google y el calendario de tutorías del profesor y, en caso de existir, se mostrarán reservas. Tras la autenticación el servicio de Google solicitará permiso para poder trabajar con varios datos del usuario. Tras la autenticación aparecerán nuevos controles para 2 tipos de acciones: creación de reservas y cancelación. 23 24 Análisis y diseño del sistema Acciones del usuario autenticado Figura 3.3: Diagrama de flujo de la carga de la página del sistema de reservas 3.2. Diseño del sistema Cualquier usuario autenticado en el sistema de reservas tendrá a su disposición de una lista que representará los horarios de tutorías de la semana y unos controles para la realización de reservas. Además, Aparecerá un botón para abortar aquellas reservas que el alumno haya realizado (ver Figura 3.3). Por otro lado, el profesor dueño del calendario poseerá la autorización necesaria para abortar las reservas de cualquier usuario (ver Figura 3.4). Figura 3.4: Página de reservas desde el punto de vista de un alumno 25 26 Análisis y diseño del sistema Administración del calendario del profesor Las acciones realizadas por los usuarios serán transmitidas al sistema de reservas como si fueran realizadas por el profesor, con la utilización de este método se evita que se puedan modificar los eventos del calendario por otras personas que no sean el propio profesor (ver Figura 3.5). Figura 3.5: Diagrama simplificado de casos de uso 3.3. Funciones y tipos de datos 3.3. Funciones y tipos de datos A continuación se nombrarán las funciones más relevantes: Función get_oauth2_token: Retorna un string llamado ’Access Token’ que servirá como código de autorización para realizar llamadas a la API usando OAuth 2. Dependiendo los parámetros y de la situación donde se use podrá obtener un ’Refresh Token’ que se almacenará en un fichero para futuros usos del sistema de reservas. Función read_calendarOwner_token: Lectura de un fichero para devolver un string que es el ’refresh_token’ asociado al calendario del tutor. En caso de no poder leer correctamente el fichero retornará una cadena vacía. Función write_calendarOwner_token: Salva a un fichero un string que corresponde al ’refresh_token’ asociado al calendario del tutor. Función send_post_query: Realiza una petición HTTP a Google para insertar datos en Google Calendar. Los datos a insertar dependerán del tipo que se hayan especificado en la URI utilizada. El dato que devuelva la función por defecto será un código HTTP de respuesta, devolviendo el código ’200’ como operación correcta. Además, se podrá especificar que retorne un string que represente un código de identificación (ID) en caso de insertarse un evento u objeto similar. Función insertNewEvent: Creación de un nuevo evento en el calendario de reservas del profesor. El propietario del evento seguirá siendo el profesor pero se incluirán datos del usuario autenticado que ha realizado la acción. Retorna el valor verdadero si se ha insertado con éxito y falso en caso contrario. Función send_delete_query: Realiza petición HTTP a Google para eliminar datos en Google Calendar. Dependiendo del tipo de dato especificado eliminará un evento o un calendario. El dato que devuelve la función por defecto será un código HTTP de respuesta, retornando el código ’204’ como operación correcta. Función deleteEvent: Eliminará el evento seleccionado del calendario que se indique. Retornará verdadero si se ha eliminado el evento y falso en caso contrario. Función deleteCalendar: Eliminará el calendario que se indique. Retorna verdadero si se ha eliminado el evento y falso en caso contrario. 27 28 Análisis y diseño del sistema Función send_put_query: Realiza una petición HTTP a Google para modificar datos en Google Calendar. Los datos a modificar dependerán del tipo que se hayan especificado en la URI utilizada. El dato que devuelve la función por defecto será un código HTTP de respuesta, retornando el código ’200’ como operación correcta. Función send_get_query: Envía una petición HTTP a Google y devuelve una lista de elementos. Estos elementos serán del tipo que se hayan especificado en la URI utilizada Función loadEvents: Dadas las fechas de inicio y de fin se realizará una búsqueda de eventos en el calendario seleccionado. Retorna una lista de eventos. Función insertNewTutoringCalendar: Creación de un nuevo calendario en el que se pueda insertar horarios de tutorías del profesor. Retorna el ID del calendario creado, cadena vacía en caso de error. Función insertNewBookingCalendar: Creación de un nuevo calendario para que se pueda insertar reservas del horario de tutorías del profesor. Retorna el ID del calendario creado, cadena vacía en caso de error. Función fill_calendarOwner_config: Salva en un fichero todos los datos de configuración necesarios para que el sistema de reservas pueda acceder a los calendarios del profesor. Función weekStartDate: Retorna la fecha del primer día de la semana, por defecto el de la semana actual. Función weekEndDate: Retorna la fecha del último día de la semana, por defecto el de la semana actual. Capítulo 4 Desarrollo y resultados del proyecto Índice 4.1. Desarrollo del proyecto . . . . . . . . . . . . . . . . . . . . 29 4.2. Resultados del proyecto . . . . . . . . . . . . . . . . . . . . 33 A continuación se explicarán los resultados obtenidos durante el desarrollo del proyecto. Toda posible desviación con respecto a la planificación inicial se detallarán y se justificarán. Para más información sobre el código fuente revisar el Apéndice IV. Para probar las especificaciones de la API de Google Calendar se debe emepezar desarrollando la parte del sistema de reservas encargada de llamar a las API de Google Calendar, el protocolo de seguridad OAuth y funciones suplementarias conforme hiciera falta. 4.1. Desarrollo del proyecto A continuación se hablará de los siguientes apartados: Implementación del sistema de seguridad OAuth Posibles problemas con OAuth: los permisos de los usuarios Implementación de funciones de consulta Implementación de funciones de manipulación de calendarios Desarrollo de funciones que emplearían los usuarios 29 30 Desarrollo y resultados del proyecto Implementación del sistema de seguridad OAuth En un primer acercamiento al sistema de seguridad OAuth se puede observar que requiere autorización explícita para acceder a información privada del usuario autenticado (ver Figura 4.1). El funcionamiento por tanto difiere ligeramente con el planeado para el nuevo sistema. Figura 4.1: Para trabajar con información privada de una cuenta de Google el sistema de seguridad empleado pide autorización Durante el proceso de autorización se obtiene un código que hay que utilizarse para acceder a los datos privados del usuario almacenados en los servicios de Google llamado ’Access Token’. Este dato sólo puede utilizarse durante un breve período de tiempo, momento en que expirará y no se permitirá más accesos válidos a datos privados en Google hasta se vuelva a obtener un nuevo ’Access Token’. Posibles problemas con OAuth: los permisos de los usuarios En lo que concierne a la manipulación de eventos de calendarios se puede plantear que los alumnos puedan insertar eventos en el calendario del profesor. Este metodología de trabajo requiere el envío de autorizaciones a los alumnos que permitan, tras la aceptación de la invitación, trabajar sobre el calendario. Si se estudia los posibles inconvenientes del sistema los alumnos tendrían privilegios suficientes para insertar eventos en áreas no destinadas para tutorías. Por consiguiente este método se descarta. Para permitir la inserción de eventos en calendarios de terceras personas en Google Calendar se plantea un sistema que trabaje con datos del profesor 4.1. Desarrollo del proyecto de manera similar a la forma de funcionamiento de una base de datos. Los alumnos trabajarían con sus credenciales mientras internamente toda acción realizada sobre el calendario utilizando el sistema de reservas pertenecería al profesor. De esta forma nadie excepto el propio profesor puede manipular su propio calendario. Con el sistema propuesto requiere el uso de un código de autorización del profesor y que siempre se renovase cuando se realizase una acción sobre el calendario del profesor. Para ello se trabaja con un código de autorización especial llamado ’Refesh Token’ muy utilizado en las aplicaciones de escritorio para generar tantos ’Access Token’ como llamadas a los servicios de Google sean necesarios sin pedir autorización explícita debido a que no posee fecha de expiración. Sin embargo, este dato sólo se obtiene durante el proceso de autorización para obtener el primer ’Access Token’ tras una primera autenticación por parte del usuario. Una vez realizado satisfactoriamente el proceso mencionado anteriormente para la creación del ’Refresh Token’ se almacena el código en un lugar seguro (ver Figura 4.2). Desde este momento ya se debe poder utilizar el sistema de reservas con el planteamiento realizado. Figura 4.2: Diagrama de flujo para cuando se da de alta el profesor en el sistema de reservas Para realizar las operaciones de este proceso se implementó la función get_oauth2_token para que retornara un ’Access Token’ que permitiera al usuario autenticado trabajar con el sistema de reservas. Además, si se realiza una llamada a la función get_oauth2_token durante el proceso de dar de alta al profesor en el sistema de reservas ésta llamará a la función wri- 31 32 Desarrollo y resultados del proyecto te_calendarOwner_token para que almacenase el ’Refresh Token’ obtenido. Implementación de funciones de consulta Toda acción solicitada al sistema de reservas trabajaría con los códigos de autorización obtenidos mediante el sistema mencionado anteriormente (ver Figura 4.3). Las acciones tienen como propietario el propio profesor, por lo que las reservas aparecerán exclusivamente en el calendario del profesor y sólo éste podrá alterar los eventos. Figura 4.3: Diagrama de flujo que muestra cómo opera el sistema de reservas internamente Las tareas más simples para realizar en los calendarios son las consultas para que devuelvan una lista de eventos. Se procede a desarrollar la función send_get_query lo más genérica que fuera posible para que partiendo de los datos específicos devuelva una lista de elementos del tipo de datos solicitado, como es el caso de la función loadEvents que pediría las fechas de inicio y fin y el calendario al que se tenía que hacer la consulta. 4.2. Resultados del proyecto Implementación de funciones de manipulación de calendarios Se destaca el retraso producido por un fallo en la documentación de Google Calendar referente a la inserción/modificación de datos. Se utilizó sin éxito una herramienta web de pruebas que proporciona Google llamada OAuth 2.0 Playground y que permite realizar peticiones de tipo REST a los servicios de Google, en este caso a Google Calendar. Se descubrió el problema de la documentación tras detectar una inconsistencia en los parámetros necesarios para realizar correctamente las peticiones REST contra los servicios de Google tras el uso de una extensión de Firefox llamada HttpRequester. Finalmente se implementan funciones de inserción send_post_query, de eliminación send_delete_query y de modificación send_put_query de una forma similar a la previemante declarada fución send_get_query para evitar duplicidad en el código. Desarrollo de funciones que emplearían los usuarios Se implementó funciones relacionadas con tareas que un usuario pudiera realizar para darle un diseño básico pero funcional a la página del sistema de reservas. Por lo que respecta al acceso del usuario al sistema de reservas hay que diferenciar del método empleado para procesar las peticiones de los usuarios en el calendario del profesor con el método de autenticación del usuario en el sistema de reservas. En el caso de los usuarios es suficiente almacenar temporalmente el ’Access Token’ del usuario en una variable de sesión y destruir dicha variable de sesión cuando se realiza una desconexión del servicio de la página web de reservas debido a que el usuario autenticado sólo se utiliza para diferenciar los visitantes desconocidos que accediesen a la web de reservas de los usuarios que tenrían autorización para realizar reservas. Sólo es preciso implementar las funciones auxiliares getAccessToken y getRefreshToken que se utilizarían únicamnete en un par de ocasiones en el código del proyecto para leer en un objeto del tipo de datos JSON (JavaScript Object Notation) el ’Access Token’ o el ’Refresh Token’ que aparecen en el objeto leído. 4.2. Resultados del proyecto Se puede declarar que se ha cumplido los objetivos planteados en la sección 1.2. A continuación se comentará cada objetivo: Aprendizaje en el servicio de Google Calendar y la API asociada al servicio: Se ha probado los diferentes menús de la interfaz web del cliente 33 34 Desarrollo y resultados del proyecto oficial de Google para estudiar los elementos que aparecen para tener una idea de qué tipo de elementos han de utilizarse en el momento de mostrar información al usuario que consultase un calendario de Google. Debido que se ha analizado las limitaciones existentes por la actual implementación de la API se pudo descartar para el sistema de reservas toda API no esencial para la realización del proyecto. Posteriormente se procedía al diseño inicial del sistema con la API relevante. Creación de una web que permita consultar horarios de tutorías de algún profesor: Se a realizado una interfaz web simple para acceder al servicio y realizar una demostración completamente funcional del sistema con las posibles operaciones implementadas en el sistema de reservas implementado. Diseño y creación de la infraestructura necesaria que permita realizar reservas de tiempo del horario de tutorías del profesor para hacerle consultas: Como se ha comentado anteriormente se ha diseñado la estructura del sistema de reservas para que cualquier usuario autenticado pueda realizar operaciones sobre las horas de tutorías del calendario del profesor pero teniendo como dueño de estas operaciones el propio profesor para evitar manipulación del calendario no deseada en el sistema de reservas. Finalmente, se ha realizado la implementación de funciones que trabajen contra el servicio de Google Calendar, tanto para realización de consultas como operaciones de inserción, modificación y eliminación de elementos de los calendarios. Para facilitar estas tareas se ha desarrollado funciones adicionales que facilitan las acciones realizadas en el sistema de reservas. Administración básica del profesor con su horario de tutorías: Con el sistema de reservas previamente implementado sólo era necesario averiguar si el tutor es el usuario autenticado en el sistema y en caso afirmativo habilitar controles que no aparecen en caso de autenticarse alumnos, como puede ser la opción de cancelar cada una de las reservas de todos los alumnos. Capítulo 5 Conclusiones y trabajo futuro Índice 5.1. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . 35 5.2. Trabajo futuro . . . . . . . . . . . . . . . . . . . . . . . . . 36 En este capítulo se mostrarán las conclusiones obtenidas una vez finalizado el proyecto, así como sus futuras ampliaciones. 5.1. Conclusiones Tras la conclusión del proyecto, podemos afirmar que se ha logrado cumplir con los objetivos propuestos al inicio de este. Se ha conseguido diseñar e implementar un sistema de reservas basándose en Google Calendar. El servicio de Google Calendar tiene potencial para utilizarse en multitud de proyectos de toda índole pero es posible que los esfuerzos de Google se enfoquen a la red social Google+, retrasando funciones muy útiles que deberían haber aparecido hace años en la API como el sistema de reservas implementado en la interfaz web de Google Calendar o mayor personalización en los datos que deberían mostrarse al público cuando se accede al calendario oficial. En cuanto a la planificación de tareas hay que comentar que era adecuada, aunque desafortunadamente un error en la documentación de Google produjera un gran retraso en fase de desarrollo del sistema de reservas. 35 36 Conclusiones y trabajo futuro A nivel personal, el diseño de este tipo de sistemas sin recurrir a los elementos tradicionales como una base de datos supone una experiencia gratificante, además de saber que ha contribuido al aprendizaje, al no ser material de estudio que ya se haya estudiado en otras asignaturas de la carrera. De normal en la carrera te enfrentan a problemas relativamente sencillos y diseñados para ser desarrollados durante un breve período de tiempo, todo lo contrario a lo desarrollado durante este proyecto. 5.2. Trabajo futuro Hay que comentar que se pensó es más características o mejoras que podía haberse incluido en el sistema de reservas pero como estamos ante un proyecto que no podía extenderse indefinidamente en el tiempo. Algunos ejemplos podrían ser los siguientes: Creación de una lista blanca de usuarios: Muchas veces es conveniente que sólo pueda acceder a este tipo de herramientas un grupo de personas, por ejemplo alumnos y/o profesores. Actualmente el proyecto sólo requiere que la cuenta sea compatible con la autenticación de Google, como puede ser las cuentas de los alumnos de la Universidad Jaume I. Creación de tutorías fuera del horario ordinal: Hay veces que se han dado casos en que el profesor de una asignatura ha cambiado el horario de tutorías de un día por cualquier razón que fuera. El sistema de reservas actualmente no puede realizar ese tipo de casos y le proporcionaría mayor libertad al profesor para manipular su horario de tutorías. Anulación temporal de tutorías: A diferencia del punto anterior a veces los profesores tienen que realizar viajes para asistir a conferencias o les es imposible asistir a trabajar por diversas razones pero que se sabe que van a volver al entorno laboral en un breve espacio de tiempo, ante estos casos el sistema de reservas no puede conocer estos problemas y podría seguir reservándose tutorías a pesar que no podrían ser atendidas. Se quería dar la posibilidad al profesor o persona competente de anular u ocupar de alguna manera el horario de reservas en las fechas afectadas para evitar que nadie pueda crear nuevas reservas. Mejorar la implementación actual: Como no se tenía experiencia en la forma de trabajar con la API proporcionada por Google si se volviera a implementar el código del sistema de reservas se enfocaría a la creación de clases. Se podría adaptar el código actual pero se requiere más tiempo del planeado para el proyecto, razón por la cual se deja como propuesta. Seleccionar un profesor de una lista: A pesar que tiene lógica que exista más de un profesor que pueda usar el sistema de reservas no entraba 5.2. Trabajo futuro en los objetivos iniciales por lo que no se incluyó dicha característica en la página de reservas. Actualmente toda la información del profesor que se utiliza en el sistema de reservas se almacena en un fichero por lo que cambiar de fichero de información dependiendo del profesor seleccionado por el alumno debería ser sencillo de implementar. 37 Bibliografía [1] API Reference organized by resource type, https://developers.google.com/google-apps/calendar/v3/ reference/ [2] Google API Offline Access Using OAuth 2.0 Refresh Token, http://www.jensbits.com/2012/01/09/google-api-offline-accessusing-oauth-2-0-refresh-token/ [3] Google APIs Console Help, https://developers.google.com/console/help/ [4] Google Apps Calendar API Concepts and Use Cases, https://developers.google.com/google-apps/calendar/concepts [5] Google Calendar API, https://developers.google.com/google-apps/calendar/ [6] Google Calendar API Forum, https://groups.google.com/forum/embed/?place=forum/ google-calendar-api#!forum/google-calendar-api [7] Google Developers, https://developers.google.com/ [8] HTML, http://en.wikipedia.org/wiki/HTML [9] Hypertext Transfer Protocol, http://en.wikipedia.org/wiki/HTTP [10] JSON, http://www.json.org/json-es.html [11] OAuth, http://es.wikipedia.org/wiki/OAuth [12] OAuth2 and Configuring Your ‘Application’ With Google, http://cornempire.net/2012/01/08/part-2-oauth2-and-configuring-yourapplication-with-google/ 39 40 Bibliografía [13] PHP, http://es.wikipedia.org/wiki/PHP [14] PHP Documentation, http://php.net/docs.php [15] REST, http://en.wikipedia.org/wiki/Representational_state_transfer [16] Using OAuth 2.0 to Access Google APIs, https://developers.google.com/accounts/docs/OAuth2 [17] Write your First Google App, https://developers.google.com/google-apps/calendar/firstapp [18] XAMPP, http://es.wikipedia.org/wiki/XAMPP Apéndice A Apéndices A.1. Apéndice I Darse de alta proyectos en Google A continuación se explicará cómo preparar el proyecto para el sistema de reservas funcione con las API que ofrece Google. Se empezará visitando la página web: https://code.google.com/apis/console/ Figura A.1: Google APIs Console: Creación de un nuevo proyecto 41 42 Apéndices Figura A.2: Google APIs Console: Página principal Desde la página principal visitamos a la pestaña Services (servicios), donde se ha de buscar la API de Google Calendar y activarla (pulsando en el interruptor de ’ON - OFF’). Figura A.3: Google APIs Console: Lista de servicios de Google A.1. Apéndice I Figura A.4: Google APIs Console: Estado actual de la API de Google Calendar 43 44 Apéndices Para activar las API relacionadas a Google Calendar habrá que aceptar los términos del servicio. Figura A.5: Google APIs Console: Términos de uso para permitir el uso de las API, 1º parte Figura A.6: Google APIs Console: Términos de uso para permitir el uso de las API, 2º parte y última A.1. Apéndice I Figura A.7: Google APIs Console: Activación éxitosa de la API de Google Calendar A continuación se tendrá que crear 2 identificaciones de cliente para OAuth 2.0 (OAuth 2.0 Client ID). Primero se creará la identificación destinada para aplicaciones web (Web Application) Figura A.8: Google APIs Console: Vista de la pestaña API Access (Acceso a la API) 45 46 Apéndices En la ventana que aparecerá se podrá especificar el nombre del proyecto que aparecerá cada vez que OAuth nos solicite permisos para trabajar sobre datos privados. Se pulsará en siguiente para continuar con el proceso. Figura A.9: Google APIs Console: Configuración del nuevo proyecto Finalmente se tendrá que configurar la ID de cliente. En el tipo de aplicación (Application type) se seleccionará ’Aplicación web’ (Web application) y la dirección del sitio web donde se utilizará el proyecto, en caso de trabajar en un servidor web en local se escribirá ’localhost’. Para finalizar el proceso se pulsará en Crear ID de cliente (Create client ID). Figura A.10: Google APIs Console: Configuración de un Client ID para aplicaciones web A.1. Apéndice I En la pestaña de Acceso a la API (API Access) aparecerá nuevos datos para su uso por aplicaciens web. Para crear la segunda ID de cliente que necesita el proyecto se pulsará el botón ’Crear otro ID de cliente’ (Create another client ID). Figura A.11: Google APIs Console: Vista de la pestaña API Access (Acceso a la API) después de crear el primer ID de los 2 necesarios 47 48 Apéndices Aparecerá de nuevo la ventana de configuración la ID de cliente pero en vez de aplicación web se seleccionará ’Aplicación instalada’ (Installed application). En el tipo de aplicación instalada se seleccionará la opción ’Otros’ (Other) y se finalizará el proceso. Figura A.12: Google APIs Console: Vista de la pestaña API Access (Acceso a la API) después de crear el primer ID de los 2 necesarios A.1. Apéndice I Con los datos obtenidos en este apéndice se deberá de usar en el sistema de reservas para que funcione el calendario del profesor. El resultado final de todo el proceso será similar a la figura A.13. Figura A.13: Google APIs Console: Vista de la pestaña API Access (Acceso a la API) después de crear los 2 ID necesarios para el funcionamiento del sistema de reservas 49 50 Apéndices A.2. Apéndice II Dar de alta la cuenta del profesor en el sistema de reservas Para dar de alta al profesor en el sistema de reservas hay que realizar previamente los pasos del Apéndice I y utilizar los datos que aparecen en las secciones de la figura A.13) en una página de administración diseñada para ese propósito (ver Figura A.14). Figura A.14: Etapa inicial del proceso de alta del calendario del profesor en el sistema de reservas A.2. Apéndice II Una vez generada una parte de los datos del tutor será posible autenticarse en la página del sistema de reservas, pero aún no estará operativo porque requiere que el profesor acceda a su cuenta y active el servicio, momento el cual ya estará el servicio de reservas activo, finalizando el proceso de alta del profesor (ver Figura A.15). Figura A.15: Etapa final del proceso de alta del calendario del profesor en el sistema de reservas 51 52 Apéndices A.3. Apéndice III Demostración del sistema de reservas A continuación se explicará en varios pasos cómo realizar una reserva para asistir a las tutorías del profesor. El objetivo de la demostración será solicitar una tutoría para un lunes 12 de noviembre para realizar una serie de cuestiones al profesor relacionadas con el temario de clase: 1. Inicialmente se accede a la página de prueba, pudiéndose observar los elementos básicos de la web disponibles cuando no se ha accedido al sistema de reservas. Al ser la página en modo consulta en la parte inferior se muestra un calendario que puede utilizarse para observar el calendario del profesor. En color verde aparecerá los horarios de tutorías y en color rojo se mostrarán las reservas realizadas por los alumnos. A continuación, para avanzar con el proceso se pulsará en ’Entrar’ para iniciar la autenticación con la cuenta del alumno de Google o de la UJI (ver Figura A.16). Figura A.16: Tutorial para crear reservas: Página inicial de pruebas del sistema de reservas A.3. Apéndice III 2. Se accede a la pantalla de autenticación de cuentas de Google (ver Figura A.17). Para continuar se indica el usuario y la contraseña y se pulsará en ’Iniciar sesión’. Si se accede al sistema de reservas con una cuenta de la universidad es probable encontrar un aviso como el de la figura A.18 para obligar la autenticación desde el servicio de la UJI. Pulsando en ’Continuar’ se procede a la siguiente pantalla. Si no se accede mediante una cuenta de la universidad se omite el paso 3. Figura A.17: Tutorial para crear reservas: Pantalla de autenticación de Google Figura A.18: Tutorial para crear reservas: Aviso para obligar la autenticación desde el servicio de la UJI 53 54 Apéndices 3. Una vez accedido a la página de autenticación de la UJI (ver Figura A.19) se ingresan los campos de usuario y contraseña y se pulsará el botón ’Continuar’. Figura A.19: Tutorial para crear reservas: Autenticación de la cuenta de usuario desde la UJI A.3. Apéndice III 4. Una vez autenticada la cuenta de usuario se procede a la autorización de Google para el uso de datos privados en sus servicios (ver Figura A.20). Para continuar se pulsa en ’Permitir acceso’, de lo contrario no se podrá utilizar el sistema de reservas. Figura A.20: Tutorial para crear reservas: Autorización de Google para el uso de datos privados 55 56 Apéndices 5. Una vez se ha regresado a la página de reservas se selecciona la semana a la que se va a realizar la reserva. En este ejemplo hay que desplazarse hasta noviembre. Para ello se pulsa en los controles de navegación [Hoy] [ ->] Ir a [ ... ] hasta llegar a la semana donde se necesita realizar la reserva (ver Figura A.21). Figura A.21: Tutorial para crear reservas: Página de prueba de reservas desde una cuenta de usuario autenticada A.3. Apéndice III 6. A continuación hay que buscar el día de tutorías donde se quiere realizar la reserva, por si hay huecos libres (ver Figura A.22). En este caso en el día 12 aún existe un hueco desde las 12:00 hasta las 13:00 para solicitar la tutoría con el profesor. Figura A.22: Tutorial para crear reservas: Mostrando reservas de la semana seleccionada 57 58 Apéndices 7. Se puede observar en la parte inferior de la página de forma gráfica las reservas que existen para esa semana antes de la creación de la reserva (ver Figura A.23). Figura A.23: Tutorial para crear reservas: Vista gráfica del calendario de tutorías. En rojo aparecen las reservas existentes A.3. Apéndice III 8. Como no se cree necesario que haga falta reservar la hora entera para que el profesor solucione todas las dudas se selecciona el tiempo de reserva mínimo que acepta el sistema de reservas y se especifica la razón de la reserva (ver Figura A.24). De esta manera el profesor podrá conocer la razón de la tutoría con antelación y prepararse si fuera necesario. Para dar de alta la reserva se pulsa sobre ’Reservar hora’. Figura A.24: Tutorial para crear reservas: Preparando la reserva 59 60 Apéndices 9. Finalmente la nueva reserva estará visible tanto en la lista de reservas de esa semana (ver Figura A.25) como en la vista gráfica de la parte inferior de la página de reservas (ver Figura A.26). Figura A.25: Tutorial para crear reservas: Mostrando reservas de la semana seleccionada tras la creación de una nueva reserva Figura A.26: Tutorial para crear reservas: Vista gráfica del calendario de tutorías después de reservar tiempo de tutorías del profesor A.3. Apéndice III 10. Si por alguna razón ha ocurrido alguna confusión con la fecha de la reserva, se prefiere cambiar la hora a otra más apropiada para el alumno o ya no se quiere asistir a la tutoría del profesor el alumno podrá anular sus propias reservas. Para realizar esa acción se pulsará en el botón ’Cancelar’ de la reserva que se desea anular (ver Figura A.25). Tras la operación se retornará el espacio de tiempo ocupado por la reserva al sistema, dejando la posibilidad de realizar nuevas reservas a nuevos alumnos (ver Figura A.27). Figura A.27: Tutorial para crear reservas: Mostrando reservas de la semana seleccionada (tras eliminación de reserva) 61 62 Apéndices A.4. Apéndice IV Código fuente de other_functions.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <?php // F u n c i o n e s de ambito g e n e r a l // Dado un rango de h o r a s para t u t o r i a s y una l i s t a de r e s e r v a s ←d e v u e l v e una m a t r i z con l a s h o r a s l i b r e s . // Cada e l e m e n t o de l a m a t r i z t e n d r a una m a t r i z de 2 e l e m e n t o s , con ←l a s h o r a s de i n i c i o y f i n d e l hueco l i b r e e n c o n t r a d o . function f i n d F r e e B o o k S l o t s ( $startDate , $endDate , $bookedSlots ) { // S o l o s e va a t r a b a j a r con l a s h o r a s . $startTime = s t r t o t i m e ( $startDate ) ; $endTime = s t r t o t i m e ( $endDate ) ; $freeSlotList = a r r a y ( ) ; $i = 0 ; // En c a s o de r e c i b i r l a l i s t a de r e s e r v a s de todo e l d i a s e ←f i l t r a r a a q u e l l a s r e s e r v a s a n t e r i o r e s a l a hora de i n i c i o ←deseada . w h i l e ( i s s e t ( $bookedSlots [ $i ] ) && s t r t o t i m e ( $bookedSlots [ $i]−>end ←−>dateTime ) <= s t r t o t i m e ( $startDate ) ) { $i++; } $start = $startTime ; // Se a n a l i z a e l h o r a r i o en e s p a c i o s de 30 minutos . w h i l e ( $start < $endTime ) { i f ( i s s e t ( $bookedSlots [ $i ] ) && $start >= s t r t o t i m e ( ←$bookedSlots [ $i]−>start−>dateTime ) && $start < s t r t o t i m e ( ←$bookedSlots [ $i]−>end−>dateTime ) ) { // Se e v i t a n l a s h o r a s r e s e r v a d a s i f ( $start >= s t r t o t i m e ( $bookedSlots [ $i]−>start−>dateTime ) ←&& $start < s t r t o t i m e ( $bookedSlots [ $i]−>end−>dateTime←)) { $start = s t r t o t i m e ( $bookedSlots [ $i]−>end−>dateTime ) ; } $i++; } else { // Se a v e r i g u a e l rango de h o r a s d e l hueco l i b r e ←encontrado $end = $start ; i f ( i s s e t ( $bookedSlots [ $i ] ) ) { w h i l e ( $end < $endTime && $end < s t r t o t i m e ( ←$bookedSlots [ $i]−>start−>dateTime ) ) { $end = $end + ( 3 0 ∗ 6 0 ) ; } A.4. Apéndice IV 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 } else { } } } } 63 w h i l e ( $end < $endTime ) { $end = $end + ( 3 0 ∗ 6 0 ) ; } $freeSlotList [ ] = a r r a y ( d a t e ( "Y−m−d\TH: i : s \Z " , $start ) , ←d a t e ( "Y−m−d\TH: i : s \Z " , $end ) ) ; $start = $end ; return $freeSlotList ; // Dado un rango de h o r a s para t u t o r i a s y una l i s t a de r e s e r v a s ←d e v u e l v e una m a t r i z con l a s h o r a s l i b r e s y l a s h o r a s ocupadas por ←reservas . // Cada e l e m e n t o de l a m a t r i z que s e a una r e s e r v a t e n d r a una c o p i a d e l ←e l e m e n t o o r i g i n a l y cada hueco l i b r e s e r a una m a t r i z de 2 ←e l e m e n t o s , con l a s h o r a s de i n i c i o y f i n d e l hueco l i b r e ←encontrado . function findAllSlots ( $startDate , $endDate , $bookedSlots ) { // S o l o s e va a t r a b a j a r con l a s h o r a s . $startTime = s t r t o t i m e ( $startDate ) ; $endTime = s t r t o t i m e ( $endDate ) ; $freeSlotList = a r r a y ( ) ; $i = 0 ; // En c a s o de r e c i b i r l a l i s t a de r e s e r v a s de todo e l d i a s e ←f i l t r a r a a q u e l l a s r e s e r v a s a n t e r i o r e s a l a hora de i n i c i o ←deseada . w h i l e ( i s s e t ( $bookedSlots [ $i ] ) && s t r t o t i m e ( $bookedSlots [ $i]−>end ←−>dateTime ) <= s t r t o t i m e ( $startDate ) ) { $i++; } $start = $startTime ; // Se a n a l i z a e l h o r a r i o en e s p a c i o s de 30 minutos . w h i l e ( $start < $endTime ) { i f ( i s s e t ( $bookedSlots [ $i ] ) && $start >= s t r t o t i m e ( ←$bookedSlots [ $i]−>start−>dateTime ) && $start < s t r t o t i m e ( ←$bookedSlots [ $i]−>end−>dateTime ) ) { // Se e v i t a n l a s h o r a s r e s e r v a d a s i f ( $start >= s t r t o t i m e ( $bookedSlots [ $i]−>start−>dateTime ) ←&& $start < s t r t o t i m e ( $bookedSlots [ $i]−>end−>dateTime←)) { $freeSlotList [ ] = $bookedSlots [ $i ] ; $start = s t r t o t i m e ( $bookedSlots [ $i]−>end−>dateTime ) ; } $i++; } 64 Apéndices 94 95 96 else { 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 } } } // Se a v e r i g u a e l rango de h o r a s d e l hueco l i b r e ←encontrado $end = $start ; i f ( i s s e t ( $bookedSlots [ $i ] ) ) { w h i l e ( $end < $endTime && $end < s t r t o t i m e ( ←$bookedSlots [ $i]−>start−>dateTime ) ) { $end = $end + ( 3 0 ∗ 6 0 ) ; } } else { w h i l e ( $end < $endTime ) { $end = $end + ( 3 0 ∗ 6 0 ) ; } } $freeSlotList [ ] = a r r a y ( d a t e ( "Y−m−d\TH: i : s \Z " , $start ) , ←d a t e ( "Y−m−d\TH: i : s \Z " , $end ) ) ; $start = $end ; return $freeSlotList ; // Dado un rango de h o r a s que queremos a n a l i z a r y una l i s t a de ←r e s e r v a s d e v u e l v e v e r d a d e r o s i e l rango de h o r a s a n a l i z a d a s e s t a ←l i b r e de r e s e r v a s , f a l s o en c a s o c o n t r a r i o . function isFree BookSlot ( $startDate , $endDate , $bookedSlots ) { d a t e _ d e f a u l t _ t i m e z o n e _ s e t ( " Europe / Madrid " ) ; $start = s t r t o t i m e ( $startDate ) ; $end = s t r t o t i m e ( $endDate ) ; $i = 0 ; // En c a s o de r e c i b i r l a l i s t a de r e s e r v a s de todo e l d i a s e ←f i l t r a r a a q u e l l a s r e s e r v a s a n t e r i o r e s a l a hora de i n i c i o ←deseada . w h i l e ( i s s e t ( $bookedSlots [ $i ] ) && s t r t o t i m e ( $bookedSlots [ $i]−>end ←−>dateTime ) <= s t r t o t i m e ( $startDate ) ) { $i++; } // Se a n a l i z a e l h o r a r i o en e s p a c i o s de 30 minutos . w h i l e ( $start < $end && $i < count ( $bookedSlots ) ) { i f ( i s s e t ( $bookedSlots [ $i ] ) ) { // S i l a s hora a n a l i z a d a e s t a r e s e r v a d a s e a b o r t a e l ←proceso i f ( $start >= s t r t o t i m e ( $bookedSlots [ $i]−>start−>dateTime ) ←&& $start < s t r t o t i m e ( $bookedSlots [ $i]−>end−>dateTime←)) { return f a l s e ; } A.4. Apéndice IV 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 65 $start = $start + ( 3 0 ∗ 6 0 ) ; } else { } } } i f ( s t r t o t i m e ( $bookedSlots [ $i]−>end−>dateTime ) <= $start ) { $i++; } return t r u e ; return t r u e ; // Dado un rango de h o r a s que queremos a n a l i z a r y una l i s t a de h o r a s ←de t u t o r i a s d e v u e l v e v e r d a d e r o s i e l rango de h o r a s a n a l i z a d a s ←e s t a d e n t r o de a l g u n a t u t o r i a , f a l s e en c a s o c o n t r a r i o . function i sV a li dB oo k Sl ot ( $startDate , $endDate , $tut oringSlo ts ) { d a t e _ d e f a u l t _ t i m e z o n e _ s e t ( " Europe / Madrid " ) ; $start = s t r t o t i m e ( $startDate ) ; $end = s t r t o t i m e ( $endDate ) ; $i = 0 ; // En c a s o de r e c i b i r l a l i s t a de t u t o r i a s de todo e l d i a s e ←f i l t r a r a a q u e l l a s r e s e r v a s a n t e r i o r e s a l a hora de i n i c i o ←deseada . w h i l e ( i s s e t ( $tut oringSlo ts [ $i ] ) && s t r t o t i m e ( $tut oringSlo ts [ $i]−>←end−>dateTime ) <= s t r t o t i m e ( $startDate ) ) { $i++; } // Se a n a l i z a e l h o r a r i o en e s p a c i o s de 30 minutos . w h i l e ( $start < $end ) { i f ( i s s e t ( $tut oringSlo ts [ $i ] ) ) { // S i l a s hora a n a l i z a d a e s t a r e s e r v a d a s e a b o r t a e l ←proceso i f ( $start >= s t r t o t i m e ( $tut oringSlo ts [ $i]−>start−>←dateTime ) && $start < s t r t o t i m e ( $tuto ringSlot s [ $i]−>←end−>dateTime ) ) { } else { } } else $start = $start + ( 3 0 ∗ 6 0 ) ; return f a l s e ; i f ( s t r t o t i m e ( $tut oringSlo ts [ $i]−>end−>dateTime ) <= $start←) { $i++; } 66 Apéndices 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 { } } } return f a l s e ; return t r u e ; function s o r t T u t o r i n g _ h o u r s ( $ tu to ri n g_ ho ur s ) { // L i s t a c o m p l e t a de t u t o r i a s de l a semana i f ( ! i s s e t ( $tutoring_hours−>items ) ) { return ; } f o r ( $i = 0 ; $i < count ( $tutoring_hours−>items ) ; $i++) { $str CurrentD ay = d a t e ( "w" , s t r t o t i m e ( $tutoring_hours−>items [ $i←]−>start−>dateTime ) ) ; } } $tutoringList [ $str CurrentD ay ] [ ] = $tutoring_hours−>items [ $i ] ; return $tutoringList ; function sortBo ok_hours ( $book_hours ) { // L i s t a de r e s e r v a s de l a semana f o r ( $i = 0 ; $i < 7 ; $i++) { $bookList [ $i ] = a r r a y ( ) ; } i f ( i s s e t ( $book_hours−>items ) ) { f o r ( $i = 0 ; $i < count ( $book_hours−>items ) ; $i++) { $str CurrentD ay = d a t e ( "w" , s t r t o t i m e ( $book_hours−>items [ $i←]−>start−>dateTime ) ) ; } } } $bookList [ $str CurrentD ay ] [ ] = $book_hours−>items [ $i ] ; return $bookList ; // Dada una f e c h a c o m p l e t a en f o r m a t o UTC cambia unicamente l a zona ←horaria sin modificar la fecha . function U T C _ t i m e Z o n e R e p l a c e r ( $oldDate ) { d a t e _ d e f a u l t _ t i m e z o n e _ s e t ( " Europe / Madrid " ) ; $newDate = s t r t o t i m e ( gmdate ( " d−m−Y\TH: i : s " , s t r t o t i m e ( $oldDate ) ) ) ; $newDate = d a t e ( " d−m−Y\TH: i : sP " , $newDate ) ; } return $newDate ; // Devuelve t r u e s i l a f e c h a de e n t r a d a e s a n t e r i o r a l a f e c h a a c t u a l ←y f a l s e en c a s o c o n t r a r i o . A.4. Apéndice IV 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 function isOldEvent ( $selectedDate , $format = "Y−m−d\TH: i " ) { i f ( s t r t o t i m e ( d a t e ( $format , s t r t o t i m e ( $selectedDate ) ) ) >= ←s t r t o t i m e ( d a t e ( $format ) ) ) { return f a l s e ; } } // Dado un rango de h o r a s de i n i c i o y f i n s e c r e a l o s c o n t r o l e s HTML5 ←que a p a r e c e r a n en e l l u g a r donde s e ha r e a l i z a d o l a l l a m a d a a e s t a ←f u n c i o n . Las l i s t a s d e s p l e g a b l e s c o n t i e n e n h o r a s v a l i d a s para ←realizar reservas . function d r a w N e w B o o k B u t t o n ( $start , $end ) { w h i l e ( isOldEvent ( gmdate ( "Y−m−d\TH: i " , $start ) ) ) { $start += ( 3 0 ∗ 6 0 ) ; } $n = ( $end − $start ) / ( 3 0 ∗ 6 0 ) ; i f ( $n <= 0 ) { return " " ; } $aux = $start ; $temp = '<i n p u t t y p e =" bu tt on " v a l u e =" R e s e r v a r hora " o n c l i c k ="←createNewBook ( \ ' ' . $start . '_ ' . $end . ' \ ' ) " /> I n i c i o : < s e l e c t i d ←="from_ ' . $start . '_ ' . $end . ' " onChange="checkBookTimes ( \ ' from_ ' . ←$start . '_ ' . $end . ' \ ' , \ ' to_ ' . $start . '_ ' . $end . ' \ ' ) "> ' ; 282 283 284 285 f o r ( $i = 0 ; $i < $n ; $i++) { $temp .= '<o p t i o n v a l u e = " ' . $aux . ' "> ' . gmdate ( "H: i " , $aux ) . '</←option>' ; $aux += ( 3 0 ∗ 6 0 ) ; } $temp .= '</ s e l e c t > ' ; 286 287 288 289 290 $temp .= ' − Fin : < s e l e c t i d ="to_ ' . $start . '_ ' . $end . ' " onChange="←checkBookTimes ( \ ' from_ ' . $start . '_ ' . $end . ' \ ' , \ ' to_ ' . $start . '_ ' ←. $end . ' \ ' ) "> ' ; $aux = $start + ( 3 0 ∗ 6 0 ) ; 291 292 293 294 295 f o r ( $i = 0 ; $i < $n ; $i++) { $temp .= '<o p t i o n v a l u e =" ' . $aux . ' "> ' . gmdate ( "H: i " , $aux ) . '</←option>' ; $aux += ( 3 0 ∗ 6 0 ) ; } $temp .= '</ s e l e c t > <i n p u t t y p e =" t e x t " i d ="desc_ ' . $start . '_ ' . $end . ←' " p l a c e h o l d e r =" I n d i c a l a r a z o n de l a r e s e r v a " s i z e =50 /> ' ; 296 297 298 299 300 301 302 303 return t r u e ; } return $temp ; // Dado un i d e n t i f i c a d o r de e v e n t o para Google C a l e n d a r s e c r e a l o s ←c o n t r o l e s HTML5 que a p a r e c e r a n en e l l u g a r donde s e ha r e a l i z a d o ←l a l l a m a d a a e s t a f u n c i o n . El boton de C a n c e l a r s i r v e para ←- 67 68 Apéndices 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 e l i m i n a r una r e s e r v a d e l c a l e n d a r i o de r e s e r v a s d e l Tutor . function d r a w Ca n c e l B u t t o n ( $event_id ) { return '<i n p u t t y p e =" bu tt on " v a l u e =" C a n c e l a r " o n c l i c k =" d e l e t e B o o k ←( \ ' ' . $event_id . ' \ ' ) " /><i n p u t t y p e =" h id de n " name="eid_ ' . ←$event_id . ' " i d ="eid_ ' . $event_id . ' " v a l u e =" ' . $event_id . ' " /> ' ; } // Anadir b o t o n e s de n a v e g a c i o n para d e s p l a z a r s e e n t r e l a s semanas d e l ←c a l e n d a r i o . Para su c o r r e c t o f u n c i o n a m i e n t o r e q u i e r e implementar ←l a f u n c i o n de J a v a s c r i p t l o a d U r l ( ) , e j e m p l o de c o d i g o : // < s c r i p t t y p e =" t e x t / j a v a s c r i p t "> // f u n c t i o n l o a d U r l ( l o c a t i o n ) // { // window . l o c a t i o n . h r e f = l o c a t i o n ; // } //</ s c r i p t > function d r a w W e e k B r o w s e r B u t t o n s ( $week_selected , $yea r_select ed ) { i f ( ! i s s e t ( $wee k_select ed ) | | ! i s s e t ( $yea r_select ed ) ) { return ' ' ; } $yea r_select ed = d a t e ( 'Y ' ) ; i f ( $wee k_select ed < ( d a t e ( 'W' ) − 1 ) ) { // En c a s o de s o l i c i t a r semanas a n t e r i o r e s a l a a c t u a l s e ←devuelve la actual . $wee k_select ed = d a t e ( 'W' ) − 1 ; } w h i l e ( $wee k_select ed > 5 1 ) { $wee k_select ed −= 5 2 ; $yea r_select ed++; } $temp = '<i n p u t t y p e =" bu tt on " v a l u e ="Hoy " o n c l i c k =" l o a d U r l ( \ ' ' . ←h t m l e n t i t i e s ( $_SERVER [ 'PHP_SELF ' ] ) . ' \ ' ) ; " /> ' ; i f ( $wee k_select ed > ( d a t e ( "W" ) − 1 ) ) { $temp .= '<i n p u t t y p e =" bu tt on " v a l u e ="<" o n c l i c k =" l o a d U r l ( \ ' ' . ←h t m l e n t i t i e s ( $_SERVER [ 'PHP_SELF ' ] ) . ' ? week= ' . ( ←$week _selecte d ) . ' \ ' ) ; " /> ' ; } $temp .= '<i n p u t t y p e =" bu tt on " v a l u e =">" o n c l i c k =" l o a d U r l ( \ ' ' . ←h t m l e n t i t i e s ( $_SERVER [ 'PHP_SELF ' ] ) . ' ? week= ' . ( $week _selecte d + ←2 ) . ' \ ' ) ; " /> ' ; $temp .= ' I r a : < s e l e c t onChange=" l o a d U r l ( \ ' ' . h t m l e n t i t i e s ( ←$_SERVER [ 'PHP_SELF ' ] ) . ' ? week =\ ' + t h i s . o p t i o n s [ t h i s . ←s e l e c t e d I n d e x ] . v a l u e ) ;" > ' ; $strMonthDays = a r r a y ( 1 => " Enero " , 2 => " F e b r e r o " , 3 => " Marzo " , ←4 => " A b r i l " , 5 => " Mayo " , 6 => " J u n i o " , 7 => " J u l i o " , 8 => " ←Agosto " , 9 => " S e p t i e m b r e " , 10 => " Octubre " , 11 => " Noviembre " ←, 12 => " D i c i e m b r e " ) ; $aux_date = d a t e ( " c " ) ; A.4. Apéndice IV 350 351 352 353 354 f o r ( $i = 0 ; $i < 1 2 ; $i++) { $year = ( d a t e ( "Y" , s t r t o t i m e ( $aux_date ) ) ) ; $month = d a t e ( " n " , s t r t o t i m e ( $aux_date ) ) ; $week_index = d a t e ( "W" , s t r t o t i m e ( $aux_date ) ) + ( 5 2 ∗ ( d a t e ( "Y←" , s t r t o t i m e ( $aux_date ) ) − d a t e ( "Y" ) ) ) ; 355 356 $temp .= '<o p t i o n v a l u e =" ' . $week_index . ' "> ' . $strMonthDays [ ←$month ] . ' , ' . $year . '</o p t i o n > ' ; 357 358 359 360 361 362 363 364 365 366 367 368 // Apuntando a l s i g u i e n t e mes $wk_ts = s t r t o t i m e ( $year . d a t e ( "m" , s t r t o t i m e ( $aux_date ) ) . ' 01 ' ) ←; $mon_ts = s t r t o t i m e ( '+1 month ' , $wk_ts ) ; $aux_date = d a t e ( " c " , $mon_ts ) ; } $temp .= '</ s e l e c t > ' ; } ?> return $temp ; 69 70 Apéndices Código fuente de: calendar_userlogin.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <?php // Nota : a l g u n a s f u n c i o n e s r e q u i e r e n l a s l i b r e r i a s para a c c e d e r a l a s ←API de Google . Hay que d e s c a r g a r l o de l a s i g u i e n t e URL: // h t t p : / / code . g o o g l e . com/p/ g o o g l e −api −php−c l i e n t / if { } if { } ( ! session_id () ) session_start () ; ( ! i s s e t ( $calendarOwnerFile ) ) $calendarOwnerFile = ' ' ; i f ( i s s e t ( $ c a l e n d a r O w n e r F i l e ) && s t r l e n ( $ c a l e n d a r O w n e r F i l e ) > 0 && ←f i l e _ e x i s t s ( " $ c a l e n d a r O w n e r F i l e . php " ) ) { include_once ( " $ c a l e n d a r O w n e r F i l e . php " ) ; i f ( i s s e t ( $_GET [ ' s t a t e ' ] ) && $_GET [ ' s t a t e ' ] == " p r o f i l e " ) { // Se p r o c e d e a l a a u t e n t i c a c i o n d e s a t e n d i d a d e l t u t o r para l a ←c a r g a d e l c a l e n d a r i o de r e s e r v a s . include_once ( " c a l e n d a r _ a u t o l o g i n . php " ) ; } } else { d i e ( " E r r o r : ' $ c a l e n d a r O w n e r F i l e . php ' no e x i s t e o no s e puede ←a c c e d e r . No s e puede c o n t i n u a r con e l p r o c e s o .< br />" ) ; } require_once ' . / s r c / a p i C l i e n t . php ' ; $client = new apiClient ( ) ; $client−>setScopes ( $ u s e r l o g i n _ sc o p e ) ; $client−>setClientId ( $ u s e r l o g i n _ c l i e n t _ i d ) ; $client−>s et Cl ie n tS ec re t ( $ u s e r l o g i n _ c l i e n t _ s e c r e t ) ; $client−>setR edirectU ri ( $ u s e r l o g i n _ r e d i r e c t _ u r i ) ; $client−>s et De ve l op er Ke y ( $api_key ) ; i f ( i s s e t ( $_REQUEST [ ' l o g o u t ' ] ) ) { u n s e t ( $_SESSION [ ' u s e r l o g i n ' ] ) ; u n s e t ( $_SESSION [ ' a c c e s s _ t o k e n ' ] ) ; } i f ( i s s e t ( $_GET [ ' code ' ] ) ) { $client−>authenticate ( ) ; $_SESSION [ ' a c c e s s _ t o k e n ' ] = $client−>getA ccessToke n ( ) ; h e a d e r ( ' L o c a t i o n : h t t p : / / ' . $_SERVER [ 'HTTP_HOST ' ] . $_SERVER [ ' ←PHP_SELF ' ] ) ; } i f ( i s s e t ( $_SESSION [ ' a c c e s s _ t o k e n ' ] ) ) A.4. Apéndice IV 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 { } $client−>setA ccessTok en ( $_SESSION [ ' a c c e s s _ t o k e n ' ] ) ; i f ( $client−>getA ccessTok en ( ) ) { $_SESSION [ ' a c c e s s _ t o k e n ' ] = $client−>getA ccessTok en ( ) ; } else { } $ us er lo g in _d on e = t r u e ; $authUrl = $client−>createAuthUrl ( ) ; $ us er lo g in _d on e = f a l s e ; // −−−−−−−−−−−−−−−−−−−−−−−− FUNCIONES ESENCIALES ←−−−−−−−−−−−−−−−−−−−−−−−− function getAcc essToken ( $jsonText ) { $responseObj = json_decode ( $jsonText ) ; // s u b s t r i f ( i s s e t ( $responseObj−>access_token ) ) { return $responseObj−>access_token ; } } return " " ; function g et R ef re sh T ok en ( $jsonText ) { $responseObj = json_decode ( $jsonText ) ; // s u b s t r i f ( i s s e t ( $responseObj−>refresh_token ) ) { return $responseObj−>refresh_token ; } } return " " ; // I n f o r m a c i o n b a s i c a d e l u s u a r i o a u t e n t i c a d o function g e t C l i e n t U s e r I n f o ( $ j s o n _ a c c e s s T o k e n ) { $accessToken = json_decode ( $ j s o n _ a c c e s s T o k e n ) ; $accessToken = $accessToken−>access_token ; $query = " h t t p s : / /www. g o o g l e a p i s . com/ oauth2 / v1 / u s e r i n f o ?←a c c e s s _ t o k e n=" . $accessToken ; $curl = c u r l _ i n i t ( $query ) ; c u r l _ s e t o p t ( $curl , CURLOPT_HTTPAUTH , CURLAUTH_ANY ) ; c u r l _ s e t o p t ( $curl , CURLOPT_SSL_VERIFYPEER , f a l s e ) ; c u r l _ s e t o p t ( $curl , CURLOPT_RETURNTRANSFER , 1 ) ; $curlheader [ 0 ] = " A u t h o r i z a t i o n : B e a r e r " . $accessToken ; c u r l _ s e t o p t ( $curl , CURLOPT_HTTPHEADER , $curlheader ) ; $jso n_respon se = c u r l _ e x e c ( $curl ) ; c u r l _ c l o s e ( $curl ) ; $responseObj = json_decode ( $jso n_respon se ) ; 71 72 Apéndices 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 return $responseObj ; } ?> // Devuelve l a s i g u i e n t e i n f o r m a c i o n : /∗ { " i d " : "##################", " e m a i l " : "############@gmail . com " , " v e r i f i e d _ e m a i l " : true , " name " : " nombre a p e l l i d o 1 " , " given_name " : " nombre " , " family_name " : " a p e l l i d o 1 " , " l i n k " : " h t t p s : / / p l u s . g o o g l e . com/#########", " g e n d e r " : " male " , " b i r t h d a y " : "0000 −08 −08" , " l o c a l e " : " es " } ∗/ A.4. Apéndice IV Código fuente de: calendar_autologin.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?php // Nota : a l g u n a s f u n c i o n e s r e q u i e r e n l a s l i b r e r i a s para a c c e d e r a l a s ←API de Google . Hay que d e s c a r g a r l o de l a s i g u i e n t e URL: // h t t p : / / code . g o o g l e . com/p/ g o o g l e −api −php−c l i e n t / // DATOS NECESARIOS DEL TUTOR $tokenFileExt = ' . r t o k e n ' ; i f ( ! i s s e t ( $calendarOwnerFile ) ) { $calendarOwnerFile = ' ' ; } i f ( i s s e t ( $ c a l e n d a r O w n e r F i l e ) && s t r l e n ( $ c a l e n d a r O w n e r F i l e ) > 0 && ←f i l e _ e x i s t s ( " $ c a l e n d a r O w n e r F i l e . php " ) ) { include_once ( " $ c a l e n d a r O w n e r F i l e . php " ) ; } else { } d i e ( " E r r o r : ' $ c a l e n d a r O w n e r F i l e . php ' no e x i s t e o no s e puede ←a c c e d e r . No s e puede c o n t i n u a r con e l p r o c e s o .< br />" ) ; // F i n a l malo // A u t o r i z a c i o n i n i c i a l de l a c u e n t a d e l t u t o r . Para t e n e r a c c e s o por ←e l u s u a r i o s e r e t o r n a e l parametro URL $accessToken = ' ' ; i f ( i s s e t ( $_REQUEST [ ' code ' ] ) ) { // A u t o r i z a c i o n i n i c i a l de l a c u e n t a d e l t u t o r . F u t u r o s a c c e s o s no←n e c e s i t a r a c o n f i r m a c i o n i n t e r a c t i v a h a s t a que s e e l i m i n e e l ←f i c h e r o a s o c i a d o a l t u t o r con e l " r e f r e s h t o k e n " v a l i d o . $accessToken = g e t _ o a u t h 2 _ to k e n ( $_REQUEST [ ' code ' ] , " o n l i n e " ) ; i f ( s t r l e n ( $ t u t o r i n g _ c a l e n d a r _ i d ) == 0 | | s t r l e n ( $ b o o k _ c a l e n d a r _ i d ←) == 0 ) { $tutoring_calendar_id = insertNewTutoringCalendar ( ) ; $book_calendar_id = insertNewBookingCalendar ( ) ; 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 $refreshToken = r e a d _ c a l e n d a r O w n e r _ t o k e n ( " ←$calendarOwnerFile$tokenFileExt " ) ; } } if { } f i l l _ c a l e n d a r O w n e r _ c o n f i g ( " $ c a l e n d a r O w n e r F i l e . php " ) ; // Limpiar l a URL h e a d e r ( ' L o c a t i o n : h t t p : / / ' . $_SERVER [ 'HTTP_HOST ' ] . $_SERVER [ ' ←PHP_SELF ' ] ) ; ( ! ( i s s e t ( $accessToken ) && s t r l e n ( $accessToken ) > 1 ) ) $accessToken = g e t _ o a u t h 2 _ to k e n ( $refreshToken , " o f f l i n e " ) ; 73 74 Apéndices 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 i f ( s t r l e n ( $accessToken ) == 0 | | ! ( i s s e t ( $ t u t o r i n g _ c a l e n d a r _ i d ) && ←i s s e t ( $ b o o k _ c a l e n d a r _ i d ) && s t r l e n ( $ t u t o r i n g _ c a l e n d a r _ i d ) > 0 && ←s t r l e n ( $book_calendar_id ) > 0) ) { $ au to lo g in _d on e = f a l s e ; } else { $ j s o n _ a c c e s s T o k e n = ' { " a c c e s s _ t o k e n " : " ' . $accessToken . ' " , " ←token_type " : " B e a r e r " , " e x p i r e s _ i n " : 3 6 0 0 , " c r e a t e d " : 1 , " ←r e f r e s h _ t o k e n " : " ' . $refreshToken . ' " } ' ; echo "<br />" ; } i f ( s t r l e n ( $accessToken ) != 0 ) { $ au to lo g in _d on e = t r u e ; } else { $ au to lo g in _d on e = f a l s e ; } // −−−−−−−−−−−−−−−−−−−−−−−− FUNCIONES ESENCIALES ←−−−−−−−−−−−−−−−−−−−−−−−− // Devuelve e l un s t r i n g l l a m a d o " A c c e s s Token " que s e r v i r a como ←c o d i g o de a u t o r i z a c i o n para r e a l i z a r l l a m a d a s a l a API usando ←OAuth 2 . // Dependiendo l o s p a r a m e t r o s y de l a s i t u a c i o n donde s e u s e podra ←o b t e n e r un " R e f r e s h Token " que s e a l m a c e n a r a en un f i c h e r o para ←f u t u r o s u s o s d e l s i s t e m a de r e s e r v a s . function send_g et_query ( $query , $aToken ) { $curl = c u r l _ i n i t ( $query ) ; c u r l _ s e t o p t ( $curl , CURLOPT_HTTPAUTH , CURLAUTH_ANY ) ; c u r l _ s e t o p t ( $curl , CURLOPT_SSL_VERIFYPEER , f a l s e ) ; c u r l _ s e t o p t ( $curl , CURLOPT_RETURNTRANSFER , 1 ) ; $curlheader [ 0 ] = " A u t h o r i z a t i o n : B e a r e r " . $aToken ; c u r l _ s e t o p t ( $curl , CURLOPT_HTTPHEADER , $curlheader ) ; $jso n_respon se = c u r l _ e x e c ( $curl ) ; c u r l _ c l o s e ( $curl ) ; $responseObj = json_decode ( $jso n_respon se ) ; } return $responseObj ; // Lee de un f i c h e r o un s t r i n g que e s e l " r e f r e s h \ _token " a s o c i a d o a l ←c a l e n d a r i o d e l t u t o r . En c a s o de no poder l e e r c o r r e c t a m e n t e e l ←f i c h e r o d e v o l v e r a una cadena v a c i a . function r e a d _ c a l e n d a r O w n e r _ t o k e n ( $ c a l O w n e r F i l e N a m e ) { i f ( f i l e _ e x i s t s ( $calOwnerFileName ) ) { $oFile = f o p e n ( $calOwnerFileName , ' r ' ) or d i e ( " E r r o r : ' ←$calOwnerFileName ' e x i s t e p e r o no s e puede a c c e d e r a su ←c o n t e n i d o .< br />" ) ; $tokenStr = f r e a d ( $oFile , f i l e s i z e ( $calOwnerFileName ) ) ; A.4. Apéndice IV 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 75 f c l o s e ( $oFile ) ; } else { } } return $tokenStr ; return ' ' ; // S a l v a a un f i c h e r o un s t r i n g que e s e l " r e f r e s h \ _token " a s o c i a d o a l ←calendario del tutor . function w r i t e _ c a l e n d a r O w n e r _ t o k e n ( $calOwnerFileName , $tokenStr ) { $oFile = f o p e n ( $calOwnerFileName , 'w ' ) or d i e ( " E r r o r : No s e puede ←a b r i r e l f i c h e r o ' $calOwnerFileName ' para e s c r i t u r a .< br />" ) ; fwrite ( $oFile , $tokenStr ) ; } f c l o s e ( $oFile ) ; // Devuelve e l un s t r i n g l l a m a d o " A c c e s s Token " que s e r v i r a como ←c o d i g o de a u t o r i z a c i o n para r e a l i z a r l l a m a d a s a l a API usando ←OAuth 2 . // Dependiendo l o s p a r a m e t r o s y de l a s i t u a c i o n donde s e u s e podra ←o b t e n e r un " R e f r e s h Token " que s e a l m a c e n a r a en un f i c h e r o para ←f u t u r o s u s o s d e l s i s t e m a de r e s e r v a s . function g e t _ oa u t h 2 _ t o k e n ( $grantCode , $grantType ) { global $client_id ; global $clien t_secret ; global $redirect_uri ; global $ c a l e n d a r O w n e r F i l e ; global $tokenFileExt ; $ o a u t h 2 t o k e n _u r l = " h t t p s : / / a c c o u n t s . g o o g l e . com/ o / oauth2 / t o k e n " ; $clienttoken_post = array ( " c l i e n t _ i d " => $client_id , " c l i e n t _ s e c r e t " => $cli ent_secr et ); i f ( $grantType === " o n l i n e " ) { $ c l i e n t t o k e n _ p o s t [ " code " ] = $grantCode ; $ c l i e n t t o k e n _ p o s t [ " r e d i r e c t _ u r i " ] = $redirect_uri ; $ c l i e n t t o k e n _ p o s t [ " grant_type " ] = " a u t h o r i z a t i o n _ c o d e " ; } i f ( $grantType === " o f f l i n e " ) { $ c l i e n t t o k e n _ p o s t [ " r e f r e s h _ t o k e n " ] = $grantCode ; $ c l i e n t t o k e n _ p o s t [ " grant_type " ] = " r e f r e s h _ t o k e n " ; } $curl = c u r l _ i n i t ( $ o a u t h 2 t o k e n_ u r l ) ; c u r l _ s e t o p t ( $curl c u r l _ s e t o p t ( $curl c u r l _ s e t o p t ( $curl c u r l _ s e t o p t ( $curl , , , , CURLOPT_POST , t r u e ) ; CURLOPT_POSTFIELDS , $ c l i e n t t o k e n _ p o s t ) ; CURLOPT_HTTPAUTH , CURLAUTH_ANY ) ; CURLOPT_SSL_VERIFYPEER , f a l s e ) ; 76 Apéndices 155 156 157 158 159 160 161 162 163 c u r l _ s e t o p t ( $curl , CURLOPT_RETURNTRANSFER , 1 ) ; $jso n_respon se = c u r l _ e x e c ( $curl ) ; c u r l _ c l o s e ( $curl ) ; $authObj = json_decode ( $jso n_respon se ) ; // S i s e ha s o l i c i t a d o e l a c c e s o o f f l i n e , r e g o g e r e l " r e f r e s h ←token " i f ( i s s e t ( $authObj−>refresh_token ) ) { global $refreshToken ; 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 $refreshToken = $authObj−>refresh_token ; // Se s a l v a e l R e f r e s h Token para f u t u r o s a c c e s o s w r i t e _ c a l e n d a r O w n e r _ t o k e n ( " $ c a l e n d a r O w n e r F i l e $ t o k e n F i l e E x t " , ←$refreshToken ) ; } } i f ( i s s e t ( $authObj−>access_token ) ) { return $authObj−>access_token ; } else { return NULL ; } // Envia una p e t i c i o n HTTP a Google para i n s e r t a r d a t o s en Google ←C a l e n d a r . Los d a t o s a i n s e r t a r dependeran d e l t i p o que s e hayan ←e s p e c i f i c a d o en l a URI u t i l i z a d a . El dato que d e v u e l v e l a f u n c i o n ←por d e f e c t o s e r a un c o d i g o HTTP de r e s p u e s t a , d e v o l v i e n d o ' 2 0 0 ' ←como o p e r a c i o n c o r r e c t a . Ademas , s e podra e s p e c i f i c a r que d e v u e l v a ←una s t r i n g que r e p r e s e n t e un c o d i g o de i d e n t i f i c a c i o n ( ID ) en ←c a s o de i n s e r t a r s e un e v e n t o u o b j e t o s i m i l a r . function s en d _p os t_ q ue ry ( $postArgs , $aToken , $URL , $return = " b o o l e a n " ←) { $session = c u r l _ i n i t ( ) ; c u r l _ s e t o p t ( $session , CURLOPT_POST , t r u e ) ; c u r l _ s e t o p t ( $session , CURLOPT_URL , $URL ) ; c u r l _ s e t o p t ( $session , CURLOPT_SSL_VERIFYPEER , f a l s e ) ; c u r l _ s e t o p t ( $session , CURLOPT_RETURNTRANSFER , 1 ) ; $curlheader [ 0 ] = " Content−Type : a p p l i c a t i o n / j s o n " ; $curlheader [ 1 ] = " A u t h o r i z a t i o n : OAuth " . $aToken ; c u r l _ s e t o p t ( $session , CURLOPT_HTTPHEADER , $curlheader ) ; c u r l _ s e t o p t ( $session , CURLOPT_POSTFIELDS , $postArgs ) ; i f ( $return == " i d " ) { $jso n_respon se = c u r l _ e x e c ( $session ) ; c u r l _ c l o s e ( $session ) ; $responseObj = json_decode ( $jso n_respon se ) ; i f ( i s s e t ( $responseObj−>id ) ) { return $responseObj−>id ; } else { A.4. Apéndice IV 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 } } return " " ; // Caso g e n e r a l : d e v o l v e r s i s e ha r e a l i z a d o c o r r e c t a m e n t e o no c u r l _ e x e c ( $session ) ; $response = c u r l _ g e t i n f o ( $session ) ; c u r l _ c l o s e ( $session ) ; } return $response [ ' http_code ' ] ; // Cre ara un nuevo e v e n t o en e l c a l e n d a r i o de r e s e r v a s d e l p r o f e s o r . ←El p r o p i e t a r i o d e l e v e n t o s e g u i r a s i e n d o e l p r o f e s o r p e r o s e ←i n c l u i r a n d a t o s d e l u s u a r i o a u t e n t i c a d o que ha r e a l i z a d o l a a c c i o n ←. Devuelve v e r d a d e r o s i s e ha i n s e r t a d o con e x i t o y f a l s o en c a s o ←contrario . function insert NewEvent ( $startDateTime , $endDateTime , ←$calendarOwnerOffice , $description , $user , $email ) { d a t e _ d e f a u l t _ t i m e z o n e _ s e t ( " Europe / Madrid " ) ; $sta rtDateTi me = U T C _ t i m e Z o n e R e p l a c e r ( $sta rtDateTi me ) ; $endDateTime = U T C _ t i m e Z o n e R e p l a c e r ( $endDateTime ) ; require_once ' . / s r c / a p i C l i e n t . php ' ; require_once ' . / s r c / c o n t r i b / a p i C a l e n d a r S e r v i c e . php ' ; global $api_key ; global $ j s o n _ a c c e s s T o k e n ; global $accessToken ; global $refreshToken ; global $ b o o k _ c a l e n d a r _ i d ; global $ t u t o r i n g _ c a l e n d a r _ i d ; global $redirect_uri ; global $clien t_secret ; global $client_id ; global $scope ; $client = new apiClient ( ) ; $client−>setScopes ( $scope ) ; $client−>setClientId ( $client_id ) ; $client−>s et Cl ie n tS ec re t ( $cli ent_secr et ) ; $client−>setR edirectU ri ( $redirect_uri ) ; $client−>s et De ve l op er Ke y ( $api_key ) ; $client−>refreshToken ( $refreshToken ) ; $sta rtDateTi me = d a t e ( " c " , s t r t o t i m e ( $sta rtDateTi me ) ) ; $endDateTime = d a t e ( " c " , s t r t o t i m e ( $endDateTime ) ) ; { $description = u t f 8 _ e n c o d e ( "<br />T u t o r i a s o l i c i t a d a por :< br />" . ←utf8_decode ( $user ) . " ( $ e m a i l )<br /><br />Razon:< br />←$ d e s c r i p t i o n <br />" ) ; $summary = u t f 8 _ e n c o d e ( " T u t o r i a en $ c a l e n d a r O w n e r O f f i c e " ) ; $tex t_portAr gs = <<<JSON " end " : { " dateTime " : " $endDateTime " }, " start " : { " dateTime " : " $ s t a r t D a t e T i m e " 77 78 Apéndices 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 }, " extendedProperties " : { " private " : { " booker_email " : " $ e m a i l " , " booker_displayName " : " $ u s e r " } }, " summary " : " $summary " , " description " : " $description " , " v i s i b i l i t y " : " public " , " guestsCanInviteOthers " : false , " anyoneCanAddSelf " : f a l s e , " guestsCanModify " : f a l s e , " guestsCanSeeOtherGuests " : f a l s e } JSON ; $responseObj = json_decode ( $client−>getA ccessTok en ( ) ) ; i f ( i s s e t ( $responseObj−>access_token ) ) { // S e l e c c i o n a n d o rango de f e c h a s que abarquen l a semana ←completa $startDate = weekStartDate ( d a t e ( "W" , s t r t o t i m e ( $sta rtDateTi me ) ←) − 1 , d a t e ( "Y" , s t r t o t i m e ( $sta rtDateTi me ) ) ) ; $endDate = weekEndDate ( d a t e ( "W" , s t r t o t i m e ( $endDateTime ) ) − 1 , ←d a t e ( "Y" , s t r t o t i m e ( $endDateTime ) ) ) ; // Cargando l i s t a s de e v e n t o s , s i hay $book_hours = loadEvents ( $startDate , $endDate , ←$book_calendar_id ) ; // L i s t a de r e s e r v a s de l a semana $bookList = sort Book_hou rs ( $book_hours ) ; if { } ( ! isFr eeBookSl ot ( $startDateTime , $endDateTime , $bookList [ ←d a t e ( 'N ' , s t r t o t i m e ( $star tDateTim e ) ) ] ) ) echo "DEBUG: hueco de r e s e r v a s en uso .< br />" ; return f a l s e ; // Cargando l i s t a s de e v e n t o s , s i hay $ tu to ri n g_ ho ur s = loadEvents ( $startDate , $endDate , ←$tutoring_calendar_id ) ; // L i s t a c o m p l e t a de t u t o r i a s de l a semana $tutoringList = s o r t T u t o r i n g _ h o u r s ( $ tu to ri n g_ ho ur s ) ; if { } ( ! i sV al id B oo kS lo t ( $startDateTime , $endDateTime , ←$tutoringList [ ( d a t e ( 'N ' , s t r t o t i m e ( $star tDateTim e ) ) ) ] ) ) echo "DEBUG: El hueco s e l e c c i o n a d o no p e r t e n e c e a l h o r a r i o ←de t u t o r i a s .< br />" ; return f a l s e ; i f ( s t r t o t i m e ( d a t e ( " c " ) ) > s t r t o t i m e ( $sta rtDateTi me ) ) { echo "DEBUG: No s e p e r m i t e s o l i c i t a r h o r a s de t u t o r i a s que←ya han pasado ( a no s e r que t e n g a un D e l o r e a n con ←- A.4. Apéndice IV 316 317 318 319 320 } 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 } else { c o n d e n s a d o r de F l u z o ) .< br />" ; return f a l s e ; echo "DEBUG: Reservando hueco . . . < br />" ; $URL = " h t t p s : / /www. g o o g l e a p i s . com/ c a l e n d a r / v3 / c a l e n d a r s /←$book_calendar_id / e v e n t s " ; $response = s en d_ po s t_ qu er y ( $text_portArgs , $responseObj−>←access_token , $URL ) ; echo "DEBUG: POST r e s p o n s e code : $ r e s p o n s e <br />" ; i f ( $response != " 200 " ) { return f a l s e ; } return f a l s e ; } return t r u e ; } // E l i m i n a r a e l e v e n t o s e l e c c i o n a d o d e l c a l e n d a r i o que s e i n d i q u e . ←D e v o l v e r a v e r d a d e r o s i s e ha e l i m i n a d o e l e v e n t o y f a l s o en c a s o ←contrario . function deleteEvent ( $event_id ) { require_once ' . / s r c / a p i C l i e n t . php ' ; require_once ' . / s r c / c o n t r i b / a p i C a l e n d a r S e r v i c e . php ' ; global $api_key ; global $accessToken ; global $refreshToken ; global $ b o o k _ c a l e n d a r _ i d ; global $redirect_uri ; global $clien t_secret ; global $client_id ; global $scope ; $client = new apiClient ( ) ; $client−>setScopes ( $scope ) ; $client−>setClientId ( $client_id ) ; $client−>s et Cl ie n tS ec re t ( $cli ent_secr et ) ; $client−>setR edirectU ri ( $redirect_uri ) ; $client−>s et De ve l op er Ke y ( $api_key ) ; $client−>refreshToken ( $refreshToken ) ; $responseObj = json_decode ( $client−>getA ccessTok en ( ) ) ; i f ( i s s e t ( $responseObj−>access_token ) ) { $response = s e n d _ d e l e t e _ q u e r y ( $event_id , $responseObj−>←access_token , $ b o o k _ c a l e n d a r _ i d ) ; echo "DEBUG: DELETE r e s p o n s e code : $ r e s p o n s e <br />" ; i f ( $response != " 204 " ) { return f a l s e ; } } else { return f a l s e ; 79 80 Apéndices 372 373 374 375 376 377 378 379 380 381 } } // Envia una p e t i c i o n HTTP a Google para e l i m i n a r d a t o s en Google ←C a l e n d a r . Dependiendo d e l t i p o de dato e s p e c i f i c a d o e l i m i n a r a un ←e v e n t o o un c a l e n d a r i o . El dato que d e v u e l v e l a f u n c i o n por ←d e f e c t o s e r a un c o d i g o HTTP de r e s p u e s t a , d e v o l v i e n d o ' 2 0 4 ' como ←operacion correcta . function s e n d _ d e l e t e _ q u e r y ( $event_id , $aToken , $calendar_id , ←$objectType = " e v e n t " ) { // E x i s t e n 2 t i p o s de b o r r a d o d e p e n d i e n d o d e l o b j e t o a e l i m i n a r : ←c a l e n d a r i o o evento $URL = " h t t p s : / /www. g o o g l e a p i s . com/ c a l e n d a r / v3 / c a l e n d a r s /←$calendar_id / events / $event_id " ; i f ( $objectType == " c a l e n d a r " ) { $URL = " h t t p s : / /www. g o o g l e a p i s . com/ c a l e n d a r / v3 / c a l e n d a r s /←$calendar_id " ; } 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 return t r u e ; $session = c u r l _ i n i t ( ) ; c u r l _ s e t o p t ( $session , CURLOPT_URL , $URL ) ; c u r l _ s e t o p t ( $session , CURLOPT_CUSTOMREQUEST , "DELETE" ) ; c u r l _ s e t o p t ( $session , CURLOPT_SSL_VERIFYPEER , f a l s e ) ; $curlheader [ 0 ] = " A u t h o r i z a t i o n : OAuth " . $aToken ; c u r l _ s e t o p t ( $session , CURLOPT_HTTPHEADER , $curlheader ) ; c u r l _ e x e c ( $session ) ; $response = c u r l _ g e t i n f o ( $session ) ; c u r l _ c l o s e ( $session ) ; } return $response [ ' http_code ' ] ; // E l i m i n a r a e l c a l e n d a r i o que s e i n d i q u e . D e v o l v e r a v e r d a d e r o s i s e ←ha e l i m i n a d o e l e v e n t o y f a l s o en c a s o c o n t r a r i o . function delete Calendar ( $calendar_id ) { require_once ' . / s r c / a p i C l i e n t . php ' ; require_once ' . / s r c / c o n t r i b / a p i C a l e n d a r S e r v i c e . php ' ; global $api_key ; global $accessToken ; global $refreshToken ; global $redirect_uri ; global $clien t_secret ; global $client_id ; global $scope ; $client = new apiClient ( ) ; $client−>setScopes ( $scope ) ; $client−>setClientId ( $client_id ) ; $client−>s et Cl ie n tS ec re t ( $cli ent_secr et ) ; $client−>setR edirectU ri ( $redirect_uri ) ; $client−>s et De ve l op er Ke y ( $api_key ) ; $client−>refreshToken ( $refreshToken ) ; $responseObj = json_decode ( $client−>getA ccessTok en ( ) ) ; A.4. Apéndice IV 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 i f ( i s s e t ( $responseObj−>access_token ) ) { $response = s e n d _ d e l e t e _ q u e r y ( " " , $responseObj−>access_token , ←$calendar_id , " c a l e n d a r " ) ; echo "DEBUG: DELETE r e s p o n s e code : $ r e s p o n s e <br />" ; i f ( $response != " 204 " ) { return f a l s e ; } } else { return f a l s e ; } } return t r u e ; // Envia una p e t i c i o n HTTP a Google para m o d i f i c a r d a t o s en Google ←C a l e n d a r . Los d a t o s a m o d i f i c a r dependeran d e l t i p o que s e hayan ←e s p e c i f i c a d o en l a URI u t i l i z a d a . El dato que d e v u e l v e l a f u n c i o n ←por d e f e c t o s e r a un c o d i g o HTTP de r e s p u e s t a , d e v o l v i e n d o ' 2 0 0 ' ←como o p e r a c i o n c o r r e c t a . function send_p ut_query ( $postArgs , $aToken , $URL ) { $session = c u r l _ i n i t ( ) ; c u r l _ s e t o p t ( $session , CURLOPT_URL , $URL ) ; c u r l _ s e t o p t ( $session , CURLOPT_CUSTOMREQUEST , "PUT" ) ; c u r l _ s e t o p t ( $session , CURLOPT_SSL_VERIFYPEER , f a l s e ) ; c u r l _ s e t o p t ( $session , CURLOPT_RETURNTRANSFER , 1 ) ; $curlheader [ 0 ] = " Content−Type : a p p l i c a t i o n / j s o n " ; $curlheader [ 1 ] = " A u t h o r i z a t i o n : OAuth " . $aToken ; c u r l _ s e t o p t ( $session , CURLOPT_HTTPHEADER , $curlheader ) ; c u r l _ s e t o p t ( $session , CURLOPT_POSTFIELDS , $postArgs ) ; c u r l _ e x e c ( $session ) ; $response = c u r l _ g e t i n f o ( $session ) ; c u r l _ c l o s e ( $session ) ; } return $response [ ' http_code ' ] ; function loadEvents ( $startDate , $endDate , $calendar_id ) { global $accessToken ; $book_query = " h t t p s : / /www. g o o g l e a p i s . com/ c a l e n d a r / v3 / c a l e n d a r s /←$ c a l e n d a r _ i d / e v e n t s ? orderBy=s t a r t T i m e&s i n g l e E v e n t s=t r u e&←timeMin=$ s t a r t D a t e&timeMax=$endDate " ; } return send_g et_query ( $book_query , $accessToken ) ; // Cre ara un c a l e n d a r i o en e l que s e pueda i n s e r t a r h o r a r i o s de ←t u t o r i a s d e l p r o f e s o r . D e v o l v e r a e l ID d e l c a l e n d a r i o c r e a d o , ←cadena v a c i a en c a s o de e r r o r . function i n s e r t N e w T u t o r i n g C a l e n d a r ( ) { require_once ' . / s r c / a p i C l i e n t . php ' ; require_once ' . / s r c / c o n t r i b / a p i C a l e n d a r S e r v i c e . php ' ; global $api_key ; global $ j s o n _ a c c e s s T o k e n ; global $accessToken ; 81 82 Apéndices 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 global global global global global $refreshToken ; $redirect_uri ; $clien t_secret ; $client_id ; $scope ; $client = new apiClient ( ) ; $client−>setScopes ( $scope ) ; $client−>setClientId ( $client_id ) ; $client−>s et Cl ie n tS ec re t ( $cli ent_secr et ) ; $client−>setR edirectU ri ( $redirect_uri ) ; $client−>s et De ve l op er Ke y ( $api_key ) ; $client−>refreshToken ( $refreshToken ) ; { $ pa rt 1_ p or tA rg s = <<<JSON " summary " : " T u t o r i a s − HORARIOS" , " timeZone " : " Europe / Madrid " } JSON ; { $ pa rt 2_ p or tA rg s = <<<JSON " c o l o r I d " : " 10 " , " s e l e c t e d " : true , " defaultReminders " : [ { " method " : " e m a i l " , " m in ut e s " : 1440 } ] } JSON ; { $ pa rt 3_ p or tA rg s = <<<JSON " role " : " reader " , " scope " : { " type " : " d e f a u l t " , " v a l u e " : " __public_principal__@public . c a l e n d a r . g o o g l e . com " }, " k i n d " : " c a l e n d a r#a c l R u l e " , " id " : " default " } JSON ; $calendar_id = " " ; $responseObj = json_decode ( $client−>getA ccessTok en ( ) ) ; i f ( i s s e t ( $responseObj−>access_token ) ) { echo "DEBUG: Creando c a l e n d a r i o de t u t o r i a s . . . < br />" ; $URL = " h t t p s : / /www. g o o g l e a p i s . com/ c a l e n d a r / v3 / c a l e n d a r s " ; $calendar_id = s en d_ po s t_ qu er y ( $part1_portArgs , $responseObj−>←access_token , $URL , " i d " ) ; echo "DEBUG: C a l e n d a r ID r e s p o n s e : $ c a l e n d a r _ i d <br />" ; i f ( s t r l e n ( $calendar_id ) == 0 ) { return " " ; } A.4. Apéndice IV 539 540 echo "DEBUG: C o n f i g u r a n d o c a l e n d a r i o de t u t o r i a s ( 1 de 2 ) . . . < ←br />" ; $URL = " h t t p s : / /www. g o o g l e a p i s . com/ c a l e n d a r / v3 / u s e r s /me/←calendarList / $calendar_id " ; $response = send _put_que ry ( $part2_portArgs , $responseObj−>←access_token , $URL ) ; echo "DEBUG: POST r e s p o n s e code : $ r e s p o n s e <br />" ; i f ( $response != " 200 " ) { echo "DEBUG: E r r o r d u r a n t e e l p r o c e s o , b o r r a n d o e l ←c a l e n d a r i o i n c o m p l e t o . . . < br />" ; dele teCalend ar ( $calendar_id ) ; 541 542 543 544 545 546 547 548 549 550 551 552 } echo "DEBUG: C o n f i g u r a n d o c a l e n d a r i o de t u t o r i a s ( 2 de 2 ) . . . < ←br />" ; $URL = " h t t p s : / /www. g o o g l e a p i s . com/ c a l e n d a r / v3 / c a l e n d a r s /←$calendar_id / ac l " ; $response = s en d_ po s t_ qu er y ( $part3_portArgs , $responseObj−>←access_token , $URL ) ; echo "DEBUG: POST r e s p o n s e code : $ r e s p o n s e <br />" ; i f ( $response != " 200 " ) { echo "DEBUG: E r r o r d u r a n t e e l p r o c e s o , b o r r a n d o e l ←c a l e n d a r i o i n c o m p l e t o . . . < br />" ; dele teCalend ar ( $calendar_id ) ; 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 return " " ; } else { } } } return " " ; return " " ; return $calendar_id ; // Cre ara un c a l e n d a r i o para que s e pueda i n s e r t a r r e s e r v a s d e l ←h o r a r i o de t u t o r i a s d e l p r o f e s o r . D e v o l v e r a e l ID d e l c a l e n d a r i o ←c r e a d o , cadena v a c i a en c a s o de e r r o r . function i n s e r t N e w B o o k i n g C a l e n d a r ( ) { require_once ' . / s r c / a p i C l i e n t . php ' ; require_once ' . / s r c / c o n t r i b / a p i C a l e n d a r S e r v i c e . php ' ; global $api_key ; global $ j s o n _ a c c e s s T o k e n ; global $accessToken ; global $refreshToken ; global $redirect_uri ; global $clien t_secret ; global $client_id ; global $scope ; $client = new apiClient ( ) ; $client−>setScopes ( $scope ) ; $client−>setClientId ( $client_id ) ; $client−>s et Cl ie n tS ec re t ( $cli ent_secr et ) ; 83 84 Apéndices 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 $client−>setR edirectU ri ( $redirect_uri ) ; $client−>s et De ve l op er Ke y ( $api_key ) ; $client−>refreshToken ( $refreshToken ) ; { $ pa rt 1_ p or tA rg s = <<<JSON " summary " : " T u t o r i a s − RESERVAS" , " timeZone " : " Europe / Madrid " } JSON ; { $ pa rt 2_ p or tA rg s = <<<JSON " colorId " : "2" , " s e l e c t e d " : true , " defaultReminders " : [ { " method " : " e m a i l " , " m in ut e s " : 1440 } ] } JSON ; { $ pa rt 3_ p or tA rg s = <<<JSON " r o l e " : " freeBusyReader " , " scope " : { " type " : " d e f a u l t " , " v a l u e " : " __public_principal__@public . c a l e n d a r . g o o g l e . com " }, " k i n d " : " c a l e n d a r#a c l R u l e " , " id " : " default " } JSON ; $calendar_id = " " ; $responseObj = json_decode ( $client−>getA ccessTok en ( ) ) ; i f ( i s s e t ( $responseObj−>access_token ) ) { echo "DEBUG: Creando c a l e n d a r i o de r e s e r v a s . . . < br />" ; $URL = " h t t p s : / /www. g o o g l e a p i s . com/ c a l e n d a r / v3 / c a l e n d a r s " ; $calendar_id = s en d_ po s t_ qu er y ( $part1_portArgs , $responseObj−>←access_token , $URL , " i d " ) ; echo "DEBUG: C a l e n d a r ID r e s p o n s e : $ c a l e n d a r _ i d <br />" ; i f ( s t r l e n ( $calendar_id ) == 0 ) { return " " ; } echo "DEBUG: C o n f i g u r a n d o c a l e n d a r i o de r e s e r v a s ( 1 de 2 ) . . . < ←br />" ; $URL = " h t t p s : / /www. g o o g l e a p i s . com/ c a l e n d a r / v3 / u s e r s /me/←calendarList / $calendar_id " ; $response = send _put_que ry ( $part2_portArgs , $responseObj−>←access_token , $URL ) ; echo "DEBUG: POST r e s p o n s e code : $ r e s p o n s e <br />" ; i f ( $response != " 200 " ) { A.4. Apéndice IV 648 echo "DEBUG: E r r o r d u r a n t e e l p r o c e s o , b o r r a n d o e l ←c a l e n d a r i o i n c o m p l e t o . . . < br />" ; dele teCalend ar ( $calendar_id ) ; 649 650 651 652 653 654 } echo "DEBUG: C o n f i g u r a n d o c a l e n d a r i o de r e s e r v a s ( 2 de 2 ) . . . < ←br />" ; $URL = " h t t p s : / /www. g o o g l e a p i s . com/ c a l e n d a r / v3 / c a l e n d a r s /←$calendar_id / ac l " ; $response = s en d_ po s t_ qu er y ( $part3_portArgs , $responseObj−>←access_token , $URL ) ; echo "DEBUG: POST r e s p o n s e code : $ r e s p o n s e <br />" ; i f ( $response != " 200 " ) { echo "DEBUG: E r r o r d u r a n t e e l p r o c e s o , b o r r a n d o e l ←c a l e n d a r i o i n c o m p l e t o . . . < br />" ; dele teCalend ar ( $calendar_id ) ; 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 return " " ; } else { } } } return " " ; return " " ; return $calendar_id ; // S a l v a en un f i c h e r o t o d o s l o s d a t o s de c o n f i g u r a c i o n n e c e s a r i o s ←para que e l s i s t e m a de r e s e r v a s pueda a c c e d e r a l o s c a l e n d a r i o s ←del profesor . function f i l l _ c a l e n d a r O w n e r _ c o n f i g ( $ c a l O w n e r F i l e N a m e ) { $oFile = f o p e n ( $calOwnerFileName , 'w ' ) or d i e ( " E r r o r : No s e puede ←a b r i r e l f i c h e r o ' $calOwnerFileName ' para e s c r i t u r a .< br />" ) ; global global global global global global global global global global global global global global $calendarOwnerOffice ; $client_id ; $clien t_secret ; $redirect_uri ; $scope ; $state ; $access_type ; $tutoring_calendar_id ; $book_calendar_id ; $userlogin_client_id ; $userlogin_client_secret ; $userlogin_redirect_uri ; $ u s er l o g i n _ s c o p e ; $api_key ; $dataToSave = '<?php ' . PHP_EOL . ' // Datos p e r s o n a l e s d e l t u t o r n e c e s a r i o s ' . PHP_EOL . ' $ c a l e n d a r O w n e r O f f i c e = " ' . $ c a l e n d a r O w n e r O f f i c e . ' " ; // Despacho ←d e l t u t o r ' . PHP_EOL . ' $ t u t o r i n g _ c a l e n d a r _ i d = " ' . $ t u t o r i n g _ c a l e n d a r _ i d . ' " ; // ←C a l e n d a r i o de t u t o r i a s d i s p o n i b l e ' . PHP_EOL . 85 86 Apéndices 699 ' $book_calendar_id = " ' . $ b o o k _ c a l e n d a r _ i d . ' " ; // L i s t a de r e s e r v a s ←r e a l i z a d a s ' . PHP_EOL . ' ' . PHP_EOL . ' // Parametros d e l u s u a r i o para e l URL de a c c e s o ' . PHP_EOL . ' $ c l i e n t _ i d = " ' . $client_id . ' " ; ' . PHP_EOL . ' $ c l i e n t _ s e c r e t = " ' . $cli ent_secr et . ' " ; ' . PHP_EOL . ' $ r e d i r e c t _ u r i = " ' . $redirect_uri . ' " ; ' . PHP_EOL . ' $ s c o p e = " ' . $scope . ' " ; ' . PHP_EOL . ' $ s t a t e = " ' . $state . ' " ; // O p c i o n a l − puede s e r c u a l q u i e r v a l o r ←que q u i e r a s ' . PHP_EOL . ' $ a c c e s s _ t y p e = " ' . $access_type . ' " ; // No cambiar e l v a l o r " ←o f f l i n e " , n e c e s a r i o para l a r e c u p e r a c i o n d e l " r e f r e s h _ t o k e n " ' . ←PHP_EOL . ' ' . PHP_EOL . ' // Parametros d e l u s u a r i o para e l a c c e s o de u s u a r i o s l o g e a d o s ' . ←PHP_EOL . ' $ u s e r l o g i n _ c l i e n t _ i d = " ' . $ u s e r l o g i n _ c l i e n t _ i d . ' " ; ' . PHP_EOL . ' $ u s e r l o g i n _ c l i e n t _ s e c r e t = " ' . $ u s e r l o g i n _ c l i e n t _ s e c r e t . ' " ; ' . ←PHP_EOL . ' $ u s e r l o g i n _ r e d i r e c t _ u r i = " ' . $ u s e r l o g i n _ r e d i r e c t _ u r i . ' " ; ' . PHP_EOL←. ' $ u s e r l o g i n _ s c o p e = " ' . $ u s e r l o g i n _ sc o p e . ' " ; ' . PHP_EOL . ' ' . PHP_EOL . ' // API de a c c e s s o s i m p l e ' . PHP_EOL . ' $api_key = " ' . $api_key . ' " ; ' . PHP_EOL . ' ?> ' . PHP_EOL . ' ' . PHP_EOL ; 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 fwrite ( $oFile , $dataToSave ) ; } f c l o s e ( $oFile ) ; // Devuelve l a f e c h a d e l p r i m e r d i a de l a semana function weekStartDate ( $wk_num , $yr , $first = 1 , $format = "Y−m−d\TH: i ←: s \Z " ) { $wk_ts = s t r t o t i m e ( '+ ' . $wk_num . ' weeks ' , s t r t o t i m e ( $yr . ' 0101 ' ) ) ; $mon_ts = s t r t o t i m e ( '− ' . d a t e ( 'w ' , $wk_ts ) + $first . ' days ' , $wk_ts←); } return d a t e ( $format , $mon_ts ) ; // Devuelve l a f e c h a d e l u l t i m o d i a de l a semana function weekEndDate ( $wk_num , $yr , $first = 7 , $format = "Y−m−d\TH: i : s ←\Z " ) { return weekStartDate ( $wk_num , $yr , $first , $format ) ; } ?> A.4. Apéndice IV Código fuente de: index.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 <! DOCTYPE html> <html> <head> <meta charset=iso −8859−15> <body> <script type=" t e x t / j a v a s c r i p t "> // V e r i f i c a s i l a s h o r a s de i n i c i o y f i n a l son c o h e r e n t e s , en c a s o ←c o n t r a r i o c o r r i g e l a hora f i n a l con r e s p e c t o a l a de i n c i o . function checkB ookTimes ( divFrom , divTo ) { var df = document . getE lementBy Id ( divFrom ) ; var dt = document . getE lementBy Id ( divTo ) ; } i f ( df . options [ df . selectedIndex ] . value >= dt . options [ dt . ←selectedIndex ] . value ) { dt . selectedIndex = df . selectedIndex ; } // Prepara l o s campos de f o r m u l a r i o u t i l i z a d o s por e l s e r v i d o r function createNewBook ( half_id ) { var df = document . getE lementBy Id ( ' from_ ' + half_id ) ; var dt = document . getE lementBy Id ( ' to_ ' + half_id ) ; var desc = document . getE lementBy Id ( ' desc_ ' + half_id ) ; var form_dtf = document . getE lementBy Id ( ' dateTimeFrom ' ) ; var form_dtt = document . getE lementBy Id ( ' dateTimeTo ' ) ; var form_desc = document . getE lementBy Id ( ' d e s c r i p t i o n ' ) ; var form_action = document . getE lementBy Id ( ' a c t i o n ' ) ; form_dtf . value = df . options [ df . selectedIndex ] . value ; form_dtt . value = dt . options [ dt . selectedIndex ] . value ; form_desc . value = desc . value ; form_action . value = ' c r e a t e _ b o o k ' ; } document . booking_menu . submit ( ) ; // Prepara l o s campos de f o r m u l a r i o u t i l i z a d o s por e l s e r v i d o r function deleteBook ( half_id ) { var eid = document . getE lementBy Id ( ' eid_ ' + half_id ) ; var form_action = document . getE lementBy Id ( ' a c t i o n ' ) ; var form_eid = document . getE lementBy Id ( ' e v e n t _ i d ' ) ; form_eid . value = eid . value ; form_action . value = ' d e l e t e _ b o o k ' ; } document . booking_menu . submit ( ) ; // Dada una URL c a r g a una nueva p a g i n a function loadUrl ( location ) { window . location . href = location ; } </script> 87 88 Apéndices 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 <?php if { ( ! session_id () ) session_start () ; } if { ( ! i s s e t ( $calendarOwnerFile ) ) } $ c a l e n d a r O w n e r F i l e = ' p r o f e s o r . t e s t s @ g m a i l . com ' ; $ au to lo g in _d on e = f a l s e ; i f ( i s s e t ( $ c a l e n d a r O w n e r F i l e ) && s t r l e n ( $ c a l e n d a r O w n e r F i l e ) > 0 && ←f i l e _ e x i s t s ( " $ c a l e n d a r O w n e r F i l e . php " ) ) { include_once ( " $ c a l e n d a r O w n e r F i l e . php " ) ; } else { d i e ( " El t u t o r con l a c u e n t a de c o r r e o \ " $ c a l e n d a r O w n e r F i l e \ " no ha←a c t i v a d o t o d a v i a e l s i s t e m a de r e s e r v a s para t u t o r i a s . E s p e r e ←a que e l t u t o r a c t i v e e l s e r v i c i o o s e l e c c i o n e o t r o t u t o r . " ) ; } // Se p r o c e d e a l a a u t e n t i c a c i o n d e l v i s i t a n t e include_once ( " c a l e n d a r _ u s e r l o g i n . php " ) ; // Se i n c l u y e n f u n c i o n e s de ambito g e n e r a l include_once ( " o t h e r _ f u n c t i o n s . php " ) ; // S i s e ha c a r g a d o con e x i t o l a c u e n t a d e l t u t o r y s e ha c o n s u l t a d o ←l o s c a l e n d a r i o s de t u t o r i a s e p e r m i t e e l a c c e s o de u s u a r i o s a l ←s i s t e m a de r e s e r v a s . $calendarOwnerLogged = f a l s e ; echo "<h2>CUENTA DE USUARIO</h2>" ; i f ( ! $ us er lo g in _d on e ) { p r i n t "<a c l a s s =' l o g i n ' h r e f =' $ a u t h U r l '> Entrar </a><br />" ; } else { i f ( count ( g et Re fr e sh To ke n ( $_SESSION [ ' a c c e s s _ t o k e n ' ] ) ) > 0 ) { $client−>refreshToken ( g et Re fr e sh To ke n ( $_SESSION [ ' a c c e s s _ t o k e n ' ←]) ) ; $_SESSION [ ' a c c e s s _ t o k e n ' ] = $client−>getA ccessToke n ( ) ; } $userInfo = g e t C l i e n t U s e r I n f o ( $client−>getA ccessTok en ( ) ) ; echo " U s u a r i o a u t e n t i c a d o : " . utf8_decode ( $userInfo−>name ) . "<br />←Cor reo : " . utf8_decode ( $userInfo−>email ) . "<br />" ; p r i n t "<a c l a s s =' l o g o u t ' h r e f = '? l o g o u t '> S a l i r </a><br />" ; i f ( i s s e t ( $ t u t o r i n g _ c a l e n d a r _ i d ) && i s s e t ( $ b o o k _ c a l e n d a r _ i d ) && ←s t r l e n ( $ t u t o r i n g _ c a l e n d a r _ i d ) > 0 && s t r l e n ( $ b o o k _ c a l e n d a r _ i d ) ←> 0) { // Se p r o c e d e a l a a u t e n t i c a c i o n d e s a t e n d i d a d e l t u t o r para l a ←c a r g a d e l c a l e n d a r i o de r e s e r v a s . A.4. Apéndice IV 109 110 111 112 113 114 115 116 } include_once ( " c a l e n d a r _ a u t o l o g i n . php " ) ; i f ( $ c a l e n d a r O w n e r F i l e == $userInfo−>email ) { $calendarOwnerLogged = t r u e ; echo "<br /><s t r o n g >A d m i n i s t r a c i o n d e l c a l e n d a r i o a c t i v a d a </←s t r o n g ><br />" ; 117 118 // Se p r o c e d e a l a a u t e n t i c a c i o n d e s a t e n d i d a d e l t u t o r para l a ←c a r g a d e l c a l e n d a r i o de r e s e r v a s . include_once ( " c a l e n d a r _ a u t o l o g i n . php " ) ; 119 120 121 i f ( s t r l e n ( $accessToken ) == 0 | | ! ( i s s e t ( $ t u t o r i n g _ c a l e n d a r _ i d ) ←&& i s s e t ( $ b o o k _ c a l e n d a r _ i d ) && s t r l e n ( ←$ t u t o r i n g _ c a l e n d a r _ i d ) > 0 && s t r l e n ( $ b o o k _ c a l e n d a r _ i d ) > ←0) ) { $loginUrl = ' h t t p s : / / a c c o u n t s . g o o g l e . com/ o / oauth2 / auth ?←s c o p e= ' . $scope . '&s t a t e= ' . $state . '&r e d i r e c t _ u r i= ' . ←$redirect_uri . '&r e s p o n s e _ t y p e=code&c l i e n t _ i d= ' . ←$client_id . '&a c c e s s _ t y p e= ' . $access_type ; 122 123 124 125 echo '<h2>AVISO : Aun no e s t a c o n f i g u r a d a l a c u e n t a d e l ←t u t o r . </h2> <p><a c l a s s =" bu tt on " h r e f =" ' . $loginUrl . ' "> A c t i v a r l a ←c u e n t a d e l t u t o r \ ' ' . $ c a l e n d a r O w n e r F i l e . ' \ ' </a></p> ' ; 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 } else { } } $ au to lo g in _d on e = f a l s e ; $calendarOwnerLogged = f a l s e ; } // En c a s o de haber e n v i a d o un f o r m u l a r i o a l s e r v i d o r a t e n d e r l a para ←que f u t u r a s p e t i c i o n e s a l o s c a l e n d a r i o s e s t e n a c t u l i z a d a s . i f ( $_SERVER [ 'REQUEST_METHOD' ] == 'POST ' ) { i f ( i s s e t ( $_POST [ ' a c t i o n ' ] ) ) { s w i t c h ( $_POST [ ' a c t i o n ' ] ) { case ' delete_book ' : i f ( i s s e t ( $_POST [ ' e v e n t _ i d ' ] ) ) { deleteEvent ( $_POST [ ' e v e n t _ i d ' ] ) ; } break ; case ' create_book ' : i f ( i s s e t ( $_POST [ ' dateTimeFrom ' ] ) && i s s e t ( $_POST [ ' ←dateTimeTo ' ] ) ) { inse rtNewEve nt ( gmdate ( "Y−m−d\TH: i : sP " , $_POST [ ' ←dateTimeFrom ' ] ) , gmdate ( "Y−m−d\TH: i : sP " , $_POST←- 89 90 Apéndices 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 } break ; } } } [ ' dateTimeTo ' ] ) , $calendarOwnerOffice , $_POST [ ←' d e s c r i p t i o n ' ] , $userInfo−>name , $userInfo−>←email ) ; default : // I g n o r a r p e t i c i o n break ; i f ( $ au to lo g in _d on e ) { // S e l e c c i o n a n d o rango de f e c h a s que abarquen l a semana c o m p l e t a i f ( i s s e t ( $_GET [ " week " ] ) && preg_match ( ' /^\d+$ / ' , $_GET [ ' week ' ] ) ) { // Una semana s o l i c i t a d a en l a URL $wee k_select ed = $_GET [ " week " ] − 1 ; $yea r_select ed = i n t v a l ( d a t e ( "Y" ) ) ; i f ( $wee k_select ed < ( d a t e ( "W" ) − 1 ) ) { // En c a s o de s o l i c i t a r semanas a n t e r i o r e s a l a a c t u a l s e ←devuelve la actual . $wee k_select ed = d a t e ( "W" ) − 1 ; } } else { w h i l e ( $wee k_select ed > 5 1 ) { $wee k_select ed −= 5 2 ; $yea r_select ed++; } // Semana a c t u a l $wee k_select ed = d a t e ( "W" ) − 1 ; $yea r_select ed = d a t e ( "Y" ) ; } $startDate = weekStartDate ( $week_selected , $yea r_select ed ) ; $endDate = weekEndDate ( $week_selected , $yea r_select ed ) ; // Cargando l i s t a s de e v e n t o s , s i hay $book_hours = loadEvents ( $startDate , $endDate , $ b o o k _ c a l e n d a r _ i d ) ; $ tu to ri n g_ ho ur s = loadEvents ( $startDate , $endDate , ←$tutoring_calendar_id ) ; // $ c u r r e n t D a t e = d a t e ( " Y−m−d\TH: i : s \Z " ) ; $strWeekDays = a r r a y ( 1 => " Lunes " , 2 => " Martes " , 3 => " M i e r c o l e s " ←, 4 => " J u e v e s " , 5 => " V i e r n e s " , 6 => " Sabado " , 0 => " Domingo " ←); // L i s t a c o m p l e t a de t u t o r i a s de l a semana $tutoringList = s o r t T u t o r i n g _ h o u r s ( $ tu to ri n g_ ho ur s ) ; // L i s t a de r e s e r v a s de l a semana $bookList = sort Book_hou rs ( $book_hours ) ; A.4. Apéndice IV 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 91 echo "<br />−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−<br←/>" ; echo "<h2>R e s e r v a s de h o r a r i o para t u t o r i a s </h2>" ; p r i n t "<h4>Semana " . ( $wee k_select ed + 1 ) . " , d e l " . d a t e ( " d−m−Y" , ←s t r t o t i m e ( $startDate ) ) . " a l " . d a t e ( " d−m−Y" , s t r t o t i m e ( $endDate←) ) . "</h4>" ; // Anyadir b o t o n e s de n a v e g a c i o n para d e s p l a z a r s e e n t r e l a s ←semanas d e l c a l e n d a r i o . echo d r a w W e e k B r o w s e r B u t t o n s ( $week_selected , $yea r_select ed ) . "<br ←/>" ; echo '<form name="booking_menu " method=" p o s t " a c t i o n =" ' . ←h t m l e n t i t i e s ( $_SERVER [ 'PHP_SELF ' ] ) . ' "> ' ; echo '<i n p u t t y p e =" hi dd en " name="dateTimeFrom " i d ="dateTimeFrom " ←v a l u e ="" /> ' ; echo '<i n p u t t y p e =" hi dd en " name="dateTimeTo " i d ="dateTimeTo " v a l u e ←="" /> ' ; echo '<i n p u t t y p e =" hi dd en " name=" d e s c r i p t i o n " i d =" d e s c r i p t i o n " ←v a l u e ="" /> ' ; echo '<i n p u t t y p e =" hi dd en " name=" e v e n t _ i d " i d =" e v e n t _ i d " v a l u e ="" ←/> ' ; echo '<i n p u t t y p e =" hi dd en " name=" a c t i o n " i d =" a c t i o n " v a l u e ="" /> ' ; f o r ( $i = 0 ; $i < 7 ; $i++) { i f ( i s s e t ( $tutoringList [ $i % 7 ] ) && ( s t r t o t i m e ( d a t e ( "Y−m−d " , ←s t r t o t i m e ( $tutoringList [ $i % 7][0] − > start−>dateTime ) ) ) >= ←s t r t o t i m e ( d a t e ( "Y−m−d " ) ) ) ) { echo "<h4>− " . $strWeekDays [ $i % 7 ] . " " . d a t e ( " j " , s t r t o t i m e ←( $tutoringList [ $i % 7][0] − > start−>dateTime ) ) . " , ←tutorias : " ; f o r ( $j = 0 ; $j < count ( $tutoringList [ $i % 7 ] ) ; $j++) { i f ( $j > 0 ) { echo " , " ; } echo " [ " . d a t e ( "H: i " , s t r t o t i m e ( $tutoringList [ $i % 7 ] [ ←$j]−>start−>dateTime ) ) . "−" . d a t e ( "H: i " , s t r t o t i m e ( ←$tutoringList [ $i % 7 ] [ $j]−>end−>dateTime ) ) . " ] " ; } echo "</h4>" ; // $m $n if { L i s t a de h u e c o s l i b r e s / r e s e r v a s : = 0; = 0; ( i s s e t ( $bookList [ $i % 7 ] ) ) $AllSlotsList = a r r a y ( ) ; f o r ( $l = 0 ; $l < count ( $tutoringList [ $i % 7 ] ) ; $l++) { $AllSlotsList [ ] = findAllSlots ( $tutoringList [ $i % ←7 ] [ $l]−>start−>dateTime , $tutoringList [ $i % ←7 ] [ $l]−>end−>dateTime , $bookList [ $i % 7 ] ) ; } } f o r ( $m = 0 ; $m < count ( $AllSlotsList ) ; $m++) 92 Apéndices 256 257 258 259 260 261 { 262 263 f o r ( $n = 0 ; $n < count ( $AllSlotsList [ $m ] ) ; $n++) { i f ( count ( $AllSlotsList [ $m ] [ $n ] ) == 2 ) { echo " [ " . gmdate ( "H: i " , s t r t o t i m e ( $AllSlotsList←[ $m ] [ $n ] [ 0 ] ) ) . "−" . gmdate ( "H: i " , s t r t o t i m e ( ←$AllSlotsList [ $m ] [ $n ] [ 1 ] ) ) . " ] LIBRE" ; 264 265 266 267 268 269 270 271 } else { 272 273 i f ( $ us er lo g in _d on e && ! isOldEvent ( ←$AllSlotsList [ $m ] [ $n ] [ 1 ] ) ) { echo " " . d r a w N e w B o o k B u t t o n ( s t r t o t i m e ( ←$AllSlotsList [ $m ] [ $n ] [ 0 ] ) , s t r t o t i m e ( ←$AllSlotsList [ $m ] [ $n ] [ 1 ] ) ) ; } echo "<br />" ; echo " [ " . d a t e ( "H: i " , s t r t o t i m e ( $AllSlotsList [ ←$m ] [ $n]−>start−>dateTime ) ) . "−" . d a t e ( "H: i " , ←s t r t o t i m e ( $AllSlotsList [ $m ] [ $n]−>end−>←dateTime ) ) . " ] RESERVADO" ; if 274 275 { 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 } 296 297 298 299 } } } ( ! isOldEvent ( $AllSlotsList [ $m ] [ $n]−>start−>←dateTime ) && ( $ c a l e n d a r O w n e r L o g g e d | | ( ←i s s e t ( $AllSlotsList [ $m ] [ $n]−>←extendedProperties−>private−>booker_email ) ←&& i s s e t ( $userInfo−>email ) && ←$AllSlotsList [ $m ] [ $n]−>extendedProperties ←−>private−>booker_email == $userInfo−>←email ) ) ) echo " " . d r a w C a n c e l B ut t o n ( $AllSlotsList [ $m←] [ $n]−>id ) ; echo "<br />" ; } } echo '</form> ' ; } echo "<br />−−−−−−−−−−−−−−−−−<br />" ; i f ( i s s e t ( $ c a l e n d a r O w n e r F i l e ) && s t r l e n ( $ c a l e n d a r O w n e r F i l e ) > 0 && ←i s s e t ( $ t u t o r i n g _ c a l e n d a r _ i d ) && s t r l e n ( $ t u t o r i n g _ c a l e n d a r _ i d ) > 0 ←&& i s s e t ( $ b o o k _ c a l e n d a r _ i d ) && s t r l e n ( $ b o o k _ c a l e n d a r _ i d ) > 0 ) { ?> <br /> <h4>Calendario del tutor <?php echo $ c a l e n d a r O w n e r F i l e ; ?></h4> A.4. Apéndice IV 300 301 302 303 304 305 306 <iframe src=" h t t p s : / /www. g o o g l e . com/ c a l e n d a r /embed? s h o w T i t l e=0& ; ←showTabs=0& ; showCalendars=0& ; showTz=0& ; mode=WEEK& ; ←h e i g h t =600& ; wkst=2& ; h l=e s& ; b g c o l o r= %23FFFFFF& ; s r c =<?←php echo $ t u t o r i n g _ c a l e n d a r _ i d ; ?>& ; c o l o r= %232F6213& ; s r c =<?←php echo $book_calendar_id ; ?>& ; c o l o r = %23691426& ; c t z=Europe←%2FMadrid " style=" b o r d e r −width : 0 " width=" 900 " height=" 1100 " ←frameborder=" 0 " scrolling=" no "></iframe> <?php } ?> </body> </html> 93 94 Apéndices Código fuente de: admin_new_tutor.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <! DOCTYPE html> <html> <head> <meta charset=iso −8859−15> <body> <h2>Admi nistraci on del sistema de reservas : dar de alta nuevo tutor </←h2> <?php // DATOS NECESARIOS DEL TUTOR $tokenFileExt = ' . r t o k e n ' ; $ c a l e n d a r O w n e r F i l e = ' p r o f e s o r . t e s t s @ g m a i l . com ' ; i f ( $_SERVER [ 'REQUEST_METHOD' ] == 'POST ' ) { i f ( i s s e t ( $_POST [ ' calendarOwnerEmail ' ] ) && i s s e t ( $_POST [ ' c a l e n d a r O w n e r O f f i c e ' ] ) && i s s e t ( $_POST [ ' a u t o l o g i n _ c i d ' ] ) && i s s e t ( $_POST [ ' a u t o l o g i n _ s e c r e t ' ] ) && i s s e t ( $_POST [ ' a u t o l o g i n _ r e d i r e c t ' ] ) && i s s e t ( $_POST [ ' a u t o l o g i n _ s c o p e ' ] ) && i s s e t ( $_POST [ ' u s e r l o g i n _ c i d ' ] ) && i s s e t ( $_POST [ ' u s e r l o g i n _ s e c r e t ' ] ) && i s s e t ( $_POST [ ' u s e r l o g i n _ r e d i r e c t ' ] ) && i s s e t ( $_POST [ ' u s e r l o g i n _ s c o p e ' ] ) && i s s e t ( $_POST [ ' api_key ' ] ) ) { i f ( s t r l e n ( $_POST [ ' calendarOwnerEmail ' ] ) > 0 && s t r l e n ( $_POST [ ' c a l e n d a r O w n e r O f f i c e ' ] ) > 0 && s t r l e n ( $_POST [ ' a u t o l o g i n _ c i d ' ] ) > 0 && s t r l e n ( $_POST [ ' a u t o l o g i n _ s e c r e t ' ] ) > 0 && s t r l e n ( $_POST [ ' a u t o l o g i n _ s c o p e ' ] ) > 0 && s t r l e n ( $_POST [ ' u s e r l o g i n _ c i d ' ] ) > 0 && s t r l e n ( $_POST [ ' u s e r l o g i n _ s e c r e t ' ] ) > 0 && s t r l e n ( $_POST [ ' u s e r l o g i n _ s c o p e ' ] ) > 0 && s t r l e n ( $_POST [ ' api_key ' ] ) > 0 ) { w r i t e _ c a l e n d a r O w n e r _ c o n f i g ( $_POST [ ' calendarOwnerEmail ' ] . " . ←php " , $_POST [ ' c a l e n d a r O w n e r O f f i c e ' ] , $_POST [ ' ←a u t o l o g i n _ c i d ' ] , $_POST [ ' a u t o l o g i n _ s e c r e t ' ] , $_POST [ ' ←a u t o l o g i n _ r e d i r e c t ' ] , $_POST [ ' a u t o l o g i n _ s c o p e ' ] , ←$_POST [ ' u s e r l o g i n _ c i d ' ] , $_POST [ ' u s e r l o g i n _ s e c r e t ' ] , ←$_POST [ ' u s e r l o g i n _ r e d i r e c t ' ] , $_POST [ ' u s e r l o g i n _ s c o p e ' ←] , $_POST [ ' api_key ' ] ) ; } else { } } } echo "DEBUG: Terminado . Ahora e l t u t o r t i e n e que e n t r a r en←e l s i s t e m a de t u t o r i a s y a c t i v a r e l s e r v i c i o para que←o t r o s u s u a r i o s puedan h a c e r r e s e r v a s .< br />" ; echo " E r r o r : Hay campos o b l i g a t o r i o s s i n r e l l e n a r .< br />" ; A.4. Apéndice IV 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 echo '<form name=" t u t o r _ a d m i n i s t r a t i o n " method=" p o s t " a c t i o n =" ' . ←h t m l e n t i t i e s ( $_SERVER [ 'PHP_SELF ' ] ) . ' "> ' ; // Datos d e l t u t o r echo '<br />Datos d e l Tutor :< br /> ' ; echo '< l a b e l f o r =" calendarOwnerEmail "> Co rreo : </ l a b e l ><i n p u t t y p e ="←t e x t " name=" calendarOwnerEmail " i d =" calendarOwnerEmail " s i z e =100 ←v a l u e ="" /><br /> ' ; echo '< l a b e l f o r =" c a l e n d a r O w n e r O f f i c e ">Despacho de T u t o r i a s : </ l a b e l ><←i n p u t t y p e =" t e x t " name=" c a l e n d a r O w n e r O f f i c e " i d ="←c a l e n d a r O w n e r O f f i c e " s i z e =100 v a l u e ="" /><br /> ' ; // C l i e n t ID f o r i n s t a l l e d a p p l i c a t i o n s echo '<br /> C l i e n t ID para a p l i c a c i o n e s i n s t a l a d a s ( C l i e n t ID f o r ←i n s t a l l e d a p p l i c a t i o n s ) :< br /> ' ; echo '< l a b e l f o r =" a u t o l o g i n _ c i d "> C l i e n t ID : </ l a b e l ><i n p u t t y p e =" t e x t " ←name=" a u t o l o g i n _ c i d " i d =" a u t o l o g i n _ c i d " s i z e =100 v a l u e ="" /><br ←/> ' ; echo '< l a b e l f o r =" a u t o l o g i n _ s e c r e t "> C l i e n t s e c r e t : </ l a b e l ><i n p u t t y p e ←=" t e x t " name=" a u t o l o g i n _ s e c r e t " i d =" a u t o l o g i n _ s e c r e t " s i z e =100 ←v a l u e ="" /><br /> ' ; echo '< l a b e l f o r =" a u t o l o g i n _ r e d i r e c t "> R e d i r e c t URIs : </ l a b e l ><i n p u t ←t y p e =" t e x t " name=" a u t o l o g i n _ r e d i r e c t " i d =" a u t o l o g i n _ r e d i r e c t " s i z e ←=100 v a l u e ="" p l a c e h o l d e r =" h t t p : / / l o c a l h o s t " /><br /> ' ; echo '< l a b e l f o r =" a u t o l o g i n _ s c o p e ">Scope : </ l a b e l ><i n p u t t y p e =" t e x t " ←name=" a u t o l o g i n _ s c o p e " i d =" a u t o l o g i n _ s c o p e " s i z e =100 v a l u e ="" /><←br /> ' ; // C l i e n t ID f o r web a p p l i c a t i o n s echo '<br /> C l i e n t ID para a p l i c a c i o n e s web ( C l i e n t ID f o r web ←a p p l i c a t i o n s ) :< br /> ' ; echo '< l a b e l f o r =" u s e r l o g i n _ c i d "> C l i e n t ID : </ l a b e l ><i n p u t t y p e =" t e x t " ←name=" u s e r l o g i n _ c i d " i d =" u s e r l o g i n _ c i d " s i z e =100 v a l u e ="" /><br ←/> ' ; echo '< l a b e l f o r =" u s e r l o g i n _ s e c r e t "> C l i e n t s e c r e t : </ l a b e l ><i n p u t t y p e ←=" t e x t " name=" u s e r l o g i n _ s e c r e t " i d =" u s e r l o g i n _ s e c r e t " s i z e =100 ←v a l u e ="" /><br /> ' ; echo '< l a b e l f o r =" u s e r l o g i n _ r e d i r e c t "> R e d i r e c t URIs : </ l a b e l ><i n p u t ←t y p e =" t e x t " name=" u s e r l o g i n _ r e d i r e c t " i d=u s e r l o g i n _ r e d i r e c t " " s i z e ←=100 v a l u e ="" p l a c e h o l d e r =" h t t p : / / l o c a l h o s t " /><br /> ' ; echo '< l a b e l f o r =" u s e r l o g i n _ s c o p e "> S c o p e s : </ l a b e l ><i n p u t t y p e =" t e x t " ←name=" u s e r l o g i n _ s c o p e " i d =" u s e r l o g i n _ s c o p e " s i z e =100 v a l u e ="" /><←br /> ' ; // S im pl e API A c c e s s ( API Key ) echo '<br />Acceso s i m p l e a l a API ( S im pl e API A c c e s s ) :< br /> ' ; echo '< l a b e l f o r ="api_key">API key : </ l a b e l ><i n p u t t y p e =" t e x t " name="←api_key " i d ="api_key " s i z e =100 v a l u e ="" /><br /> ' ; echo '<br /><i n p u t t y p e =" submit " v a l u e ="Dar de a l t a Tutor " /><br /> ' ; echo '</form> ' ; // −−−−−−−−−−−−−−−−−−−−−−−− FUNCIONES −−−−−−−−−−−−−−−−−−−−−−−− // S a l v a l o s d a t o s de c o n f i g u r a c i o n n e c e s a r i o s para e l a c c e s o a l o s ←calendarios del tutor . function w r i t e _ c a l e n d a r O w n e r _ c o n f i g ( $calOwnerFileName , ←$calendarOwnerOffice , $client_id , $client_secret , $redirect_uri , ←$scope , $userlogin_client_id , $userlogin_client_secret , ←$userlogin_redirect_uri , $userlogin_scope , $api_key ) { 95 96 Apéndices 83 $oFile = f o p e n ( $calOwnerFileName , 'w ' ) or d i e ( " E r r o r : No s e puede ←a b r i r e l f i c h e r o ' $calOwnerFileName ' para e s c r i t u r a .< br />" ) ; 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 $state = " p r o f i l e " ; $access_type = " o f f l i n e " ; $tutoring_calendar_id = " " ; $book_calendar_id = " " ; i f ( s t r l e n ( $redirect_uri ) == 0 ) { $redirect_uri = " h t t p : / / l o c a l h o s t " ; } i f ( s t r l e n ( $ u s e r l o g i n _ r e d i r e c t _ u r i ) == 0 ) { $userlogin_redirect_uri = " http : / / l o c a l h o s t " ; } $dataToSave = '<?php ' . PHP_EOL . ' // Datos p e r s o n a l e s d e l t u t o r n e c e s a r i o s ' . PHP_EOL . ' $ c a l e n d a r O w n e r O f f i c e = " ' . $ c a l e n d a r O w n e r O f f i c e . ' " ; // Despacho ←d e l t u t o r ' . PHP_EOL . ' $ t u t o r i n g _ c a l e n d a r _ i d = " ' . $ t u t o r i n g _ c a l e n d a r _ i d . ' " ; // ←C a l e n d a r i o de t u t o r i a s d i s p o n i b l e ' . PHP_EOL . ' $book_calendar_id = " ' . $ b o o k _ c a l e n d a r _ i d . ' " ; // L i s t a de r e s e r v a s ←r e a l i z a d a s ' . PHP_EOL . ' ' . PHP_EOL . ' // Parametros d e l u s u a r i o para e l URL de a c c e s o ' . PHP_EOL . ' $ c l i e n t _ i d = " ' . $client_id . ' " ; ' . PHP_EOL . ' $ c l i e n t _ s e c r e t = " ' . $cli ent_secr et . ' " ; ' . PHP_EOL . ' $ r e d i r e c t _ u r i = " ' . $redirect_uri . ' " ; ' . PHP_EOL . ' $ s c o p e = " ' . $scope . ' " ; ' . PHP_EOL . ' $ s t a t e = " ' . $state . ' " ; // O p c i o n a l − puede s e r c u a l q u i e r v a l o r ←que q u i e r a s ' . PHP_EOL . ' $ a c c e s s _ t y p e = " ' . $access_type . ' " ; // No cambiar e l v a l o r " ←o f f l i n e " , n e c e s a r i o para l a r e c u p e r a c i o n d e l " r e f r e s h _ t o k e n " ' . ←PHP_EOL . ' ' . PHP_EOL . ' // Parametros d e l u s u a r i o para e l a c c e s o de u s u a r i o s l o g e a d o s ' . ←PHP_EOL . ' $ u s e r l o g i n _ c l i e n t _ i d = " ' . $ u s e r l o g i n _ c l i e n t _ i d . ' " ; ' . PHP_EOL . ' $ u s e r l o g i n _ c l i e n t _ s e c r e t = " ' . $ u s e r l o g i n _ c l i e n t _ s e c r e t . ' " ; ' . ←PHP_EOL . ' $ u s e r l o g i n _ r e d i r e c t _ u r i = " ' . $ u s e r l o g i n _ r e d i r e c t _ u r i . ' " ; ' . PHP_EOL←. ' $ u s e r l o g i n _ s c o p e = " ' . $ u s e r l o g i n _ sc o p e . ' " ; ' . PHP_EOL . ' ' . PHP_EOL . ' // API de a c c e s s o s i m p l e ' . PHP_EOL . ' $api_key = " ' . $api_key . ' " ; ' . PHP_EOL . ' ?> ' . PHP_EOL . ' ' . PHP_EOL ; 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 fwrite ( $oFile , $dataToSave ) ; } f c l o s e ( $oFile ) ; ?> </body> </html>