Memoria - Departament d`Enginyeria Informàtica i Matemàtiques
Transcription
Memoria - Departament d`Enginyeria Informàtica i Matemàtiques
Departament d’Enginyeria Informàtica i Matemàtiques Portal de Gremio de Ragnarok Online con DRUPAL TITULACIÓN: Ingeniería Técnica Informática de Gestión AUTOR: Cristian Garcés Ruiz DIRECTOR: Carlos Molina Clemente FECHA: 06 / 2011 2 Prefacio Internet es una herramienta más usada cada día en todos los sectores, pero si hay algún sector en que el uso de Internet este más expandido es posiblemente el del ocio y más concretamente en el de los videojuegos. La cantidad de jugadores online se ha incrementado considerablemente, sobretodo a partir del lanzamiento del popular juego de Blizzard World of Warcraft, esto ha hecho que la curiosidad por otros juegos online también se haya incrementado. Ragnarok Online es un juego ya antiguo desde algunos puntos de vista, pero mantiene una gran cantidad de jugadores que se ha visto incrementada por la moda de los juegos online. Uno de los aspectos más característicos de Ragnarok Online es que requiere de la agrupación en guild (clanes, gremios) para poder experimentar en la totalidad el juego. Estos clanes pueden ser de hasta un máximo de 76 personas. La organización y comunicación entre este número de personas puede ser complicado, y si tenemos en cuenta que este número se puede ver multiplicado por cuatro debido al sistema de alianzas del juego, puede convertirse todo en un autentico caos si no se usan las herramientas adecuadas. Por estos motivos en este proyecto se quiere implementar un portal para la administración y gestión de una guild y su alianza. La implementación del portal se va a realizar mediante el uso del CMS Drupal. Los CMS son herramientas software que nos proporciona la estructura y funciones básicas de una web, como la creación, edición y publicación de contenidos y la gestión de usuarios entre otras. Drupal es un CMS modular, esto quiere decir que está formado por una estructura básica, a la cual podemos ir añadiendo funcionalidades mediante la instalación de pequeñas aplicaciones que reciben el nombre de módulo. La gran cantidad de módulos disponibles en la comunidad de Drupal permite la construcción del sitio sin la necesidad de crear prácticamente código. Aunque dispongamos de un gran repositorio de de módulos, al tratarse de un portal para una labor tan específica y poco habitual nos encontraremos en situaciones en que los módulos ya creados no cumplan los requisitos que nosotros queremos o que simplemente 3 no exista módulo para cierta funcionalidad, en este punto es cuando deberemos desarrollar nuestros propios módulos para satisfacer nuestras necesidades. Podemos resumir que en este proyecto se va a implementar un portal para la gestión de una guild de Ragnarok Online, mediante el uso de Drupal, el cual ya nos proporciona un amplio abanico de módulos para satisfacer necesidades básicas de un sitio web y para las necesidades y/o funcionalidades para las que no encontremos una solución entre los módulos disponibles, procederemos a la programación de unos módulos propios para satisfacer dichas necesidades. 4 Índice 1. Introducción ….............................................................................................................. 11 1.1. CMS …...................................................................................................................... 11 1.1.1 Definición ….............…....................................................................................... 11 1.1.2 Historia …............................................................................................................ 12 1.1.3 Funcionalidades …............................................................................................... 12 1.1.4 Tipos de CMS ….................................................................................................. 14 1.2. Drupal …................................................................................................................... 16 1.2.1 Qué es Drupal ….................................................................................................. 16 1.2.2 Arquitectura …..................................................................................................... 16 1.3 Ragnarok Online ….................................................................................................... 18 1.3.1 Qué es MMORPG …............................................................................................18 1.3.2 Qué es Ragnarok Online ….................................................................................. 18 2. Objetivos ….................................................................................................................... 21 3. Especificaciones …........................................................................................................ 23 3.1. Funcionalidades ….................................................................................................... 23 3.2. Restricciones …........................................................................................................ 25 3.2.1 Restricciones en cuanto a tipos de contenido ….................................................. 26 3.2.2 Restricciones en cuanto a visibilidad de secciones …......................................... 26 3.2.3 Restricciones en cuanto a acceso a funcionalidades …....................................... 27 4. Diseño …........................................................................................................................ 29 4.1. Roles …..................................................................................................................... 29 4.2. Tipos de Contenido ….............................................................................................. 31 5 4.3 Bloques …................................................................................................................. 38 5. Instalación y Configuración ….................................................................................... 41 5.1 Requerimientos del Sistema ….................................................................................. 41 5.2 Instalación del Servidor Web y Drupal …................................................................. 41 5.3 Instalación de Módulos y Temas para Drupal …....................................................... 49 6. Implementación …........................................................................................................ 51 6.1 Módulos del Core Utilizados …................................................................................. 51 6.1.1 Obligatorios …..................................................................................................... 51 6.1.2 Opcionales …....................................................................................................... 51 6.2 Módulos Adicionales Instalados …........................................................................... 54 6.2.1 Acces Control ….................................................................................................. 54 6.2.2 Administración …................................................................................................ 55 6.2.3 CCK …................................................................................................................. 55 6.2.4 Media …............................................................................................................... 57 6.2.5 Fecha y Hora …................................................................................................... 58 6.2.6 Views …............................................................................................................... 58 6.2.7 Votación ….......................................................................................................... 59 6.2.8 Otros …................................................................................................................ 60 6.2.9 Ragnarok Guild (módulos de desarrollo propio) …............................................. 61 6.3 Creación de Tipos de Contenidos mediante CCK …................................................. 62 6.3.1 Creación del Tipo de Contenido …...................................................................... 62 6.3.2 Creación de grupos de campos …........................................................................ 67 6.3.3 Creación de campos …......................................................................................... 68 6.3.4 Asociar un campo con un grupo …...................................................................... 69 6.3.5 Mostrar Campos ….............................................................................................. 69 6 6.4 Creación de Secciones mediante Views …................................................................ 70 6.4.1 Creación de vistas …............................................................................................ 71 6.4.2 Configuración Básica de la Vista ….................................................................... 73 6.4.3 Filtrado y Ordenación de la Vista …................................................................... 75 6.4.4 Creación de la Presentación de la Vista ….......................................................... 77 6.5 Creación de Menús …................................................................................................ 78 6.6 Creación de Roles y Concesión de Permisos …........................................................ 82 6.6.1 Creación de Roles …............................................................................................ 82 6.6.2 Asignación de Permisos a Roles …..................................................................... 83 6.6.3 Asignación de Roles a usuarios …....................................................................... 83 6.7 Creación de Alias de URL Automáticos mediante Pathauto …................................ 85 6.8 Desarrollo de Módulos en Drupal …......................................................................... 86 6.8.1 Creación del Módulo …....................................................................................... 86 6.8.2 Creación del Archivo .install …........................................................................... 88 6.8.3 Creación de Menús y Páginas …......................................................................... 91 6.8.4 Creación de Formularios …................................................................................. 93 6.8.5 Creación de Bloques …........................................................................................ 94 6.8.6 Creación de Tipo de Contenido …....................................................................... 95 6.8.7 Creación de Vistas …........................................................................................... 97 6.9 Desarrollo del Módulo Achievements …................................................................... 98 6.9.1 Tablas de la base de datos …............................................................................... 98 6.9.2 Tipos de contenido …...........................................................................................99 6.9.3 Formularios …..................................................................................................... 99 6.9.4 Bloques ….......................................................................................................... 100 6.9.5 Vistas …............................................................................................................. 100 7 6.9.6 Otras Funciones Importantes Implementadas …............................................... 100 6.10 Desarrollo del Módulo Castle Control ….............................................................. 101 6.10.1 Tablas de la base de datos …........................................................................... 101 6.10.2 Formularios …................................................................................................. 102 6.10.3 Bloques …........................................................................................................ 102 6.10.4 Otras Funciones Importantes Implementadas …............................................. 102 6.11 Desarrollo del Módulo Guild Shop …................................................................... 103 6.11.1 Tablas de la base de datos …........................................................................... 103 6.11.2 Tipos de contenido …...................................................................................... 104 6.11.3 Formularios …................................................................................................. 104 6.11.4 Bloques …........................................................................................................ 105 6.11.5 Vistas …........................................................................................................... 105 6.11.6 Otras Funciones Importantes Implementadas …..............................................105 6.12 Desarrollo del Módulo Assistance ….................................................................... 107 6.12.1 Tablas de la base de datos …........................................................................... 107 6.12.2 Tipos de contenido …...................................................................................... 108 6.12.3 Formularios …................................................................................................. 108 6.12.4 Vistas …........................................................................................................... 108 6.12.5 Otras Funciones Importantes Implementadas …............................................. 109 6.13 Desarrollo del Módulo Party …............................................................................. 110 6.13.1 Tablas de la base de datos …........................................................................... 110 6.13.2 Tipos de contenido …...................................................................................... 110 6.13.3 Formularios …................................................................................................. 111 6.13.4 Vistas …........................................................................................................... 112 6.13.5 Otras Funciones Importantes Implementadas …............................................. 113 8 6.14 Desarrollo de Temas en Drupal …......................................................................... 115 6.15 Desarrollo del Tema Propio ….............................................................................. 116 6.15.1 Definición del Tema Archivo .info …............................................................. 116 6.15.2 Creación de Plantillas ….................................................................................. 117 6.15.3 Creación del Archivo Template.php …........................................................... 117 7. Evaluación …............................................................................................................... 119 7.1 Evaluación del Módulo Achievements …................................................................ 119 7.1.1 Creación Edición Eliminación y Presentación de Nodos ….............................. 119 7.1.2 Asignación y Desasignación de los Logros …................................................... 120 7.1.3 Funcionamiento de los Bloques ….................................................................... 120 7.2 Evaluación del Módulo Castle Control …............................................................... 121 7.2.1 Creación Edición Eliminación y Presentación de Bloques …........................... 121 7.3 Evaluación del Módulo Guild Shop ….................................................................... 121 7.3.1 Creación Edición Eliminación y Presentación de Nodos ….............................. 121 7.3.2 Uso del Área de Administración …................................................................... 122 7.3.3 Realización de Compras …................................................................................ 122 7.3.4 Funcionamiento de los Bloques ….................................................................... 123 7.4 Evaluación del Módulo Assistance …..................................................................... 123 7.4.1 Creación Edición y Eliminación de Nodos …................................................... 123 7.5 Evaluación del Módulo Party ….............................................................................. 124 7.5.1 Creación de las Partys …................................................................................... 124 7.5.2 Adición y Eliminación de Asistencias a las Partys …....................................... 124 7.5.3 Presentación de las Partys …............................................................................. 125 7.6 Evaluaciones sobre Permisos y Visibilidad …......................................................... 125 8.Aspectos legales …........................................................................................................ 127 9 9. Costes …....................................................................................................................... 129 10. Conclusiones ….......................................................................................................... 131 11. Planificación temporal …......................................................................................... 133 12. Bibliografía ………………………………………………………………………… 135 13. Anexos ………..………………………………………………………………….... 137 13.1 Módulo Achievements .......................................................................................... 137 13.2 Módulo Assistance …………………………………………………...…………. 153 13.3 Módulo Castle Control ………………………………………………………….. 165 13.4 Módulo Guiad Shop …………………………………………………………….. 174 13.5 Módulo Party ……………………………………………………………………. 198 13.6 Tema Propio …………………………………………………………………….. 226 10 1. Introducción 1.1. CMS 1.1.1 Definición CMS son las siglas de Content Management System, que se traduce directamente al español como Sistema Gestor de Contenidos. Como su propio nombre indica, es un sistema que nos permite gestionar contenidos. En líneas generales, un CMS permitiría administrar contenidos en un medio digital y para el caso particular que nos ocupa, un CMS permitiría gestionar los contenidos de una web. Dicho de otra forma, un CMS es una herramienta que permite a un editor crear, clasificar y publicar cualquier tipo de información en una página web. Generalmente los CMS trabajan contra una base de datos, de modo que el editor simplemente actualiza una base de datos, incluyendo nueva información o editando la existente. Una herramienta CMS generalmente contendrá una interfaz basada en formularios, a los que habitualmente se accede con el navegador, donde se pueden dar de alta los contenidos fácilmente. Esos contenidos luego aparecerán en la página en los lugares donde se ha indicado al darlos de alta. Por lo tanto, un CMS estará compuesto de dos partes, un back y un front, siendo el back la parte donde los administradores publican las informaciones y el front la parte donde los visitantes visualizan las mismas. Los primeros sistemas de administración de contenidos fueron desarrollados por organizaciones que publicaban una gran cantidad de contenido en Internet, y necesitaban de continuas actualizaciones; como revistas en línea, periódicos y publicaciones corporativas. 11 1.1.2 Historia En 1995, el sitio de noticias tecnológicas CNET sacó su sistema de administración de documentos y publicación y creó una compañía llamada Vignette, pionero de los sistemas de administración de contenido comerciales. La evolución de Internet hacia portales con más contenido y la alta participación de los usuarios directamente, a través de blogs y redes sociales, ha convertido a los gestores de contenidos en una herramienta esencial en Internet, tanto para empresas e instituciones como para las personas. 1.1.3 Funcionalidades La funcionalidad de los sistemas de gestión de contenidos se pueden dividir en cuatro categorías: creación de contenido, gestión de contenido, publicación y presentación. Creación de contenido Un CMS aporta herramientas para que los creadores sin conocimientos técnicos en páginas web puedan concentrarse en el contenido. Lo más habitual es proporcionar un editor de texto WYSIWYG, en el que el usuario ve el resultado final mientras escribe, al estilo de los editores comerciales, pero con un rango de formatos de texto limitado. Esta limitación tiene sentido, ya que el objetivo es que el creador pueda poner énfasis en algunos puntos, pero sin modificar mucho el estilo general del sitio web. Hay otras herramientas como la edición de los documentos en XML, utilización de aplicaciones ofimáticas con las que se integra el CMS, importación de documentos existentes y editores que permiten añadir marcas, habitualmente HTML, para indicar el formato y estructura de un documento. Un CMS puede incorporar una o varias de estas herramientas, pero siempre tendría que proporcionar un editor WYSIWYG por su facilidad de uso y la comodidad de acceso desde cualquier ordenador con un navegador y acceso a Internet. 12 Para la creación del sitio propiamente dicho, los CMS aportan herramientas para definir la estructura, el formato de las páginas, el aspecto visual, uso de patrones, y un sistema modular que permite incluir funciones no previstas originalmente. Gestión de contenido Los documentos creados se depositan en una base de datos central donde también se guardan el resto de datos de la web, cómo son los datos relativos a los documentos (versiones hechas, autor, fecha de publicación y caducidad, etc.), datos y preferencias de los usuarios, la estructura de la web, etc. La estructura de la web se puede configurar con una herramienta que, habitualmente, presenta una visión jerárquica del sitio y permite modificaciones. Mediante esta estructura se puede asignar un grupo a cada área, con responsables, editores, autores y usuarios con diferentes permisos. Eso es imprescindible para facilitar el ciclo de trabajo con un circuito de edición que va desde el autor hasta el responsable final de la publicación. El CMS permite la comunicación entre los miembros del grupo y hace un seguimiento del estado de cada paso del ciclo de trabajo. Publicación Una página aprobada se publica automáticamente cuando llega la fecha de publicación, y cuando caduca se archiva para futuras referencias. En su publicación se aplica el patrón definido para toda la web o para la sección concreta donde está situada, de forma que el resultado final es un sitio web con un aspecto consistente en todas sus páginas. Esta separación entre contenido y forma permite que se pueda modificar el aspecto visual de un sitio web sin afectar a los documentos ya creados y libera a los autores de preocuparse por el diseño final de sus páginas. Presentación Un CMS puede gestionar automáticamente la accesibilidad del web, con soporte de normas internacionales de accesibilidad como WAI, y adaptarse a las preferencias o necesidades de cada usuario. También puede proporcionar compatibilidad con los diferentes navegadores disponibles en todas las plataformas (Windows, Linux, Mac, Palm, etc.) y su capacidad de internacionalización lo permite adaptarse al idioma, sistema de medidas y cultura del visitante. 13 El sistema se encarga de gestionar muchos otros aspectos como son los menús de navegación o la jerarquía de la página actual dentro del web, añadiendo enlaces de forma automática. También gestiona todos los módulos, internos o externos, que incorpore al sistema. 1.1.4 Tipos de CMS Podemos dividir de diferentes maneras, la más básica es según el tipo de portal que nos permiten implementar, así de esta manera, podemos encontrar los siguientes tipos: • Genéricos: ofrecen la plataforma necesaria para desarrollar e implementar aplicaciones que den solución a necesidades específicas. Pueden servir para construir soluciones de gestión de contenidos, para soluciones de comercio electrónico, blogs, portales. Algunos de los más usados son Joomla, Drupal, Typo3. • Foros: sitio que permite la discusión en línea donde los usuarios pueden reunirse y discutir temas en los que están interesados. Los más usados son phpBB, Invision o Vbulletin. • Blog: publicación de noticias o artículos en orden cronológico con espacio para comentarios y discusión. Algunos de los más populares son WordPress o Blogger. 14 • Wikis: sitio web dónde todos los usuarios pueden colaborar en los artículos, aportando información o reescribiéndola. También permite espacio para discusiones. Indicado para material que irá evolucionando con el tiempo. Los más utilizados són MediaWiki o TikiWiki. • Ecommerce: son Sitios web para comercio electrónico. Los más conocidos son osCommerce y Dynamicweb eCommerce. • Galería: permite administrar y generar automáticamente un portal o sitio web que muestra contenido audiovisual, normalmente imágenes. El más utilizado es Gallery. • E-Learning: sirve para la enseñanza de conocimientos. Los usuarios son los profesores y estudiantes, tenemos aulas virtuales donde se ponen a disposición el material del curso. La publicación de un contenido por un profesor es la puesta a disposición de los estudiantes, en una aula virtual, de ese contenido. Los más conocidos son WebCT o Moodle. 15 • Publicaciones digitales: son plataformas especialmente diseñadas teniendo en cuenta las necesidades de las publicaciones digitales, tales como periódicos, revistas, etc. Los más conocidos son ePrints y Thinkindot CMS. 1.2 Drupal 1.2.1 Qué es Drupal Drupal es un CMS que se distribuye como software libre bajo licencia GNU GPL. El software está desarrollado con el lenguaje de programación PHP y utiliza base de datos MYSQL. Está maquetado con hojas de estilo CSS, con lo que es posible construir sitios web totalmente accesibles. Además de las funcionalidades básicas que vienen integradas con el software, es poisble añadir nuevas funcionalidades mediante módulos. 1.2.2 Arquitectura Los módulos Los módulos son aplicaciones desarrolladas que se distribuyen libremente bajo la misma licencia GPL, mediante las cuales es posible añadir funcionalidades adicionales al núcleo de Drupal. Estos módulos implementan diferentes funciones llamadas hooks, las cuales serán llamadas por el núcleo de Drupal en el momento necesario. El núcleo El núcleo de Drupal aporta la base necesaria para su funcionamiento y para la incorporación del resto de componentes de la arquitectura. Es posible acceder al núcleo y hacer uso directo de sus funciones a través de la API de programación. 16 Para el funcionamiento básico de Drupal hay 5 módulos obligatorios que forman parte del núcleo : • Block: controla los bloques del sitio. • Filter: realiza acciones de filtrado sobre los contenidos a mostrar. • Node: realiza las acciones necesarias para la publicación y gestión de los contenidos. • System: encargado de la administración general del sitio. • User: necesario para el registro, acceso y gestión de usuarios. Área de administración Una de las características que diferencia a Drupal de otros CMS es que su área de administración viene totalmente integrada en la interfaz del portal. No existe una área de administración en si, sino un menú de navegación que permite acceso a las opciones de administración. Nodos Los nodos son el tipo de contenido básico con el que trabaja el núcleo de Drupal, para el que se proporcionan las acciones básicas de creación, publicación etc. El resto de tipos de contenido heredan del tipo nodo. Bloques Los bloques son contenidos principalmente dinámicos que se pueden habilitar en distintas zonas del sitio. Temas El tema define un diseño específico para el sitio, estan formados por diferentes archivos php que definen la estructura y por archivos css que definen el diseño. 17 1.3 Ragnarok Online 1.3.1 Qué es MMORPG Un mmorpg es lo que se conoce como juego de rol multijugador masivo en línea. Son juegos de rol que permiten que miles de usuarios interactuen en un mundo virtual mediante internet. El juego consiste en crear un personaje, el cual va evolcuionando mediante el aumento de niveles a través de la experiencia conseguida luchando contra monstruos o realizando misiones (llamadas habitualmente quest). El atractivo de este juego no solo reside en el hecho de jugar al juego, sino también en la posibilidad de poder interpretar un personaje y crear una historia alrededor de él. Características comunes entre los MMORPG Un estilo de juego típico de los juegos de rol tradicionales, que incluye misiones y sistema de recompensa basado en la recogida de tesoros. • Un sistema de desarrollo de estadísticas, normalmente relacionado con niveles y puntos de experiencia. • Una economía basada en el trueque y una unidad monetaria o más de una. • Organización de los jugadores en clanes, estén o no soportados directamente por el juego. • Moderadores del juego y, en algunas ocasiones, personas retribuidas por vigilar el mundo. • 1.3.2 Qué es Ragnarok Online Ragnarok online (RO) es un mmorpg de origen coreano basado en la mitología nórdica. Su éxito reside en su estética de tipo manga, su versatilidad en el desarrollo de los personajes y en sus eventos gvg (guild vs guild). 18 Sistema de personajes La versatilidad de la que hablábamos anteriormente, viene dada por 2 conceptos: • Job: define el tipo de personaje que vas a usar. Mago, guerrero, curador etc. • Build: se entiende por build las características únicas de cada personaje. De esta forma, dentro de un mismo tipo de clase, podemos encontrar diferentes tipos de personajes que se adecuaran mejor a unos roles que otros dentro del juego. Sistema de guilds Las guilds, o clanes, son la agrupación de personajes (hasta un máximo de 76) con el fin de conseguir propósitos en común o simplemente por tener una misma filosofía de juego. Normalmente los propósitos se resumen en participar en las WoEs y cooperación en el progreso del juego. El sello identificativo de las guilds es una pequeña imagen conocida como emblema, que se muestra al lado de todos los personajes que forman parte de ella. Sistema de partys Las partys son una pequeña agrupación temporal de personajes (hasta 12 miembros) las cuales suelen estar destinadas a la cooperación entre los usuarios, para poder subir de nivel en zonas en las que no se puede hacer de forma individual o para eliminar determinados monstruos considerados como jefes (MvP). También se usan como unidad de organización dentro de la guild a la hora de asistir a WoEs u otros eventos. War of Emperium Las War of Emperium (WoE) es una guerra entre guilds para reclamar un castillo . Si destruis el Emperio del castillo, tu guild se convierte en la propietaria del castillo y se mostrará el emblema de tu guild en las banderas del castillo y también las banderas de los castillos que hay en la ciudad. Para conseguir el castillo, tu guild debe ser la propietaria de él a la hora de finalización de la WoE. La obtención de un castillo supone una gran recompensa. Diariamente el castillo genera unos cofres que dan items de forma aleatoria. Para poder recoger los cofres, sólo hace falta 19 mantener el castillo en el momento que termine la WoE, pero para poder tener más cofres, es necesario invertir diariamente en Comercio (únicamente una vez al dia). Cada vez que una guild atacante rompe el Emperio, la Defensa y Comercio del castillo bajan 5 niveles. El nivel máximo es 100. Sistema de alianzas Una guild puede tener hasta un máximo de 3 guilds aliadas. Gracias a la alianza entre las guilds, los miembros integrantes de las diferentes guilds no pueden recibir daño entre ellos durante el desarrollo de las WoEs. 20 2. Objetivos Ragnarok online es un juego que se creó en 2002, eso implica que el conocimiento sobre el juego de los usuarios sea muy extenso, de esta manera cada día las diferencias entre guilds a nivel de juego son menores, lo que hace que una buena organización dentro de la guild sea un factor determinante para el éxito de la misma. Si añadimos la variedad de nacionalidades de los jugadores, haciendo a veces que el contacto online a través del juego sea mínimo por causas horarias, hace que la existencia de de un sitio de contacto externo al juego, para mantener informado a todos los miembros de la guild y organizarlos, sea a veces un factor directo en la competitividad de la guild. Por estás razones se ha planteado este proyecto, una web en la que cumpla las siguientes características: • Agrupar los elementos básicos para la organización de la guild. • Mantener informados a los miembros del entorno actual en el juego. • Facilitar la comunicación entre usuarios con distintas franjas horarias. • Crear un repositorio con información valiosa para el desarrollo del juego. Además de crear la web que cumpla estas características, también se quiere lograr con este proyecto lo siguiente: • Aprender el funcionamiento del framework Drupal. • Aprender a desarrollar módulos para Drupal. • Aprender a desarrollar temas gráficos para Drupal. 21 22 3. Especificaciones 3.1. Funcionalidades: Las funcionalidades que debe implementar la web para la correcta organización de la guild, la mejora de comunicación entre usuarios y la recopilación de información relevante para los miembros de la guild son las siguientes 1. Posteo de noticias. Para mantener informados a los miembros de la web sobre los acontecimientos importantes que sucedan. 2. Posteo de eventos. Para informar de cuando se va a realizar un evento, temática del mismo e información relevante sobre el. 3. Posteo de estrategias. 1. WoEs. Sección web donde queden recogidas las estrategias que se usarán en los diferentes castillos. 2. Mvp. Sección web donde queden recogidas las estrategias que se usarán para matar a los diferentes MvP. 4. Tienda interna. Para poder repartir los items que se consigan de los cofres de castillo o de los eventos de guild mediante un saldo propio ficticio. 23 5. Posteo de guiás de ayuda. Sección de la web en la que se puedan encontrar diferentes tipos de guias de ayuda sobre el juego. 6. Roster de guild. Sección de la web con un listado con información sobre los personajes de los que disponen los integrantes de la guild para participar en los diferentes eventos. 7. Calendario de eventos. Calendario donde poder apuntar los días en los que exista algún evento. 8. Posteo de applys y puntuación de los mismos. Sistema para que gente interesada en entrar a la guild pueda dejar su solicitud y los integrantes de la guild puedan votar si quieren que la persona solicitante ingrese o no. 9. Organizador de partys. Sistema para poder tener una sección de web donde haya un listado de las partys que se formarán para los distintos eventos y woes. 10. Posteo de eventos interguild. Para que los encargados de las guilds aliadas puedan dejar constancia en la web de eventos que van realizar a los que estamos invitados. 11. Indice de videos de woes hechos por los usuarios. Listado de videos hechos por los miembros de la guild que quieran compartirlos. 12. Sistema logros. Sistema mediante el cual un usuario tendrá una puntuación en consecuencia de los logros conseguidos, además de poder acceder al listado de logros conseguidos por cada usuario o al listado general de logros disponibles con una breve explicación. 24 13. Posteo de encuestas. Para poder someter a consulta algunas de las decisiones que se tengan que tomar. 14. Listado de guilds secundarias. Listado de las guilds secundarias que pertenezcan a miembros de la guild en la que podamos entrar con nuestros personajes secundarios. 15. Foro de guild Foro para poder discutir algunos temas relacionados con la guild. 16. Foros privados Foros de acceso restringido a los cuales solo podrán acceder los altos cargos de las guilds. 17. Control de castillos. Para tener información de los castillos conquistados por la guild/alianza y detalle del estado de ellos. 3.2. Restricciones Las únicas restricciones que encontraremos en este sitio web, serán las relacionadas con la visibilidad de las diferentes secciones o acceso a las áreas de administración, creación de contenidos y acceso a funcionalidades concretas, todas ellas definidas por el tipo de usuario. Las restricciones identificadas son las siguientes. 25 3.2.1 Restricciones en cuanto a tipos de contenido 1. Únicamente podrán realizar guiás de ayuda aquellos usuarios que el el guildmaster crea adecuado. 2. Únicamente podrán postear videos de woes aquellos usuarios que el el guildmaster crea adecuado. 3. Las encuestas solo podrán ser posteadas por los mandos de la guild. 4. Únicamente el administrador de logros podrá crear logros. 5. Únicamente el tesorero de la guild podrá crear items de la tienda. 6. Los eventos podrán ser creados por el organizador de eventos o los guildmaster de las otras guilds. 7. Las eventos referentes a las woes (tipo de contenido WoE) solamente podrán ser creados por el organizador de woes. 8. Las partys de eventos y woes solo podrán ser creadas por los organizadores de eventos y woes respectivamente. 9. Las estrategias de mvp solo podrán ser creadas por el organizador de eventos. 10. Las estrategias de castillos/woes solo podrán ser creadas por el organizador de WoEs. 11. Sólo los miembros de guild podrán postear sus personajes en el roster. 12. Únicamente los usuarios no registrados podrán realizar applys. 3.2.2 Restricciones en cuanto a visibilidad de secciones 1. Únicamente serán visibles para los miembros de guilds aliadas las secciones de: 1. Guiás de ayuda 2. Videos de woes 3. Estrategias de woes 4. Guilds secundarias 5. Foros comunes. 2. La sección de compras realizadas solo será visible para el tesorero de la guild. 3. Los miembros de la guild podrán ver sus propias compras realizadas. 4. El área de administración de la tienda solo será visible para el tesorero de la guild. 26 5. El área de administración de logros solo será visible para el administrador de logros. 6. El área de administración de bloques será visible para los usuarios que puedan crear los bloques de seguimiento de castillos. 7. Las secciones sin ninguna restricción mencionada anteriormente serán visibles para todos los miembros de la guild. 8. Únicamente los guildmaster/subguildmaster de las guilds tendrán acceso a los foros privados. 3.2.3 Restricciones en cuanto a acceso a funcionalidades 1. Únicamente los miembros de la guild podrán votar los applys. 2. Los miembros de guilds aliadas no podrán postear comentarios en ningún contenido. 3. Los guildmasters de guilds aliadas podrán postear comentarios en las secciones visibles para ellos. 4. Únicamente los miembros de la guild podrán votar las encuestas. 5. Únicamente el encargado de los logros podrá asignar logros conseguidos a los usuarios. 6. Únicamente los usuarios de la guild tendrán acceso a la compra de items. 7. Los bloques de control de castillos podrán ser creados por el tesorero, los mandos de la guild y los mandos de las guilds aliadas. 8. Únicamente el organizador de eventos podrá añadir miembros a una party de evento creada. 9. Únicamente el organizador de eventos podrá eliminar miembros a una party de evento creada. 10. Únicamente el organizador de woes podrá añadir miembros a una party de woes creada. 11. Únicamente el organizador de woes podrá eliminar miembros a una party de woes creada. 27 28 4. Diseño 4.1. Roles Podemos dividir los roles en 3 grupos: • Básicos: Roles para distinguir a los miembros segun a que guild pertenecen (la nuestra o una aliada) y si son un miembro general de la guild, o un miembro con poderes. • Funciones Intraguild: Roles específicos relacionados directamente con alguna de las funciones que afectan al funcionamiento de la guild. • Funciones Web: Roles específicos relacionados con funciones del contenido web estrictamente. 1. Básicos: 1. GuildMaster Miembro líder de la guild, por lo tanto gozará de todos los permisos posibles y podrá editar o eliminar cualquier tipo de contenido. 2. SubGuildMaster Miembro de la guild ayudante del líder, como tal, tendrá todos los permisos generales exceptuando los de funciones de intraguild y podrá editar y eliminar el contenido que no este relacionado con las funciones intraguild. 3. GuildMember Miembro general, este miembro solo tendrá permisos para contenidos generales y únicamente podrá editar sus contenidos, pero no eliminarlos. 29 4. Ally Guildmaster Miembro jefe de una guild aliada, podrá postear eventos únicamente, y postear comentarios en las zonas que tenga visibles. 5. Ally Guildmember Miembro general de una guild aliada, únicamente podrá ver unas pocas zonas visibles, pero no postear ningún tipo de contenido ni comentario. 2. Funciones intraguild: 1. Tesorero Miembro dedicado a repartir y mantener un control de los items de la guild, será el encargado de administrar la tienda interna. 2. Organizador de eventos Miembro dedicado a organizar los eventos, será el único que pueda crear eventos de la guild y el único que pueda organizar partys para los mismos. 3. Organizador de woes Miembro dedicado a organizar las woes, será el único que pueda crear estrategias de woes y el único que pueda organizar partys para las mismas. 3. Funciones web: 1. Editor Rol para todo miembro al cual se le permita postear guías de ayuda o estrategias para mvp de la guild. 2. Moderador foros Rol para todo miembro que ayude a controlar el posteo de temas en el foro. 3. Uploader de videos Rol para todo miembro al cual se le permita enlazar sus videos. 30 4. Controlador de logros Rol para el miembro de la guild que se encargue de controlar la asignación de logros del resto de miembros. 4.2. Tipos de Contenido: Para poder implementar las funcionalidades listadas, se van a crear los siguientes tipos de contenido. 1. Apply Este contenido se utilizará para que usuarios no miembros de la guild puedan solicitar su ingreso en la misma. En este contenido se ha de encontrar representada la información básica del personaje del usuario, los hábitos de juego de la persona, además de alguna información más que pueda ser interesante para la guild. También ha de ser posible que este tipo de contenido tenga una puntuación que le darán los miembros de la guild, para poder decidir si la solicitud es aceptada o no. Una vez publicado este contenido únicamente puede ser visto por los miembros de la guild, igual que su votación, ni la persona que lo postea ni los integrantes de guilds aliadas han de poder ver el contenido. Este contenido únicamente puede ser publicado por usuarios no registrados. 2. Asistencia a evento Este contenido se utilizará para que los miembros de la guild confirmen o soliciten (dependiendo de si es cupo cerrado o no) su asistencia a un evento. En ese contenido se ha de encontrar referenciado el usuario que solicita la asistencia, el personaje con el cual quiere asistir, y a que evento se refiere. Una vez publicado este contenido únicamente puede ser visto por los mandos de la guild, por el encargado de eventos y por el usuario que posteo el contenido. Este contenido sólo puede ser publicado por miembros de la guild. 31 3. Asistencia a woes Este contenido se utilizará para que los miembros de la guild confirmen su asistencia a las woes de la semana. En ese contenido se ha de encontrar referenciado el usuario que confirma la asistencia, el personaje con el cual va a asistir y a que woes va a asistir. Una vez publicado este contenido únicamente puede ser visto por los mandos de la guild, por el encargado de woes y por el usuario que posteo el contenido . Este contenido sólo puede ser publicado por miembros de la guild. 4. Encuesta Este contenido se utilizará para someter a votación aspectos de interés general de la guild, su estructura es la de una encuesta normal, la pregunta que se somete a votación y las posibles respuestas. Únicamente los miembros de la guild pueden votar en dichas encuestas. Este contenido sólo puede ser creado por uno de los mandos de la guild. 5. Estrategia woes FE Este contenido se utilizará para publicar una guías sobre como defender un castillo de las woes fe en concreto. En este contenido se ha de encontrar la ciudad a la que pertenece el castillo, un esquema de cada una de las salas del castillo con la disposición de los miembros de la guild, además de una explicación escrita para aclarar los esquemas y dar las indicaciones que se crean necesarias. Este contenido ha de ser visible tanto para lo miembros de las guild como para los miembros de las guilds aliadas. Este contenido solo puede ser creado por el organizador de woes. 6. Estrategia woes SE Este contenido se utilizará para publicar una guías sobre como defender un castillo de las woes se en concreto. En este contenido se ha de encontrar la ciudad a la que pertenece el castillo, esquemas de las diferentes zonas del castillo donde se tenga que poder montar un precast, un listado de las banderas para llegar a cada una de esas zonas y una explicación escrita para aclarar los esquemas y dar las indicaciones que se crean necesarias. Este contenido ha de ser visible tanto para lo 32 miembros de las guild como para los miembros de las guilds aliadas. Este contenido solo puede ser creado por el organizador de woes. 7. Estrategia MvP Este contenido se utilizará para realizar una guía del procedimiento a seguir a la hora de matar un MvP. En este contenido se ha de encontrar un listado con la gente mínima que se requiere para poder lleva a cabo la estrategia, una explicación escrita de dicha estrategia, puede ir acompañada de imágenes o no y un video donde se vea la estrategia puesta en funcionamiento. Únicamente los miembros de guild pueden ver este contenido. Este contenido sólo puede ser publicado por editores. 8. Evento Este contenido se utilizará para informar sobre la existencia de un futuro evento. En este contenido se ha de encontrar la fecha en que se realizará el evento, donde se reunirá la gente para él, una breve descripción del cometido del evento y si es necesario un link a una de las estrategias de mvp disponibles. Este contenido únicamente ha de ser visible para los miembros de la guild. Este contenido solo puede ser creado por el organizador de eventos. 9. WoE Este contenido se utilizará para informar sobre la existencia de una WoE. En este contenido se ha de encontrar la fecha en que se realizará el evento, donde se reunirá la gente para él, una breve explicación de los objetivo de la woe y si es necesario un link a una de las estrategias de castillo disponibles. Este contenido únicamente ha de ser visible para los miembros de la guild. Este contenido solo puede ser creado por el organizador de woes. 10. Guía Quest Este contenido se utilizará para crea guías para ayudar a los usuarios a completar quest del juego. En este contenido se ha de encontrar toda la información de la quest como son el lvl mínimo para hacerla, los items que se requieren y su recompensa, ya sean items o experiencia, además por supuesto ha de tener un 33 escrito de como realizar la quest paso a paso, este escrito puede ir acompañado o no de imágenes. Este contenido ha de ser visible tanto para miembros de la guild como para aliados. Este contenido sólo puede ser publicado por editores. 11. Guild secundaria Este contenido se utilizará para tener un listado de las guilds secundarias pertenecientes a personas de la guild. Este contenido ha de indicar a que usuario pertenece esa guild y con que fin fue creada. Este contenido puede ser visto tanto por miembros de guild como aliados. y sólo puede ser creado por miembros de la guild. 12. Noticia Este contenido se utilizará para informar a los integrantes de la guild de algún suceso importante. Este contenido podrá ser leído exclusivamente por los miembros de la guild. Este contenido solo podrá ser creado por mandos de la guild. 13. Party de evento Este contenido servirá para informar a los usuarios de como han quedado confeccionadas las partys para un evento en concreto. Este contenido sólo puede ser visto por los usuarios de la guild y únicamente puede ser creado por el organizador de eventos. 14. Party de woe Este contenido servirá para informar a los usuarios de como han quedado confeccionadas las partys para una woe. Este contenido sólo puede ser visto por los usuarios de la guild y únicamente puede ser creado por el organizador de woes. 15. Personaje roster Este contenido servirá para tener un listado de los personajes de los que dispone la guild para eventos y woes. En este contenido ha de quedar reflejado el usuario al que pertenece el personaje y toda la información básica del personaje, job, lvl etc. Este contenido solo puede ser visto y creado por los miembros de la guild. 34 16. Video de woes Este contenido es para realizar un repositorio de los videos de woes creados por miembros de la guild y aliados. Aquí constará la fecha del video, la guild a la cual pertenece el personaje con el que se grabó el video y un embed del video en cuestión. Este contenido puede ser posteado por aquellos miembros que tengan permiso explicito para hacerlo. 17. Foros Con este contenido se pretende tener una zona donde los miembros de la guild puedan hablar sobre vario temas, para ello se crearan los siguientes foros: 1. GuildMasters 1. Discusión general Foro para que los guildmasters de las diferentes guilds de la alianza discutan cualquier tema referente a la misma. 2. Planificación de WoEs Foro para que los guildmasters de las diferentes guilds de la alianza concreten los objetivos a realizar en cada una de las WoEs. 2. Discusión general 1. Eventos Foro para hablar de como salieron los eventos realizados, poner imágenes de ellos etc. 2. Sugerencias Foro para que los miembros de la guild puedan hacer sugerencias sobre cualquier cosa a los mandos de la guild. 35 3. WoEs Foro destinado a discutir la actuación de la guild y alianza en cada una de las woes. 3. Comercio 1. Compra Foro para que los miembros de la guild pongan en que items están interesados para ver si algún otro miembro de la guild puede vendérselo. 2. Venta Foro para que los miembros de guild informen al resto de que items disponen para la venta. 4. Ayuda 1. Quest Foro destinado por si algún miembro se queda encallado con una quest y las guiás no son suficiente. 2. Jobs En realidad no se tratará de un foro sino de varios, uno por cada rama. Estos foros servirán para debatir sobre tipos de builds de las distintas ramas o para pedir ayuda sobre la build de tu personaje. 5. Offtopic 1. Fan art Foro para que los miembros de la guild que quieran compartir sus creaciones lo hagan. 36 2. Fotos de los miembros Foro para que los miembros posteen sus fotos . 3. Full offtopic Foro para postear todo lo que no tenga lugar en el resto de foros. 4. Ocio Foro para discutir sobre otros pasatiempos a parte del juego, como series, libros etc. 5. Quedadas Foro para organizar quedadas entre la gente de la guild. 18. Logro Este contenido representará un logro conseguido. Estos logros serán creados por el encargado de controlar los logros, normalmente representarán hitos difíciles de conseguir en el juego. Cada logro tendrá una pequeña descripción de lo que representa, una puntuación asociada y si así se determina una imagen representativa del logro. 19. Item de tienda Este contenido sirve para tener un listado de los items conseguidos por lo miembros de la guild de forma común, ya sea obtenido de cofres de castillos, loot de cualquier evento o donación directa de un miembro. El contenido contará de una imagen representativa de dicho objeto, una descripción de él , la categoría de item y su precio expresado en saldo interno. Este contenido solo será visible para los miembros de la guild. 37 20. Compra Este contenido está destinado a reflejar las compras realizas por los miembros en la tienda de la guild. El contenido estará formado por el usuario que ha comprado el item, el item que ha comprado y la cantidad de items comprados. Este contenido solo será visible para el tesorero de la guild. 4.3. Bloques Para completar las funcionalidades se crearán los siguientes bloques que añadan información adicional. 1. Calendario de eventos Bloque con un calendario donde estarán indicados lo eventos que se realicen cada uno d ellos días. Este bloque sólo será visible para miembros de la guild. 2. Hall of fame Bloque que presentará un ranking con los miembros que tengan mayor puntuación acumulada por la asignación de logros. Este bloque no será visible para invitados. 3. Menú de la tienda de la guild Bloque que presentará el menú de acceso a los items de la tienda, categorizados por tipo. Este bloque sólo será visible para miembros de la guild. 4. Últimos items añadidos a la tienda Bloque que presentará la lista de los últimos items que se han añadido a la tienda. Este bloque sólo será visible para miembros de la guild. 5. Últimas compras realizadas Bloque que presentará la lista de las últimas compras realizadas por los usuarios. Este bloque sólo será visible para el tesorero de la guild. 38 6. Información del usuario sobre la tienda Bloque que mostrará a cada usuario su saldo interno actual y la última compra que realizó. Este bloque sólo será visible para miembros de la guild. 7. Control de castillos En realidad no se trata de un único bloque, sino de una plantilla para bloques. En este bloque se mostrará la información principal de un castillo, Nombre, dueño, cofres que da al día, coste de la inversión, nivel de comercio etc. Este bloque no será visible para invitados. 8. Menú de invitados Tendrá un link a las dos únicas secciones que tienen acceso los invitados, creación de apply y repositorio de videos de WoEs. 39 40 5. Instalación y Configuración 5.1 Requerimientos del Sistema Para la instalación de Drupal requerimos 3 cosas a parte del software propio del framework: 1. Un servidor web que ejecute scripts PHP. 2. PHP, versión 4.1 o superior de PHP. Extensión XML de PHP (para los módulos bloggerapi, drupal, jabber, ping). 3. Un servidor de base de datos soportado por PHP. 5.2 Instalación del Servidor Web y Drupal Para agilizar y facilitar la fase de instalación se opta por usar wampserver, que en una sola instalación nos proporciona el servidor web Apache versión 2.2.11, el entorno PHP5 versión 5.0.3, la base de datos MySQL versión 5.1.36, así como el gestor PHPmyadmin versión 3.2.0.1. La versión de php que incorpora por defecto el servidor wamp no es compatible con el núcleo de Drupal utilizado, por lo que se debe descargar de la propia página de wampserver la versión anterior, 5.2.11. Una vez descargado el servidor wamp y el módulo php con la versión que nos interesa., deberemos instalar los componentes en el ordenador, primero el servidor wamp y después el módulo de php con la versión anterior. Una vez instalado todo debemos seleccionar en la configuración del servidor wamp que utilice la versión anterior que hemos instalado (Figura 5.1), ya que sino por defecto seguirá usando la versión que el traía por defecto. 41 Figura 5.1 El siguiente paso es crear la base de datos que va a utilizar nuestra web. Accederemos a PHPmyadmin y crearemos una nueva base de datos (Figura 5.2). Figura 5.2 Una vez creada la base de datos, descomprimiremos el archivo de la instalación de Drupal (descargado anteriormente). Copiaremos la carpeta en el directorio www del servidor wamp y le cambiaremos el nombre por el nombre que queramos darle a la web. Por defecto el idioma de nuestro software Drupal será el inglés, para traducirlo deberemos descargar el pack de traducción que nos interese y descomprimirlo en la misma carpeta de instalación. 42 Ahora para seguir con la instalación de Drupal, accederemos mediante el navegador a la dirección http://localhost/nombredelacarpeta y tendremos un asistente que nos guiará por la instalación del software, el asistente nos permitirá elegir entre los lenguajes instalados (Figura 5.3). Figura 5.3 En el siguiente paso nos pedirá la información de la base de datos, nombre, usuario y password (Figura 5.4). Figura 5.4 En opciones avanzadas encontraremos algunos datos adicionales que podria ser necesario configurar en algún caso como por ejemplo un prefijo para las tablas de la base de datos, 43 puerto y servidor de la base de datos, aunque en nuestro caso no será necesario modificar ninguna de esas opciones. Después nos pedirá la información básica del sitio, nombre, mail, la información sobre la cuenta de superadministrador, usuario, password y mail, zona horaria predifinida y si se desea activar las URL's limpias, está opción por defecto no está disponible posteriormente se explicará como activarlas (Figura 5.5). Con esto finaliza el proceso de instalación de Drupal y queda listo para su uso. 44 Figura 5.5 Lo siguiente es configurar la información básica del sitio, pero antes de seguir activaremos como anteriormente mencionamos el uso de URL's limpias. Para ello lo único que debemos hacer es acceder al archivo httpd.conf del servidor apache y descomentar la linea que pone LoadModule rewrite_module modules/mod_rewrite.so. Una vez hecho esto 45 debemos reiniciar nuestro servidor web y después ir a nuestro sitio web en el área de administración y acceder a Administrar > Configuración del sitio > URLS limpios (Figura 5.6) y acativar la opción. Mediante las URL's limpias conseguimos que las direcciones de nuestro sitio en vez de ser del tipo http://web.es/?q=node/3 pasen a ser del tipo http://web.es/node/3, mucho más fáciles de recordar para el usuario. Figura 5.6 Ahora si podemos ir a la configuración de nuestro sitio, podemos acceder a ella yendo a Administrar > Configuración del sitio > Información del sitio (Figura 5.7) donde podremos introducir los siguientes datos: • Nombre El nombre del sitio web • Dirección de correo La dirección De en los correos automáticos enviados durante el registro y tras la solicitud de nueva contraseña, y otros avisos. • Eslogan El lema, declaración o frase vendedora de su sitio. 46 • Misión La declaración de misión o de enfoque de su sitio. • Mensaje del pie de página • Este texto se mostrará al final de cada página. Es útil para agregar un aviso de derechos de autor a sus páginas. • Nombre del usuario anónimo El nombre usado para indicar usuarios anónimos. • Página inicial predeterminada La página de inicio muestra el contenido de este URL relativo. 47 Figura 5.7 Algunos de estos datos ya los pudimos introducir en el momento de la instalación pero de está manera pueden ser modificados en cualquier momento. Con esto acabamos la instalación y configuración básica de nuestro sitio, el próximo paso será la instalación de los módulos que vayamos a usar para complementar la funcionalidad de la página. 48 5.3 Instalación de Módulos y Temas para Drupal El proceso de instalación del módulo es común para cualquier módulo que queramos instalar en nuestro sitio. Lo primero que debemos hacer es descargar el módulo a instalar, una vez lo tengamos únicamente debemos descomprimirlo y colocarlo dentro de la carpeta en la que se deben colocar todos los módulos adicionales, su ruta es sites/all/modules (se recomienda instalar aquí y no en sites/default/modules, ya que dado el caso de tener una instalación multisitio tendríamos el módulo disponible para todos los sitios). Después de haberlo colocado en su lugar la carpeta del módulo, únicamente debemos acceder a Administrar > Construcción del sitio > Módulos buscar el módulo en el listado, activar su casilla de verificación y guardar (Figura 5.8). Del resto del proceso como puede ser creación de las tablas de la base de datos, creación de links etc se encarga Drupal de realizarlo mediante los hooks apropiados. Figura 5.8 49 El proceso de instalación de los temas de Drupal es análogo al proceso de instalación de los módulos pero con dos pequeñas diferencias, la primera es que la ruta en la que debemos situar la carpeta del tema es sites/all/themes en lugar de sites/all/modules (nuevamente lo hacemos en el directorio all y no en el default por las mismas razones que lo hacíamos con los módulos). La otra diferencia es que a la hora de activar el tema deberemos dirigirnos a Administrar > Construcción del sitio > Temas establecerlo como predeterminado y guardar (Figura 5.9). Figura 5.9 Unas de las peculiaridades que tiene Drupal es que nos da la posibilidad de establecer un tema para el sitio y otro para las áreas de administración, esto nos es beneficioso sobretodo a la hora de desarrollar temas nuevos, ya que si el tema tiene algún error, siempre podremos acceder al área de administración para desactivarlo, además de poder elegir un tema con una interfaz más legible aunque menos atractiva para dichas áreas. Así que se recomienda dejar uno de los temas que proporciona Drupal por defecto para las áreas de administración. 50 6. Implementación 6.1 Módulos del Core Utilizados Los módulos del core son aquellos que vienen provistos con la instalación del software de Drupal. Podemos diferenciar entre dos tipos, los obligatorios y los opcionales. 6.1.1 Obligatorios Estos módulos son los que manejan el funcionamiento básico del software y sin los cuales no podría funcionar. Block Este módulo controla las cajas que se muestran alrededor del contenido principal. Filter Este módulo maneja el filtrado de contenido en preparación para mostrarlo. Node Este módulo permite que se envíe contenido al sitio y que se despliegue en páginas. System Este módulo provee la gestión de la configuración general del sitio por administradores. User Este módulo administra el registro de usuarios y el sistema de inicio de sesión. 6.1.2 Opcionales Estos son módulos que también vienen en la instalación básica de Drupal, pero que no son necesarios para el funcionamiento del software en si, aunque si añaden algunas de las funcionalidades más habituales en los sitios web 51 Comment Este módulo añade la funcionalidad de publicar comentarios en los contenidos publicados para opinar o discutir sobre el contenido en cuestión. El módulo nos deja configurar si queremos que se activen o no los comentarios para cada tipo de contenido y varias opciones más como por ejemplo el número de mensajes mostrados, la disposición etc. Este módulo además de añadirnos la funcionalidad comentada, es requerido para el funcionamiento del módulo Forum. Database logging Este módulo anota y registra eventos del sistema en la base de datos. Forum Este módulo permite la construcción de foros de discusión en nuestro sitio. Nos añade la infraestructura básica para poder crear las diferentes categorías y topics. Como ya se ha dicho, este módulo requiere del módulo comment para funcionar, pero también requiere que el módulo taxonomy esté activado. Help Módulo que administra el despliegue de ayuda en linea que puedan tener el resto de módulos implementados. Locale Este módulo activa la función de traducción de interfaz. Este módulo nos presenta una lista de las cadenas traducibles disponibles en la web y nos permite introducir la traducción para el idioma en uso. Las cadenas traducibles vienen definidas por una función de la API de Drupal, esta función, es la función t(), cualquier cadena que pongamos dentro de está función será contemplada como una cadena que es susceptible de ser traducida. 52 Menu Este módulo permite a los administradores del sitio personalizar el menú de navegación. La personalización se resume en añadir o quitar links del menú, y en controlar la visibilidad de los links ya añadidos. Path Este módulo permite cambiar el nombre de cualquier URL del sitio. Gracias a ello podremos conseguir rutas más amigables para el usuario. Poll Este módulo es el encargado de crear toda la funcionalidad de publicación de encuestas y de controlar los votos de las mismas. Taxonomy Este módulo permite la categorización del contenido mediante términos que después pueden ser mostrados como etiquetas. Con este módulo Drupal intenta hacer un pequeño acercamiento a lo conocido como web semántica. Update Status Este módulo es e encargado de comprobar las actualizaciones de Drupal disponibles, así como las de los módulos y temas instalados. 53 6.2 Módulos Adicionales Instalados Debido al número de módulos instalados, se va a hacer un listado separado por paquetes/categorías de los módulos. 6.2.1 Access Control ACL Este módulo únicamente se encarga de incorporar una API para el control de acceso que después puede ser utilizada por otros módulos. Content Acces Este módulo nos permite configurar para cada uno de los tipos de contenido disponibles en nuestra web, las opciones de visibilidad y edición basándose en la propiedad del contenido y el rol del usuario. Gracias a este módulo podemos definir que “secciones” van a ser visibles y cuales no para cada rol del sitio. Forum Access Este módulo nos permite aumentar las posibilidades de nuestro foro permitiendo crear foros privados y añadir moderadores a cada uno de los foros. Este módulo agrega un rol al sitio llamado moderator, el cual ya trae los permisos predefinidos para poder actuar como moderador de los foros, por este motivo el rol moderador no hará falta crearlo manualmente. 54 6.2.2 Administración Administration menu Este módulo Provee un menú desplegable para la mayoría de las tareas administrativas y otros destinos comunes (para los usuarios con los permisos apropiados), con este módulo los administradores del sitio pueden acceder directamente a las opciones de administración sin necesidad de pasar por las páginas previas. No añade ninguna funcionalidad nueva para el sitio, pero si permite un uso más cómodo del él. 6.2.3 CCK Este conjunto de módulos es una de las herramientas más usadas de Drupal, CCK (Content Construction Kit) provee al usuario de una forma fácil y rápida de crear tipos de contenido sin necesidad de saber programar. Además también permite añadir nuevos campos a cualquier tipo de contenido existente. Content Es el núcleo del paquete de módulos CCK és el que permite la creación del nuevo tipo de contenido y permite añadir los campos básicos al contenido. Todo el resto de módulos listados a continuación dependerán de este para su funcionamiento CCK Time Permite añadir campos de tipo tiempo a los tipos de contenido. Content Copy Activa la posibilidad de importar y exportar la definición de campos. Content Permisions Añade la posibilidad de definir permisos de visibilidad y edición para cada uno de los campos creados mediante cualquier submódulo de CCK. 55 Content Templates Crea plantillas de visualización de los campos definidos y permite modificar algunas de sus características de visualización. Email Permite añadir campos de tipo email a los contenidos. Fieldgroup Permite crear grupos de campos (fieldsets) para separar los campos por algún criterio o por simple esteticismo. FileField Define los campos de tipo fichero, este módulo nuevamente es más una herramienta para otros módulos que una herramienta a usar por si solo. Cualquier módulo que provea una subida de archivos o referencias a ellos, dependerá de este módulo para su funcionamiento. ImageField Permite añadir un campo de tipo imagen a los contenidos. Link Permite añadir un campo de tipo link a los contenidos. Node reference Permite añadir un campo que haga referencia a otro nodo (contenido) publicado en el web. Number Permite añadir un campo de tipo numérico. 56 Option Widgets Permite añadir campos de selección de tipo checkbox o radiobutton para campos de texto y numéricos. Text Permite añadir un campo de tipo texto. 6.2.4 Media Este conjunto de módulos es otra extensión del CCK pero que solo se centra en la adición de campos con contenido multimédia. Embedded Media Field Esté módulo es el núcleo de este paquete de módulos, provee de un motor para insertar contenidos multimedia de diferentes proveedores. Embedded Video File Permite añadir campos para insertar videos incrustados en los tipos de contenidos. Media: Megavideo Este módulo provee de las reglas especificas para que el módulo Embedded Video File pueda incrustar videos desde Megavideo. Media: YouTube Este módulo provee de las reglas especificas para que el módulo Embedded Video File pueda incrustar videos desde YouTube. Únicamente se han añadido los módulos para incrustar videos desde dos proveedores diferentes por considerarse lo más usados entre el ámbito de players para subir sus videos, aunque se podría haber optado por añadir más proveedores como vimeo o google videos entre otros. 57 6.2.5 Fecha y Hora Este conjunto de módulos es otra extensión del CCK pero que solo se centra en la adición de campos de fechas y tiempo. Date Este módulo permite añadir campos y controles (widgets) de fecha/hora. Date API Este módulo únicamente se encarga de incorporar una API de fechas que puede ser usada por otros módulos. Date Timezone Este módulo es requerido cuando se usa Date API. Modifica la gestión de zona horaria del sitio y del usuario para usar nombres de zonas horarias en vez de un desplazamiento. 6.2.6 Views Views Este módulo provee a Drupal de una forma sencilla de crear listas y consultas a la base de datos sin necesidad de tener conocimientos de programación. Este módulo únicamente incluye el motor para realizar estas cosas, pero no la interficie para crear dichas listas y consultas. Views UI Este módulo provee a Drupal de la interficie gráfica para crear las vistas mediante el módulo views. 58 Views Exporter Este módulo consigue provee de la función de exportar a views, mediante la cual podremos obtener el código a incluir en un módulo programado para que al instalarse instale también la vista. 6.2.7 Votación Voting API Este módulo únicamente se encarga de incorporar una API de votación para que pueda ser usada por otros módulos. Vote Up/Down Este módulo provee un widget para la votación positiva/negativa par aque otros módulos lo usen. Vote Up/Down Nodes Este módulo permite agregar un widget de votación de vote up/down a los tipos de contenido seleccionados. Chaos tools Este módulo incluye una API de herramientas que pueden ser usadas por otros módulos, el módulo Vote Up/Down requiere de ella. 59 6.2.8 Otros Event Este módulo proporciona un API de calendarización, para desplegar y exportar calendario. CKEditor Este módulo activa el uso de un editor de texto WYSIWYG sustituyendo al editor de texto plano incorporado por defecto. Este módulo permite elegir que roles tendrán acceso a este editor y cuales no. Un editor WYSIWG es un editor de texto con formato que permiten escribir un documento viendo directamente el resultado final. IMCE Este módulo provee a Drupal de una interfaz sencilla para poder incluir imágenes desde disco duro a nuestros textos, basado en una aplicación tipo ftp que permite subir la imagen al servidor y colocarla en nuestro texto. Token Este módulo provee de una API compartida para el remplazo de comodines textuales con datos concretos. PathAuto Este módulo proporciona un método para que los módulos puedan crear automáticamente alias de URLs del contenido que gestionan. Este módulo provee de un sistema de reemplazo automatizado de urls según el tipo de contenido que se está publicando. De esta manera podemos conseguir que por ejemplo que las rutas de nuestro sitio sean de la forma http//:miweb/tipodecontenido/titulodelcontenido en vez del tipo http//:miweb/node/id 60 6.2.9 Ragnarok Guild (módulos de desarrollo propio) Achievements Módulo que añade un sistema de logros. Permite crear el tipo de contenido logro, que después puede ser asignado y desasignado a los usuarios. El módulo también mantiene la puntuación que van acumulando los usuarios al ir consiguiendo los logros y permite ver el listado y puntuación de los logros de cada uno de los usuarios en su perfil. Este módulo puede funcionar en cualquier instalación de Drupal. Castle Control Este módulo añade unos formularios para poder añadir/eliminar unos bloques que contendrán toda la información básica sobre un castillo. Este módulo puede funcionar en cualquier instalación de Drupal. Guild Shop Este modulo proporciona una tienda para items de Ragnarok Online que trabaja con saldo propio, es decir, los items no se compran con dinero real, sino con un crédito que otorga el encargado de la tienda. Además de la tienda añade el tipo de contenido Item de tienda y compra, uno para poder añadir productos a la tienda y el otro para mantener una constancia de los items que se van comprando en la tienda. Este módulo puede funcionar en cualquier instalación de Drupal. Assistance Módulo que añade a Drupal la posibilidad de crear un tipo de contenido para confirmar la asistencia a los eventos futuros (tanto normales como de WoEs) publicados y que su fecha de realización aun no haya pasado. A la hora de crear el contenido nos dejará elegir un evento disponible y con que personaje del roster vamos a asistir al evento. No deja crear dos asistencias para un mismo evento ni que dos personas elijan un mismo personaje para el mismo evento. Este módulo solo funciona completamente en esta instalación de Drupal ya que requiere que los tipos de contenido Evento y WoEs estén creados. 61 Party Módulo que complementa al módulo Assistance, mediante este módulo obtenemos el método para poder crear partys, añadir miembros o eliminarlos de ellas en función de las asistencias creadas con el módulo anterior. Este módulo solo funciona completamente en esta instalación de Drupal al igual que al módulo que complementa por motivos derivados de la dependencia de los módulos entre si. 6.3 Creación de Tipos de Contenidos mediante CCK La comunidad de Drupal nos ofrece una potente herramienta para la implementación de tipos de contenido sin necesidad de tener que picar código, esta herramienta es el módulo CCK (Construction Content Kit) que ha sido utilizada para crear la mayor parte de los tipos de contenido del sitio web. Por esta razón se va a explicar como crear un tipo de contenido de muestra paso por paso. 6.3.1 Creación del Tipo de Contenido Lo primero que debemos hacer es instalar el módulo CCK y todos los submódulos necesarios para tener disponibles los tipos de campos que queramos añadir. El proceso de instalación de módulos ya se explicó en el punto 5.3 de este documento. Una vez tenemos instalados los módulos hemos de crear un nuevo tipo de contenido, esto lo hacemos accediendo a Administrar > Administración de contenido > Tipos de contenido y acceder a la pestaña Añadir tipo de contenido. Una vez estemos situados en la pestaña Añadir tipo de contenido deberemos rellenar el formulario que consta de: 1. Identificación (Figura 6.1) Información básica relativa al tipo. Consta de Nombre, se trata del nombre del tipo de contenido. Este nombre será el que sistema muestre a los usuarios en los diferentes apartados del sitio, por tanto debe ser un nombre legible para las personas. Tipo, es el nombre con que Drupal conocerá el tipo de contenido, se 62 usará internamente para trabajar con este tipo de contenido, el nombre únicamente puede incluir letras minúsculas, números y guiones bajos. Descripción, la descripción servirá de ayuda para los usuarios para que se hagan una idea general sobre el tipo de contenido. La descripción se mostrará en el listado de creación de tipos de contenido. Figura 6.1 2. Opciones del formulario de envío (Figura 6.2) Información sobre como se mostrará al usuario el formulario de creación del contenido. Consta de Titulo, valor de la etiqueta del titulo que se muestra al usuario. Cuerpo, valor de la etiqueta del cuerpo del contenido que se muestra al usuario. Número mínimo de palabras, en el caso de que se quiera definir un 63 número mínimo de palabras que deba tener el cuerpo del contenido. Explicación o directrices para envíos, ayuda o instrucciones para el usuario. Figura 6.2 3. Opciones de flujo de trabajo (Figura 6.3) Permite establecer las opciones predeterminadas para el tipo de contenido a la hora de la creación de un nodo. Consta de Publicado, indica si el nodo será publicado cuando se cree. Promocionado en la pagina principal, indica si se verá en la pagina de inicio del sitio. Pegajoso en la cima de las listas, indica si se situará al principio de la pagina inicial de forma permanente. Crear nueva revisión, define si se crea o no una nueva revisión cuando el nodo se modifique. Soporte multilenguaje, permite habilitar o no el soporte multilenguaje. 64 Figura 6.3 4. Opciones de comentarios (Figura 6.4) Define las opciones para configurar el comportamiento de los comentarios de este tipo de contenido. Consta de Opciones predefinidas para comentarios, define si estarán activos y en caso de estar activos si se podrán solo leer o leer y escribir. El resto de opciones solo tiene sentido modificarlas en caso de que los comentarios estén activos, son las siguientes, Modo predefinido de visualización, puedes seleccionar entre lista plana o lista de hilos. Ordenación predefinida de visualización, permite elegir si se mostraran primero los comentarios más nuevos o los más antiguos. Comentarios predefinidos por pagina, número de mensajes mostrados en cada pagina.. Controles de comentarios, permite activar un menú para que cada usuario puede controlar el orden y el tipo de visualización de los comentarios. Comentario anónimo, define si los usuarios anónimos pueden comentar o no. Campo asunto del comentario, define si los comentarios han de tener un campo asunto o no. Vista previa del usuario, define si los usuarios deben previsualizar obligatoriamente u opcionalmente los comentarios. Ubicación de formulario de envío de los comentarios, define donde se mostrara el formulario de envió de comentarios, puede ser en una pagina a parte o mostrar al final de la pagina. 65 Figura 6.4 66 Una vez creado el tipo de contenido buscaremos en la lista el tipo de contenido recién creado y accederemos a Configurar los campos y nos encontraremos con una lista de campos (Figura 6.5) que no podremos modificar o eliminar pero si ordenar, estos campos provienen del núcleo o de otros módulos instalados. Desde la sección add podremos añadir campos o grupos de campos. Figura 6.5 6.3.2 Creación de grupos de campos Los grupos son elementos que nos permiten juntar diferentes campos para mejorar la presentación y la lógica del formulario. Para crear un Nuevo Grupo (new group), debemos rellenar la Etiqueta, nombre que se mostrará en el formulario. Nombre del grupo, nombre con que Drupal trabajará internamente con el grupo. Para ordenar el grupo solamente habrá que arrastrarlo a la posición deseada. 67 6.3.3 Creación de campos Para crear un Nuevo Campo (new field), debemos rellenar la Etiqueta, nombre que se mostrará en el formulario. Nombre del campo, nombre con que Drupal trabajará internamente con el campo. Tipo de datos, seleccionaremos de un listado el tipo de dato que queremos que contenga el campo, el listado d ellos tipos de datos listados dependerá de la cantidad de módulos que tengamos instalados. Widget, define el tipo de elemento o control de formulario que se mostrará al usuario para el campo. Los tipos de widgets dependen de el tipo de dato que sea el campo. Una vez damos a guardar el sistema nos mostrará un formulario de configuración que variará dependiendo del tipo de dato y el widget elegido. En la figura 6.6 se muestra un ejemplo de formulario de configuración para un campo de mail. Figura 6.6 68 6.3.4 Asociar un campo con un grupo Para relacionar un campo con un grupo únicamente debemos arrastrar el campo debajo y a la derecha del grupo. En la figura 6.7 se muestra como quedaría un campo asociado a un grupo que ha sido situado debajo del titulo del formulario. Figura 6.7 6.3.5 Mostrar Campos Para finalizar la creación del tipo de contenido, podemos definir la visualización de cada uno de los elementos añadidos mediante CCK en la presentación del nodo. Para ello accederemos a la pestaña Display Fields (figura 6.8). Para cada campo podemos especificar la visualización tanto para el resumen del nodo (teaser) como para el nodo completo. Las opciones que nos dan son elegir si la etiqueta del campo se va a mostrar en la misma linea que el contenido del campo o encima y si queremos que el campo sea visible o no. 69 Figura 6.8 6.4 Creación de Secciones mediante Views El módulo views es otro de los módulos que nos ofrece la comunidad de Drupal para poder desarrollar nuestro sitio web sin necesidad de tener que picar nada de código, esta vez la funcionalidad del módulo es la de crear páginas dinámicas (conocidas como vistas) alimentadas con los contenidos del sitio. Con las vistas podemos crear contenidos dinámicos de muchos tipos: • Un bloque con un listado de los últimos usuarios registrados en el sitio. • Una tabla de datos con campos ordenables. • Una página que muestre todos los nodos de un mismo tipo. Precisamente esté último contenido es el que vamos a utilizar nosotros y así implementar las secciones del sitio web. 70 6.4.1 Creación de vistas Una vez más lo primero que debemos hacer es instalar los módulos views y views UI, el proceso de instalación ya ha sido descrito en el apartado 5.3 de este mismo documento. Una vez todo instalado nos dirigiremos a Administrar > Construcción del sitio > Views y seleccionaremos la pestaña Añadir (add). El primer paso será rellenar el formulario web (Figura 6.9) que consta de: • Nombre de la vista Es el nombre interno para la vista. • Descripción de la vista Es el nombre de la vista tal y como se le mostrará al usuario en el área de administración de vistas. • Etiqueta de vista Se trata de u campo opcional que permite etiquetar las vistas, para su clasificación en el área de administración. • Tipo de vista Según el tipo de vista seleccionado podremos construir tipos de vista diferentes, ya que lo que estamos seleccionando es el elemento principal sobre el que la vista realizará la consulta. En nuestro caso seleccionaremos Nodo, ya que lo que nos interesa es hacer un listado de los nodos de cada tipo de contenido. 71 Figura 6.9 Después de rellenar estos campos haremos clic en Next para crear la vista y pasar a la pantalla de edición (Figura 6.10).En la pantalla de edición de la vista podemos encontrar varios bloques, pero a nosotros solo usaremos los de basic settings, filtros, sort criteria y la opción de add display. Si ahora guardásemos la vista ya tendríamos la vista creada aunque aun no podríamos usarla, para ello debemos definir las opciones de los bloques mencionados anteriormente, cosa que nos disponemos a hacer a continuación. 72 Figura 6.10 6.4.2 Configuración Básica de la Vista Del bloque de configuración básica lo primero que necesitamos modificar es el Row Style, clicaremos sobre el enlace y en el cuadro que nos aparecerá (Figura 6.11)seleccionaremos Nodo y le daremos a actualizar. Ahora nos aparecerá otro cuadro en el cual nos dejará elehir si queremos que el nodo se muestre en su modo resumido o completo y si queremos que se muestren los links al nodo y los comentarios del nodo, dejaremos tal y como nos aparecen las opciones (Figura 6.12) visualización del nodo en resumen que muestre los links del nodo y no muestre los comentarios, una vez demos a actualizar otra vez ya quedará modificada la opción aunque no se guardará finalmente hasta que no demos al botón guardar. Para saber si la vista ha sido guardada después de realizar un cambio,views 73 mantendrá las opciones modificadas sin guardar resaltas en amarillo para indicarnos que no han sido guardadas aun. Figura 6.11 Figura 6.12 El siguiente elemento que nos interesa del bloque de configuración básica es el de Access, mediante este elemento podremos elegir criterios de acceso sobre la vista. Por defecto es Unrestricted, caso que nos serviría para una sección pública como puede ser la de los videos de woes, si por ejemplo la vista que estuviéramos creando ahora fuese al de noticias, sección que solo pueden ver los miembros de la guild, lo que haríamos seria lo siguiente, clicariamos en Unrestricted y nos aparecería un cuadro (Figura 6.13) que nos daría a elegir entre Ninguno, Permiso o Role, en este nuestro caso seleccionaríamos Role y le dariamos a actualizar y nos aparecería una lista de los roles creados en el sitio (Figura 6.14), únicamente deberíamos elegir los roles para los cuales queremos que sea accesible la vista y volver a actualizar. Nuevamente quedará indicado que los cambios no están salvados hasta que no demos a guardar. Figura 6.13 74 Figura 6.14 Otra opción que podríamos modificar si nos interesa es la de Items to display, para definir el numero de elementos que queremos que se nos muestre por pagina en la vista. También podríamos modificar el Use pager que nos dejaría elegir entre un paginador o completo o uno más minimalista. Con esto ya acabaríamos con la configuración básica de la vista, el siguiente paso será definir que queremos que se muestre. 6.4.3 Filtrado y Ordenación de la Vista Lo primero que vamos a definir son los filtros de nuestra consulta para decidir que es lo que queremos que se muestre. Para añadir un filtro solo hemos de clicar en el símbolo + del bloque filtros y nos aparecerá una larga lista de filtros (Figura 6.15), por suerte tenemos una lista desplegable para filtrar los distintos grupos de filtros, en nuestro caso como lo que queremos crear es una lista con los nodos de un mismo tipo, seleccionaremos en la lista desplegable Nodo buscaremos el filtro Nodo: Tipo lo seleccionaremos y le daremos a añadir. Después de esta acción nos aparecerá una lista de con los tipos de contenido creados y la opción de escoger si queremos mostrar los nodos que sean del tipo seleccionado o lo contrario (Figura 6.16), en nuestro caso lo más sencillo es seleccionar que sean del tipo de contenido seleccionado y seleccionar el contenido en cuestión de la lista y dar a actualizar, con esto ya se habrá añadido el filtro. 75 Figura 6.15 Figura 6.16 Ahora lo que falta es establecer un criterio de ordenación para la vista, el sistema de añadir el filtro es totalmente análogo al de añadir un filtro, solo que esta vez después de haber seleccionado el grupo de criterios nodo, buscaremos el criterio Nodo: update date (Figura 6.17), y cuando le demos a añadir nos dejará escoger entre si queremos orden ascendente o descendente y la precisión que queremos (segundo, minuto, etc) (Figura 6.18). Una vez le demos a actualizar y guardar ya solo nos quedará añadir un display a nuestra vista para que sea usable. Figura 6.17 76 Figura 6.18 6.4.4 Creación de la Presentación de la Vista La presentación definirá la forma en la que se mostrará nuestra vista, una misma vista puede tener varias presentaciones. En nuestro caso como lo que queremos es crear las secciones de nuestra web, las presentaciones que crearemos serán del tipo página, así que tendremos que seleccionar en la lista desplegable el tipo página y dar a añadir. Después de esto nos aparecerá un nuevo bloque llamado Page Settings (Figura 6.19) con dos nuevas opciones a configurar, Ruta, cuando cliquemos en ella nos aparecerá un cuadro de texto donde podremos introducir la ruta relativa que le queremos dar a la vista para poder acceder posteriormente a ella (figura 6.20). También tendríamos la opción de añadir esta ruta a un menú, pero en vista de que las vistas puedan ser usadas en varios menús diferentes, en vez de añadirla aquí nos encargaremos posteriormente de añadir el elemento a los menús pertinentes. Figura 6.19 77 Figura 6.20 6.5 Creación de Menús Para crear un menú debemos acceder a Administrar > Construcción del sitio > Menús. Lo primero que debemos hacer es añadir un nuevo menú desde la pestaña Añadir Menú aquí deberemos rellenar un sencillo formulario (Figura 6.21) formado por el Nombre del menú, este es el nombre interno mediante el cual Drupal trabajará con el menú. Titulo, es el nombre que mostrará el menú en las diferentes interfaces del sitio. Descripción, esta descripción sólo se ve en las áreas de administración y sirve para organizar mejor los menús creados. Una vez rellenado y guardado el formulario, tendremos el menú creado, aunque aun no se mostrará en nuestro sitio, sólo estará disponible en el área de administración de menús, así que después tendremos que colocarlo en una de las regiones del sitio para tenerlo disponible, pero antes añadiremos los elementos al menú. Figura 6.21 78 Después de guardar el formulario anterior, directamente nos encontraremos en el área de edición del menú, desde la que podremos añadir elementos a nuestro menú, aprovechamos para decir que si quisiéramos añadir un elemento a un menú ya existente, ya sea uno predefinido o uno creado por nosotros los pasos serian los mismos, deberíamos acceder al área de administración de menús de la misma forma que cuando queremos añadir un menú y clicar sobre el menú que queremos editar. Desde el área de edición del menú para añadir un elemento seleccionamos la pestaña Añadir elemento y nos aparecerá un formulario a rellenar (Figura 6.22) con los siguientes elementos, Ruta, que es el enlace a la página que se cargará cuando el usuario clique en el elemento del menú. Las rutas pueden ser enlaces internos del sitio o enlaces externos. Titulo del enlace, será el nombre que aparezca en el menú. Descripción, la descripción aparecerá cuando se pase el ratón por encima del elemento del menú. Activado, únicamente los elementos activos serán mostrados, de esta manera podemos hacer que un elemento deje de mostrarse sin necesidad de eliminarlo para poder recuperarlo en un futuro sin necesidad de introducir nuevamente todos los datos. Expandido, en caso de tratarse de un elemento con hijos, si marcamos esta opción el menú aparecerá desplegado mostrando sus elementos hijos. Elemento padre, sirve para indicar el elemento padre del elemento que estamos añadiendo, si se trata de un menú de primer nivel el elemento padre será el nombre que le hemos dado al menú, si queremos que el elemento que estamos añadiendo sea un submenú de algún elemento, solo deberemos indicar el nombre de dicho elemento. Peso, el peso representa la posición que tendrá el elemento respecto a elementos del mismo nivel, los elementos de mayor peso se situarán más abajo que los de menor peso. 79 Figura 6.22 Una vez añadidos los elementos desde la pestaña Listado de elementos encontraremos una estructura de árbol (Figura 6.23), mediante la cual podremos cambiar el orden de los elementos arrastrándolos, cambiar las opciones de Activado y Expandido entre otras cosas. Figura 6.23 80 Una vez creado el menú y añadidos todos los elementos el último paso a realizar para tener disponible el menú en el sitio es activarlo en una de las regiones del sitio, para ello debemos acceder a Adminstrar > Construcción del sitio > Bloques, esto es debido a que los menús una vez creados Drupal los trata como si fuesen bloques. Una vez en el área de adminstración de bloques (Figura 6.24) solo tendremos que localizar nuestro menú en la lista de bloques desactivados, y seleccionar en la lista desplegable que sale al lado del nombre en que región queremos colocarlo, una vez colocado solo tendremos que guardar los cambios y el menú ya estará disponible en el sitio. Figura 6.24 81 En el caso de que quisiéramos crear un menú solo visible para alguno de los roles (el procedimiento en realidad sirve para cualquier bloque, ya que lo que configuramos es la visibilidad del bloque) desde la sección de administración de bloques deberíamos dar a la opción Configurar del menú en cuestión y seleccionar los roles que queremos que puedan ver el menú (Figura 6.25) de esta manera para el resto de roles será como si ese menú no existiera. Figura 6.25 6.6 Creación de Roles y Concesión de Permisos a Usuarios 6.6.1 Creación de Roles Por defecto Drupal dispone de dos roles predefinidos que no pueden ser eliminados del sistema, Usuario anónimo y Usuario autenticado. Para añadir nuevos roles deberemos acceder a Administrar > Administración de usuarios > Roles. Para crear un rol únicamente deberemos introducir el nombre del nuevo rol en el cuadro de texto (Figura 6.26) y dar a Añadir rol. Figura 6.26 82 6.6.2 Asignación de Permisos a Roles Para asignar permisos a los roles tenemos dos opciones, una es desde el área de administración de Roles (Administrar > Administración de usuarios > Roles) clicar en editar permisos del rol al que queramos asignar los permisos. La otra opción es acceder a Administrar > Administración de usuarios > Permisos, en ambas opciones nos encontraremos una tabla del mismo tipo (Figura 6.27), solo que en la primera opción únicamente tendremos dos columnas, una para el nombre del permiso y otra para las casillas de verificación del rol, mientras que en la segunda opción se mantendrá la columna con el nombre de los permisos, pero tendremos una columna por cada rol creado en el sitio. Con la segunda opción la asignación de los permisos es más rápida ya que de una sola vez puedes asignar los permisos a todos los roles, pero puede llegar a ser tediosa si el número de roles creados en el sitio es muy elevado. Para asignar un permiso a un rol únicamente hay que marcar la casilla de verificación de dicho permiso. Figura 6.27 6.6.3 Asignación de Roles a usuarios Para asignar permisos debemos acceder a Administrar > Administración de usuarios > Usuarios, ahora tenemos dos opciones para asignar los permisos, la primera y más cómoda, es seleccionar los usuarios a los cuales queremos asignar un mismo rol activando su casilla de verificación y a continuación desplegar la lista desplegable de Actualizar opciones y seleccionar Añadir un rol a los usuarios seleccionados y el rol en cuestión (Figura 6.28). La segunda opción es acceder a la configuración del usuario clicando sobre el nombre del usuario ir a la pestaña de Editar y seleccionar los roles que queramos que tenga de la lista de roles del sitio (Figura 6.29). 83 Figura 6.28 Figura 6.29 84 6.7 Creación de Alias de URL Automáticos mediante Pathauto Para hacer las rutas más amigables y fáciles de recordar para los usuarios se suele aplicar un alias a la URL del contenido que se esta creando mediante el formulario de creación de este. Aunque no es un proceso complicado conseguir que los diferentes usuarios mantengan un mismo patrón a la hora de crear los contenidos puede llegar a ser complicado, por estas razones el uso del módulo pathauto es una buena manera de solventar estos problemas. Mediante el este módulo podemos crear unos patrones para la generación automática de los alias, tanto para contenidos, categorías, usuarios etc. Aunque pathauto tiene muchas opciones de configuración únicamente explicaremos como establecer los patrones de reemplazo para contenidos que es la función que se le ha dado en el sitio. Lo primero de todo es instalar los módulos pathauto y token, el módulo token es requerido por pathauto para su funcionamiento. Una vez instalados los módulos accedemos a Administrar > Construcción del sitio > Alias de URL y vamos a la pestaña Opciones de alias de rutas automáticos. Una vez en esta página desplegamos Node paths y aquí es donde deberemos introducir el patrón de reemplazo (Figura 6.30). Figura 6.30 85 Como se ve en la imagen las rutas constan de dos partes, la primera que la definimos según el tipo de contenido, y la segunda que es común a todas, esta segunda parte lo que hace es coger el título que tenga el nodo que se esta creando y añadirlo, por ejemplo un nodo de tipo encuesta con titulo hace buen día quedaría de la forma encuestas/hace-buen-dia. Para la primera parte d ella ruta lo que hemos hecho es introducir el texto con el nombre que le dimos a la vista creada con Views, de esta forma, aunque realmente los contenidos no tengan relación con su vista a efectos del usuario es como si los nodos de cada tipo colgaran de su vista. 6.8 Desarrollo de Módulos en Drupal Dependiendo de la complejidad del módulo que se vaya a desarrollar la cantidad de archivos y/o hooks que se han de definir puede variar de gran manera. A continuación se va a explicar como se implementan los archivos y hooks más comunes en el desarrollo de módulos en general. Por convención todos los archivos y hooks de Drupal deben empezar con el nombre que se le da a la carpeta del módulo, en el caso de los archivos debe ir seguido de la extensión del archivo y en el caso de los hooks del identificador de este. 6.8.1 Creación del Módulo Todos los módulos necesitan dos archivos principales para funcionar, el archivo .info y el archivo .module, aunque los archivos sean archivos de texto plano debemos especificar que los archivos que trabajen con codificación UTF-8 sin BOM ya que es la codificación que usa Drupal y en el caso de trabajar con otra podrían haber problemas con los caracteres especiales como las tildes y las eñes. Archivo .info Este archivo contiene la información básica sobre el módulo, los requisitos mínimos y las posibles dependencias con otros módulos. La primera linea del archivo .info debe ser la cadena ; $Id$ esta cadena es usada por la comunidad de Drupal para el servidor de control de versiones, en el caso de que 86 compartamos nuestro módulo en la comunidad. A continuación vienen los campos que contienen la información del módulo: • name Campo obligatorio. Define el nombre del módulo tal y como se le mostrará al usuario. • description Campo obligatorio. Es la descripción que se mostrará al usuario en el área de administración de módulos. • core Campo obligatorio.Define la versión de Drupal para la que se ha desarrollado el módulo, no hace falta indicar la versión exacta como por ejemplo 6.22 sino que se ha de indicar 6.x. • php Campo opcional. Define la versión mínima de php que debemos tener instalada para que el módulo funcione. • dependencies Campo opcional. Define los módulos que nuestro módulo requiere que estén instalados para funcionar. • package Campo opcional. Define a que conjunto de módulos pertenece el módulo. 87 Ejemplo de archivo .info del módulo forum_access: ; $Id$ name = Forum Access description = Allows forums to be set private and allows forums to be given moderators. dependencies[] = acl dependencies[] = forum package = Access control core = 6.x Archivo .module Este archivo es donde se implementarán los principales hooks y funciones del código del módulo. 6.8.2 Creación del Archivo .install En el archivo .install se incluyen las acciones que ha de realizar el módulo en la instalación o desisntalación del mismo. Estas acciones suelen estar relacionadas con la base de datos, como la creación/supresión de tablas en la base de datos, modificación de tablas existentes en la base de datos etc. Para definir las tablas que se han de crear en la base de datos, no escribiremos el código en SQL directamente, sino que utilizaremos un hook especifico de Drupal con una sintaxis propia. Función hook_schema Esta es la función mediante la cual se definen las tablas que va a instalar el módulo, cabe aclarar que esta función únicamente define las tablas, no las instala, para instalar y desisntalar las tablas deberemos implementar los hook install y uninstall. 88 Las tablas se definirán por medio de arrays asociativos, los siguientes valores son los que se pueden usar en la definición de las tablas del hook_schema. • description Pequeña descripción de la tabla y su dunción. • fields Se trata de otro array asociativo mediante el cual definimos la estructura de los campos de la tabla. Cada campo vendrá identificado por su nombre y también será un array asociativo que podrá tener las siguientes opciones: • description describe el campo y su función • type define el tipo de dato del campo. • size define el tamaño máximo del campo. • not null booleano que indica si el campo puede o no acceder valores nulos. • default define el valor por defecto del campo. • length define la longitud máxima para los campos de tipo varchar, text etc. • unsigned booleano que define si los tipos numéricos llevan o no signo. • precision/scale indica la precisión decimal para los tipos numéricos. • serialize booelano que define si el campo se almacena como una cadena serializada • primary key Array que toma por valor uno o más campos de la tabla que especifican la clave primaria • unique keys Array que define las claves únicas de la tabla 89 • indexes Array que define los índices de la tabla. Ejemplo de la definición de una tabla mediante la función hook_schema del módulo taxonomy. $schema['term_hierarchy'] = array( 'description' => 'Stores the hierarchical relationship between terms.', 'fields' => array( 'tid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'Primary Key: The {term_data}.tid of the term.', ), 'parent' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => "Primary Key: The {term_data}.tid of the term's parent. 0 indicates no parent.", ), ), 'indexes' => array( 'parent' => array('parent'), ), 'primary key' => array('tid', 'parent'), ); Funciones hook_install y hook_uninstall Estas dos funciones son las que se llaman en el momento de instalación y desisntalación de un módulo. Para crear las tablas en la base de datos sólo tenemos que usar la función drupal_install_schema() pasandole por parametro el nombre del módulo, automáticamente el recogerá la función hook_schema para obtener la definición de las 90 tablas. La función hook_uninstall funciona de manera análoga pero haciendo uso de la función drupal_uninstall_schema(). Si queremos hacer alguna inserción/supresión en las tablas de la base de datos, también es en estas funciones en las que deberíamos incluir el código. 6.8.3 Creación de Menús y Páginas Mediante la implementación del hook_menú conseguimos 2 cosas, la primera es crear los elementos que se han de añadir a los menús para mantener organizado jerárquicamente el sitio, la segunda es la de hacer que cualquier página definida además de ser añadida al menú pertinente, sea accesible mediante la ruta que le definamos. Así por ejemplo con el código añadido a continuación definiríamos una URL con dirección www.web.com/example que ejecutaria la función de retorno example_function(). Código de ejemplo: $items['example'] = array( 'title' => 'página de ejemplo', 'page callback' => 'example_function', 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); 91 Los valores más importantes que puede contener el array con el que definimos la página son: • title Titulo de la página. • page callback Función de retorno que nos presentará la página cuando se demande su URL a través del navegador. • page arguments Array de argumentos que se le han de pasar a la función definida en page callback en caso de que sean necesarios. • access callback Funcion de retonro para determinar quien puede acceder a la página. • access arguments Array de argumentos que se le han de pasar a la función definida en access callback en caso de que sean necesarios. • menu name Nombre del menú en el que queremos que se cree el elemento, ha de ser el nombre interno del menú. • type Determina el tipo de elemento utilizado. 92 6.8.4 Creación de Formularios Para crear un formulario lo primero que debemos hacer es definir la URL en la que va a estar disponible el formulario mediante la función hook_menu. En el parámetro page callback le indicaremos el valor drupal_get_form, función incluida en el API de Drupal que recibe por parámetro una definición de un formulario y la presenta en el sitio. En el parametro page arguments le indicaremos el nombre de la función en la que definamos el formulario. Igual que pasaba con la definición de las tablas de la base de datos, Drupal tiene una sintaxis propia para la definición de formularios nuevamente basada en arrays asociativos. Siendo la URL del formulario form1 y la función que define el formulario form1_function la definición en el hook menú quedaria de la siguiente manera: $items['form1'] = array( 'title' => 'primer formulario', 'page_callback' => 'drupal_get_form', 'page arguments' => 'form1_function', 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); Validación y procesamiento de los formularios De forma predeterminada Drupal utiliza para cada formulario unas funciones de validación y procesamiento que debemos implementar. Sus nombres han de ser el nombre que el dimos a la función que definía el formulario seguida de _validate en el caso de la función de validación y _submit en el caso de la función de procesamiento. 93 6.8.5 Creación de Bloques La creación de los bloques se realiza mediante la función hook_block. La función hook_block tiene dos parámetros importantes, $op y $delta. El parámetro $deta es el identificador mediante el cual Drupal diferencia un bloque de otro bloque definido dentro del mismo módulo, los valores de $delta pueden ser tanto númericos como cadenas. • El parámetro $op indica la acción que ha de realizar la función en esa ejecución. Los valores de $op pueden ser: list Retorna un vector con todos los bloques definidos, cada elemento del vector (bloque) será identificado por su delta y podrá tener los siguientes campos: • info es la descripción del bloque apra el área de administración. • cache indica como debe funcionar la cache del bloque. • status booleano que indica si el bloque esta activado o desactivado por defecto • region marca la región en la que debe situarse el bloque en el caso de que este activo por defecto. • weight indica la posición del bloque respecto a otros bloques de la misma región. • pages indica en que páginas debe mostrarse el el bloque por defecto. • custom booleano que indica si es un bloque implmentado por un módulo. • title Indica el título del bloque por defecto. • configure Devuelve un vector con la definición del formulario de configuración del bloque. • save Se realizan las operaciones de salvado de las opciones de configuración del bloque. 94 • view Devuelve un vector con la información que ha de mostrar el bloque. 6.8.6 Creación de Tipos de Contenido Podemos dividir la creación de tipos de contenidos en tres fases, definición del tipo de contenido, gestión del nodo y presentación del nodo. Definición del tipo de contenido Para definir el tipo de contenido debemos implementar dos hooks, el hook_node_info() en el cual definiremos el nombre y las propiedades del tipo de contenido, Definiremos dichos atributos mediante un array asociativo que podrá tener varios componentes, los más relevantes son: • name Nombre del tipo de contenido que se presentará a los usuarios en la interfaz. • module Nombre del módulo que implementa el contenido. • description Deascripción del tipo de contenido para yuda del usuario. • has_title Booleano que indica si el contenido tendrá o no titulo. • has_body Booleano que indica si el contenido tendrá o no cuerpo. 95 Una vez implementado este hook deberemos implementar el hook_form(), este hook no es más que otra función en la que deberemos definir un formulario mediante la sintaxis de Drupal. Este formulario será el formulario de creación/edición de los nodos del tipo de contenido. Gestión de los nodos Obviamente para mantener la integridad del contenido deberemos tener una tabla en la base de datos donde almacenar la información del nodo. Para tarbajar con la información del nodo y la base de datos tendremos que implementar cuatro hooks, hook_insert, hook_update, hook_delete y hook_load. hook_insert A la hora de crear unn nodo los campos básicos del nodo como son el titulo y el cuerpo, se encarga el núcleo de Drupal de almacenarlos, sin embargo el resto de campos adicionales debemos indicar nosotros como deben ser guardados, es aquí donde indicaremos en que tabla y en que cloumnas se ha de colocar cada uno de los campos del tipo de contenido. hook_update Del mismo modo que pasa a la hora de crear el nodo, cuando lo actualizamos los campos adicionales del tipo de contenido no son almacenados por el núcleo de Drupal, asi que en esta función hemos de implementar el tratamiento de los datos para que queden almacenados en la base de datos. hook_delete De forma análoga al borrar un nodo, debemos implementar esta función para indicar que información de la base de datos debe ser eliminada. hook_load Este hook se encarga de recuperar la información adicional de la base de datos para poder presentarsela al usuario. 96 Presentación de los nodos En la implementación de la presentación de nodos podemos diferenciar tres partes, los hooks que debemos crear en el archivo .module que son el hook_view y el hook_theme, la plantilla de presentación que se trata de un archivo php y el archivo css que es el encargado dar los estilos a la plantilla. hook_view Es la función encargada de preparar el nodo para su presentación añadiendole la información de los campos adicionales hook_theme Es la función encargada de definir la plantilla que va a utlizar el tipo de contenido y los argumentos que va a recibir dicha plantilla. Plantilla de presentación (archivo .tpl.php) Este archivo es el que implementa el código html de presentación del nodo mediante código php. Archivo css Define los estilos que se usarán en la plantilla de presentación. 6.8.7 Creación de Vistas Para que nuestro módulo cree una vista en el momento de su instalación, lo más sencillo es crear la vista mediante el módulo views, tal y como se explicó en el apartado 6.4 de este mismo documento. Una veza creada la vista hemos de usar la herramienta de export del mñodulo views, esta herramientas nos proporcionará el código a añadir a nuestro módulo. Generalmente habrá que añadir el hook_views_api en el archivo .module, y crear el archivo .views_default.inc con el código que implementa la vista. 97 6.9 Desarrollo del Módulo Achievements Para el desarrollo del módulo se han seguido las directrices explicadas en el apartado anterior (6.8 Desarrollo de Módulos en Drupal), por esta razón únicamente se especificarán los hooks y funciones implementadas que no se hayan explicado en dicho apartado, para el resto de situaciones se considerara implícita la información referente a los hooks implementados y propiedades de los mismos. Este párrafo es aplicable a todos los apartados referidos a la implementación de los módulos propios. 6.9.1 Tablas de la base de datos Este módulo instala tres tablas en la base de datos. • achievements_list En esta tabla se almacena la información adicional del nodo achievement. Está formada por los campos: • • nid, identificador del nodo que se ha creado. • vid, versión del nodo creado. • description_achievement, guarda la descripción del logro. • value, guarda la puntuación del logro. • picture, guarda la ruta donde se encuentra la imagen del logro. achievements_user En esta tabla se almacena la relación entre usuarios y logros asignados. Está formada por los campos: • nid, identificador del nodo del logro que se asigna. • uid, identificador del usuario al que se asigna el logro 98 • achievements_users_punctuation En esta tabla se almacena la puntuación conseguida por cada usuario por los logros conseguidos. Está formada por los campos: • uid, identificador del usuario. • punctuation, puntuación acumulada del usuario por la asignación de logros. 6.9.2 Tipos de contenido Este módulo implementa un tipo de contenido llamado logro, este tipo de contenido esta explicado en el apartado 4.2 de este mismo documento. 6.9.3 Formularios Para implementar todas las funciones del módulo se implementan cuatro formularios. El primer formulario es el formulario de selección de roles a los que se le pueden asignar logros, este formulario nos presenta una serie de checkboxes con los roles disponibles del sitio. El segundo formulario es el formulario para la creación y edición del nodo del tipo de contenido logro. Este formulario presenta los campos de texto para añadir la información adicional del logro y un botón de selección de archivo para añadir la imagen del logro. Los dos últimos formularios muy parecidos entre si, son los formularios de asignación y desasignación de los logros a los usuarios. Ambos formularios se han implementado mediante formularios de varios pasos. En el primero de los pasos nos aparece en el caso de la asignación la lsita desplegable de los usuarios que tienen alguno de los roles que pueden tener asignados logros, en el caso de la desasignación la lista contiene los usuarios que tienen algun logro asignado. Una vez seleccionado el usuario damos a siguiente y nos aparece una nueva lista desplegable, que en el caso de la asignación nos muestra la lista de 99 los logros que aun no han sido asignados a ese usuario y en el caso de la desasignación nos muestra los logros que tiene asignados. 6.9.4 Bloques El módulo crea un bloque llamado Hall of Fame en el que se muestra una lista de los usuarios con la puntuación más alta conseguida hasta el momento por la asignación de módulos. 6.9.5 Vistas Elmódulo implementa una vista con una presentación de página en la que se muestran los nodos del tipo logro ordenados por fecha de actualización. 6.9.6 Otras Funciones Importantes Implementadas hook_user Mediante este hook conseguimos que cuando borramos a un usuario se borren las entradas de las tablas achievements_user y achievements_users_punctuation de la base de datos y que se muestre en el perfil del usuario la lista de logros que tiene asignados y la puntuación que tiene acumulada. hook_perm Mediante este hook definimos los permisos que proporciona este módulo para determinar que acciones puede realizar cada rol. Los permisos son los siguientes: • create achievement • delete own achievement • delete any achievement 100 • edit own achievement • edit any achievement • administer achievements hook_access Mediante este hook definimos los permisos que ha de tener un rol para poder crear, actualizar y borrar un nodo del tipo de contenido implementado por el módulo. 6.10 Desarrollo del Módulo Castle Control 6.10.1 Tablas de la base de datos Este módulo únicamente instala una tabla en la base de datos. • castle_info Recoge la información que ha de presentar cada uno de los bloques que se creen para representar el estado de un castillo. Los campos de la tabla son los siguientes: • delta, identificador del bloque, usado por Drupal a la hora de realizar operaciones con el bloque. • economy, valor del nivel de economía del castillo. • defense, valor del nivel de defensa del castillo. • daily_chests, número de cofres diarios que da el castillo. • daily_cost, coste diario en zenys de aumentar los nieveles de economia y defensa del castillo. • owner, guild dueña del castillo. 101 6.10.2 Formularios Este módulo implementa tres formularios para la creación, edición y eliminación de los bloques que representan la información de los castillos. El formulario de creación de los bloques es un formulario de dos pasos,en un primer instante nos presenta un campo de texto donde deberemos introducir el nombre del castillo, este valor será utilizado como delta del bloque. En el siguiente paso se nos pide el resto de información relativa al castillo, economía, defensa etc. El formulario de actualización es muy similar al formulario de creación, pero esta vez en el primer paso lo que nos encontramos es una lista desplegable con los bloques que hay creados, en el siguiente paso se nos mostrará nuevamente los campos para introducir la información del castillo. El último formulario, el formulario de eliminación de bloques, únicamente presenta una lista desplegable con los bloques creados para poder seleccionar cual queremos eliminar. 6.10.3 Bloques Aunque el módulo no presenta ningún bloque de inicio si implementa el hook_block para que una vez añadido un castillo se pueda mostrar la información deseada. 6.10.4 Otras Funciones Importantes Implementadas hook_perm Mediante este hook definimos los permisos que proporciona este módulo para determinar que acciones puede realizar cada rol. Los permisos son los siguientes: • adminster castles • view castles 102 6.11 Desarrollo del Módulo Guild Shop 6.11.1 Tablas de la base de datos Este módulo instala tres tablas en la base de datos. • gs_internal_balance Esta tabla es la encargada de almacenar el crédito interno de cada uno de los usuarios. Consta de los siguientes campos: • • uid, es la id del usuario. • balance, es el saldo que tiene el usuario. gs_item Esta tabla almacena la información adicional del nodo del tipo de contenido item shop. Tiene los siguientes campos: • • nid, identificador del nodo que se ha creado. • vid, versión del nodo creado. • description_item, es la descripción del item. • type_item, nos indica de que tipo de item se trata, carta, armadura etc. • price, precio del item. • stock, cantidad de items de este tipo que hay. • picture, imagen del item. gs_purchase Esta tabla almacena la información adicional del tipo de contenido purchase. Esta formada por los siguientes campos: • nid, identificador del nodo que se ha creado. • vid, versión del nodo creado. • user_name, nombre del usuario que ha realizado la compra. 103 • item_name, nombre del item comprado. • amount, cantidad de items comprados. Este módulo a parte de la creación de las tablas en la base de datos, en el momento de la instalación realiza otra acción. Dicha acción es insertar en las tablas del módulo taxonomía los valores necesarios para poder crear los términos para categorizar los items cuando se creen según el tipo de item que son, esto posteriormente nos permitirá crear las presentaciones de la vista según el tipo de item. En la desinstalación realizará el paso contrario, es decir eliminar las entradas creadas en la instalación en las tablas del módulo taxonomía. 6.11.2 Tipos de contenido Este módulo implementa dos tipos de contenido llamados item de tienda y compra, estos tipos de contenido están explicado en el apartado 4.2 de este mismo documento. 6.11.3 Formularios Este módulo implementa cinco formularios, el formulario para añadir stock a un item de la tienda, el formulario para añadir crédito a un usuario, el formulario de compra y los formularios de creación y edición de los dos tipos de contenido implementados por el módulo. El formulario para añadir stock a un item nos presenta una lista desplegable en la que aparecen todos los items creados para la tienda donde seleccionaremos a que item queremos añadir el stock y un campo para añadir la cantidad que se debe añadir al stock de dicho item. El formulario para añadir crédito es muy similar al anterior formulario, solo que esta vez la lista desplegable presentará la lista de usuarios a los que se les puede añadir saldo y en el campo de texto inferior es donde deberemos añadir el saldo. 104 Si un usuario puede comprar cosas de la tienda y hay stock del mismo, aparecerá un link para acceder al formulario de compra de dicho item, una vez accedamos a él veremos el crédito total del que dispone el usuario que está realizando la compra, el item que se quiere comprar, el precio de dicho item, el stock restante y un campo para poner que cantidad de items queremos comprar. Si la compra tiene éxito, es decir, nos alcanza el crédito para comprar la cantidad de items que hemos especificado y la cantidad especificada no es superior al stock restante, seremos redirigidos a una página donde se nos indicará los datos de la compra y el saldo restante después de realizarla. Esta compra creará automáticamente el nodo relacionado con la compra para que el tesorero tenga constancia de la compra realizada. 6.11.4 Bloques Este módulo implementa cuatro bloques, el bloque con los últimos items añadidos a la tienda, el bloque con las últimas compras realizadas por los usuarios, un bloque con el menú de la tienda y un bloque con la información del usuario referente a la tienda. El menú de tienda nos permite acceder a la vista que queramos según el tipo de item o a una vista con todos los items de la tienda. La información del usuario presenta la última compra realizada por el usuario y el saldo actual en su poder. 6.11.5 Vistas Este módulo incluye una vista con varias presentaciones, cada uno de ellas muestra los items de un tipo de item determinado y una vista que presenta todos los items sin importar el tipo. 105 6.11.6 Otras Funciones Importantes Implementadas hook_perm Mediante este hook definimos los permisos que proporciona este módulo para determinar que acciones puede realizar cada rol. Los permisos son los siguientes: • create item shop • delete own item shop • delete any item shop • edit own item shop • edit any item shop • administer shop • create purchase • buy item hook_access Mediante este hook definimos los permisos que ha de tener un rol para poder crear, actualizar y borrar un nodo del tipo de contenido implementado por el módulo. 106 6.12 Desarrollo del Módulo Assistance 6.12.1 Tablas de la base de datos Este módulo utiliza dos tablas. • assistance_node Almacena la información adicional del nodo del tipo de contenido asistencia. Está formada por los siguientes campos: • • nid, identificador del nodo que se ha creado. • vid, versión del nodo creado. • uid, usuario que realiza la asistencia. • enid, identificador del nodo de evento sobre el que se realiza la asistencia. • job, identificador del nodo que representa el personaje utilizado. • status, indica si la asistencia ha sido ya asignada a una party o no. assistance_job Almacena una relación de nombre de un job y un identificador numérico, esta tabla se utilizará posteriormente para crear elementos de algunos formularios mediante bucles. Esta formada por los siguientes campos: • id, identificador del job. • name, nombre del job. Este módulo además de la creación de las tablas, en su instalación inserta toda la información de la tabla assistance_job. 107 6.12.2 Tipos de contenido Este módulo implementa dos tipos de contenido, el de asistencia a evento y el de asistencia a woe, estos tipos de contenido están explicado en el apartado 4.2 de este mismo documento. En este caso, a la hora de gestionar los nodos de los tipos de contenido, en el momento de actualizar y eliminar un nodo de uno de estos dos tipos de contenido debemos tener en cuenta si el módulo party está instalado, ya que de ser así deberemos mirar si esa asistencia había sido asignada a una party para eliminarla de ella y hacer los cambios pertinentes en las tablas del módulo party. 6.12.3 Formularios Este módulo a parte de los dos formularios para la creación y edición de los tipos de contenido implementa otro formulario en el que indicar que tipo de contenido es el que implementa el roster de personajes de la guild. Este formulario presenta una lista de los tipos de contenidos disponibles en la web mediante radio buttons para que podamos seleccionar el contenido adecuado. 6.12.4 Vistas Este módulo implementa dos vistas que presentan los nodos de cada uno de los tipos de contenido implementados por el módulo. 108 6.12.5 Otras Funciones Importantes Implementadas hook_perm Mediante este hook definimos los permisos que proporciona este módulo para determinar que acciones puede realizar cada rol. Los permisos son los siguientes: • admin assistance • create assistance • edit any assistance • edit own assistance • delete any assistance • delete own assistance hook_access Mediante este hook definimos los permisos que ha de tener un rol para poder crear, actualizar y borrar un nodo del tipo de contenido implementado por el módulo. hook_user En el caso de que no este instalado el módulo party, en el momento de eliminar un usuario deberemos eliminar la información de los nodos de asistencia de dicho usuario. function get_future_events Esta función nos devuelve un array asociativo que contiene sólo los eventos con fecha de realización posterior a la fecha actual y que son de woes o eventos normales según se le especifique por parámetro. Esta función es usada en los formularios de creación de nodo para crear la lista desplegable del evento a seleccionar para la asistencia. 109 6.13 Desarrollo del Módulo Party 6.13.1 Tablas de la base de datos Este módulo instala dos tablas en la base de datos. • party Almacena la información básica de una party, para cada una de las asistencias asignadas a un party existirá una entrada en la tabla, consta de los siguientes campos: • • enid, identificador del nodo del evento para el que es la party • party_name, nombre de la party. • type_event, indica si el evento es un evento de woe o evento corriente. • anid, identificador del nodo asistencia que se asigna a la party. party_members Almacena la cantidad de gente que hay asignada a una party. Consta de los siguientes campos: • enid, identificador del nodo del evento para el que es la party. • party_name, nombre de la party. • number, cantidad de gente asignada a la party. 6.13.2 Tipos de contenido Este módulo presenta una peculiaridad respecto al resto de módulos implementados, aun implementar un tipo de contenido como es la party (para eventos y woes), no lo implementa mediante la creación de un nodo. Para la presentación de estos tipos de contenido se ha creado una página estática que presenta una lista desplegable con los eventos/woes (según de que partys se trate) para los 110 que hay creadas partys, una vez seleccionamos uno de los eventos se ejecuta una función ajax que es la encargada de mostrar en la parte inferior todas las partys creadas para ese evento y los miembros que están asignados a cada una de las partys (figura 6.31). Figura 6.31 6.13.3 Formularios Este módulo implementa seis formularios, uno para la creación de partys de cada uno de los tipos de party y después dos para la adición y eliminación de miembros de las partys para cada uno de los dos tipos de partys. Solo se va a explicar los formularios para uno del tipo de las partys con la diferencia de que los eventos, asistencias y/o partys que se mostrarán serán del tipo evento o del tipo woe. El formulario de creación de partys es un formulario de tres pasos, en el primero de estos nos saldrá una lista desplegable con los eventos que tienen alguna asistencia pendiente de asignar. En el segundo paso aparecerá un campo para introducir el nombre que queremos dar a la party, este nombre no se puede repetir con el de otras partys del mismo evento, pero si con el nombre de otra party de otro evento. Por último nos aparecerá el listado de asistencias divididos por el job del personaje que esta asignado a la asistencia (figura 6.32). 111 Figura 6.32 El formulario para añadir un miembro a una party es un formulario de tres pasos. En el primer paso nos aparcen los eventos que ya tienen alguna party creada y no estas llena, es decir tiene menos de 12 integrantes. En el siguiente paso nos aparece una lista desplegable con las partys creadas para dicho evento, para facilitar la colocación de los usuarios en las partys, al selccionar una party en la lista desplegable cargamos mediante ajax el contenido de esa party (de manera análoga a la hora de mostrar el contenido party) para poder revisar que jobs hay añadidos en cada una de las partys. En el último paso se nos muestra la lista de asistencias pendientes para ese evento de forma idéntica que se hacia en la creación de la party. El formulario para eliminar miembros es muy parecido al de añadir miembros, los dos primeros pasos son iguales, aunque esta vez en el segundo paso no mostraremos los miembros de la partys al cambiar el valor de la lista desplegable, esto lo hacemos así porque se ha considerado que a la hora de eliminar un miembro de una party ya tienes claro del miembro que se trata y a que party pertenece, asi que no nos aportaría ninguna ayuda dicha presentación. En el último paso se muestra una lista de checkboxes con los miembros de la party. 6.13.4 Vistas Este módulo implementa dos vistas que presentan los nodos de cada uno de los tipos de contenido implementados por el módulo. 112 6.13.5 Otras Funciones Importantes Implementadas hook_perm Mediante este hook definimos los permisos que proporciona este módulo para determinar que acciones puede realizar cada rol. Los permisos son los siguientes: • create party event • create party woe • view party hook_access Mediante este hook definimos los permisos que ha de tener un rol para poder crear, actualizar y borrar un nodo del tipo de contenido implementado por el módulo. hook_user En el momento de eliminar un usuario deberemos eliminar la información de los nodos de asistencia y de las partys de dicho usuario. function party_get_event Función que devuelve los eventos que tienen alguna asistencia sin asignar aun, son del tipo pasado por parámetro y la fecha de inicio es mayor que la fecha actual. function party_get_assistances Función que devuelve el listado de asistencias sin asignar para un evento pasado por parámetro y para unos jobs concretos. Esta función se usa a la hora de crear el paso 3 de la creación de partys o adición de miembros a una party, mediante un bucle, esta función y la tabla assistance_jobs podemos crear todos los fieldsets de los jobs en pocas lineas de código. 113 function get_partys Función que devuelve el código html que mostrará todas las partys de un evento pasado por parámetro. function party_get_event_js Función que devuelve los eventos que tienen alguna party creada function party_get_event_nofull Función que devuelve los eventos que tienen alguna party creada sin llenar y asistencias pendientes. function party_get_partys Función que devuelve el nombre de las partys de un evento. function party_get_partys_nofull Función que devuelve el nombre de las partys de un evento que no estén llenas. function get_partys_build Función que devuelve el codigo html que mostrará la party del evento pasada por parámetro. function get_members_party Función que devuelve los miembros de una party de un evento. 114 6.14 Desarrollo de Temas en Drupal Los temas en Drupal estan formados por cuatro tipos de archivos, el archivo .info (archivo único) los archivos de plantilla .tpl.php, los archivos de hojas de estilo .css y el archivo template.php. En el archivo .info describe las características básicas del tema, los parámetros que se pueden definir en el archivo son los siguientes: • name, nombre del tema. • description, descripción del tema, • screenshot, ruta de la imagen que contiene la imagen previa del tema. • core, versión de Drupal para el que esta desarrollado el tema. • project, nombre del proyecto. • Engine, motor de plantilla utilizado. • version, versión de la plantilla. • regions[], vector con las regiones disponibles en el tema. • features[], vector con las funciones disponibles en el área de administración del tema. • stylesheets[], define las hohas de estilo que utiliza el tema. Los archivos de plantilla definen la presentación mediante un código php. Como mínimo todos los temas deben tener el archivo page.tpl.php que es el archivo que define la estructura de todas las páginas del sitio. A parte de este archivo podemos sobrescribir cualquier plantilla implementada por otro módulo para modificar su presentación, por ejemplo si queremos modificar la presentación de los nodos en general deberíamos implementar el archivo node.tpl.php. Los archivos .css se utilizan para definir los estilos que utilizarán las plantillas para complementar su presentación. El archivo template.php se usa para definir funciones que después son usadas por los archivos tpl.php. 115 6.15 Desarrollo del Tema Propio Este tema está compuesto por cinco archivos, el archivo .info donde definimos las características básicas del tema, dos plantillas una para las páginas y otra para los nodos y por último el archivo .css donde definimos los estilos usados por los dos archivos de plantilla. Se decide crear un tema a tres columnas, una central para el contenido más dos columnas una a cada lado para colocar los bloques creados en el sitio web. Además tendremos las zonas de cabecera y pié de página también disponibles. 6.15.1 Definición del Tema Archivo .info En el archivo .info definimos cuatro regiones en las que podremos colocar bloques, superior, izquierda, derecha e inferior. También añadimos las siguientes opciones de administración: • logo. • Name. • node_user_picture. • comment_user_picture. • search. • favicon. • primary_links. • secondary_links. 116 6.15.2 Creación de Plantillas page.tpl.php Como hemos comentado anteriormente la página estará formada por tres columnas, una cabecera y un pié de página. En la cabecera únicamente imprimiremos el logo del sitio, el logo es una imagen del ancho de las tres columnas que presenta el nombre de la supuesta guild dueña del sitio. En la zona central, donde tenemos las tres columnas dejamos las columnas laterales para imprimir solamente los bloques que decidamos colocar encada una de ellas, mientras que en la columna central se imprimirá todo el resto de contenido del sitio. Para un correcto funcionamiento del sitio debemos imprimir en la cabecera de la página html (no confundir con la cabecera de la página mostrada) los scripts y archivos css que se utilicen en el sitio, pues de otra forma no podrían ser utilizados. Finalmente también es necesario imprimir la variable $closure, es una variable utilizada por varios módulos para añadir scripts y código vario de utilidad para el módulo. node.tpl.php En realidad en esta plantilla mantenemos la estructura de presentación de la plantilla proporcionada por el módulo node, con la única diferencia de que no mostramos el enlace del término de taxonomía en caso de que el nodo lo tenga. Esto no es más que una decisión de tipo estético. 6.15.3 Creación del Archivo Template.php En este archivo sobrescribiremos una única función, la función de breadcrumb, por defecto Drupal separa los enlaces del breadcrumb por los carácteres “>>” con nuestra función hacemos que los enlaces vayan separados por el contenido de un array que contiene los siguientes símbolos: 117 $char[0] = ' <(o.o<) '; $char[1] = ' ^(o.o)^ '; $char[2] =' (>o.o)> '; Estos símbolos son un guiño a unos emoticonos que se suelen usar en varios juegos online para simular una ola de celebración. 118 7. Evaluación Las partes implementadas mediante módulos disponibles en la comunidad de Drupal no serán evaluados en esta zona, ya que su utilización se resume prácticamente a la definición de tipos de contenido y damos por hecho que módulos tan consolidados como son CCK y Views no van a presentar problemas en su utilización. Por estas razones la evaluación del sitio web se va a centrar en los módulos propios desarrollados. 7.1 Evaluación del Módulo Achievements 7.1.1 Creación Edición Eliminación y Presentación de Nodos La primera prueba a realizar es la de comprobación del correcto funcionamiento a la hora de crear un nodo del tipo de contenido logro, así que para comprobarlo creamos un nodo y miramos que efectivamente la información almacenada en la base de datos es la correcta y que a la hora de visualizar el nodo la información es bien recuperada de la base de datos. Lo siguiente que se prueba es la validación de entradas correctas en los campos del formulario de creación y edición, hace falta comprobar que si intentamos introducir por ejemplo un número negativo o una cadena en un campo numérico la creación del nodo no se produzca y nos indique el error. Otra prueba que debemos realizar es a la hora de modificar un nodo que el comportamiento sea el correcto cuando modificas la imagen y cuando no, cuando la modificamos la ruta de la imagen debe cambiar, mientras que si no se indica una imagen nueva la ruta no debe ser cambiada por una cadena vacía. También se ha comprobado que al modificar el valor de la puntuación de un logro, las puntuaciones de los usuarios que tienen ese logro asignada es modificada. Se comprueba que si el nodo no tiene ninguna imagen asignada en su presentación se muestre una imagen comodín para mantener la estructura del nodo. 119 Por último, se ha comprobado que cuando se elimina un nodo del tipo logro, la información es eliminada de todas las tablas necesarias y que las puntuaciones de los usuarios que tenían asignado ese logro es actualizada. 7.1.2 Asignación y Desasignación de los Logros Lo primero que se prueba es que si no existe ningún logro creado no nos deja elegir ningún usuario en el formulario de asignación/desasignación. La siguiente prueba mira que los usuarios que se muestran en los formularios de asignación/desasignación realmente pertenezcan a los roles que se indicaron en el área de administración del módulo. También se comprueba que la lista de logros presentada en los formularios de asignación/desasignación sean los que deben, es decir en el de asignación los logros que aun no tiene asignados el usuario y en el de desasignación los que ya tiene asignados. Otra prueba que se ha realizado es que la puntuación del usuario se actualice correctamente con la asignación y desasignación de los logros. Se comprueba que tras la desasignación de todos los logros de un usuario este es eliminado de la tabla que guarda las puntuaciones de cada usuario. Por último se comprueba que en el perfil del usuario se muestren los logros y la puntuación correcta que tiene el usuario. 7.1.3 Funcionamiento de los Bloques Se comprueba que en todo momento la información mostrada por el bloque Hall of Fame es la correcta. 120 7.2 Evaluación del Módulo Castle Control 7.2.1 Creación Edición Eliminación y Presentación de Bloques La primera prueba es que tras crear un bloque mediante el formulario de creación, el bloque está realmente disponible en el área de administración de bloques. Una vez creado el bloque se comprueba que la información guardada en la base de datos es la correcta. Otra prueba realizada es que usando el formulario de modificación del bloque, el bloque se actualice correctamente. También se prueba que no se puedan añadir dos castillos con el mismo nombre. Otra prueba que se realiza es que después de eliminar un bloque, deje de mostrarse si se estaba mostrando, que desaparezca del área de administración y que se borre toda la información relativa a él en la base de datos. Por último se prueba que no deje crease el bloque si se intenta introducir información no válida en los campos de los formularios como cadenas en valores numéricos, campos vacíos etc. 7.3 Evaluación del Módulo Guild Shop 7.3.1 Creación Edición Eliminación y Presentación de Nodos La primera prueba a realizar es la de comprobación del correcto funcionamiento a la hora de crear un nodo del tipo de contenido logro, así que para comprobarlo creamos un nodo y miramos que efectivamente la información almacenada en la base de datos es la correcta y que a la hora de visualizar el nodo la información es bien recuperada de la base de datos. Lo siguiente que se prueba es la validación de entradas correctas en los campos del formulario de creación y edición, hace falta comprobar que si intentamos introducir por ejemplo un número negativo o una cadena en un campo numérico la creación del nodo no se produzca y nos indique el error. 121 Otra prueba que debemos realizar es a la hora de modificar un nodo que el comportamiento sea el correcto cuando modificas la imagen y cuando no, cuando la modificamos la ruta de la imagen debe cambiar, mientras que si no se indica una imagen nueva la ruta no debe ser cambiada por una cadena vacía. Al borrar un nodo hemos de comprobar que realmente la información es eliminada de la base de datos. También se comprueba que si un objeto no tiene stock no se muestra la opción de comprar dicho objeto. 7.3.2 Uso del Área de Administración Se comprueba que al introducir un stock en el área de administración a uno de los items este stock se ve reflejado tanto en la base de datos como en la presentación del nodo. Se mira que al añadir crédito a un usuario mediante el área de administración este saldo se le ve reflejado tanto en la base de datos como en el sitio web. Por último se comprueba que los formularios del área de administración no dejen que las entradas de sus campos contengan información no válida. 7.3.3 Realización de Compras Se comprueba que el proceso de compra se realice con normalidad, es decir, que cuando seleccionas un item a comprar y rellenas el formulario de compra que al enviarlo si intentas comprar mayor cantidad del stock restante o no te alcanza tu saldo para realizar la compra no te deje hacerlo. La id del item que se está comprando se recoge como parámetro a través de la url, por este motivo se comprueba que si cambias la id del item por una de un item sin stock o por una id que corresponda a un nodo que no es del tipo item de tienda no muestre el formulario de compra. 122 También hace falta validar que no se puedan introducir valores no permitidos en el formulario de compra, como un número negativo por ejemplo. Hay que comprobar que después de realizar una compra se crea correctamente el tipo de contenido con la información relativa a la compra y que esta información es la correcta. Hay que comprobar que después de una compra tanto el stock del item comprado como el saldo del usuario que realiza la compra se actualicen correctamente. 7.3.4 Funcionamiento de los Bloques Se comprueba que los bloques últimas compras realizadas, últimos items añadidos e información de usuario presentan la información correctamente. 7.4 Evaluación del Módulo Assistance 7.4.1 Creación Edición y Eliminación de Nodos Se comprueba que no deje a un usuario realizar dos nodos de asistencia para un mismo evento. Se comprueba que no deje utilizar un personaje para un evento si otra persona ya ha creado una asistencia para ese evento con ese personaje. Otra prueba que se realiza el correcto comportamiento a la hora de editar un nodo que ya estaba asignado a una party, si este el caso, como se ha modificado ya sea el evento o el personaje que el usuario piensa utilizar, dicha asistencia debe ser eliminada de la party y puesta como pendiente de asignar de nuevo. Cuando se actualice uno de los nodos se ha de comprobar que tanto la condición de no asistir ya ha dicho evento como la de que el personaje no este en uso se han de comprobar. 123 Al eliminar un nodo su este estaba asignado a una party se ha de comprobar que desaparece de dicha party. También habrá que comprobar que la información relativa al nodo desaparece de la base de datos. Hay que comprobar que cuando se crea un nodo del tipo asistencia su estado se pone automáticamente a 0 es decir que no está signado. 7.5 Evaluación del Módulo Party 7.5.1 Creación de las Partys Lo primero que debemos comprobar es que en el formulario de creación de partys el listado de eventos y asistencias que se nos presenten sean realmente los que tocan a ese formulario, es decir en el formulario de woes solo eventos de woes y una vez seleccionado un evento en concreto que únicamente se nos presenten las asistencias de ese evento. También es necesario comprobar que las asistencias se ordenan correctamente por el job del personaje, es decir, que una asistencia con el job High Priest por ejemplo, este en el fieldset correspondiente. También hace falta comprobar que a la hora de crear la party si se intentan añadir más de 12 asistencias de error, ya que las partys son de un máximo de 12 personas. Como solo habían 3 personajes de roster creados y no se podían repetir personajes, se redujo el limite a 2 asistencias para comprobar el resultado al añadir 3 asistencias, se da por hecho que si funciona con un limite de 2 también funcionará con un limite de 12. Otra prueba a realizar es la comprobación de que la party una vez creada esta bien almacenada en la base de datos. 7.5.2 Adición y Eliminación de Asistencias a las Partys Lo primero es comprobar que en los formularios de adición y eliminación de asistencias a las partys los eventos y partys que se muestran son los correctos, es decir, los eventos con 124 partys creadas y en el caso de adición que no estén llenas, que las partys mostradas después de haber elegido el evento sean las correctas y que una vez elegida la party las asistencias pendientes o asignada, dependiendo del formulario que se trate también sean las que corresponden. También hay que comprobar que a la hora de añadir miembros a una party no se supere el limite de 12 personas, se ha realizado el mismo procedimiento que en el formulario de creación de partys para comprobar este aspecto. Otra prueba necesaria es la comprobación de la eliminación de la party de la base de datos cuando ya no le quedan miembros asignados. 7.5.3 Presentación de las Partys En este apartado solo hay que comprobar que los eventos que salgan en la lista sean los que tienen alguna party creada y que la información de las partys creadas para dicho evento y los miembros de las mismas sean los correctos. 7.6 Evaluaciones sobre Permisos y Visibilidad Hay que comprobar que las visibilidades de las secciones y contenidos para cada uno de los roles sea la correcta. También hay que tener en cuenta que cada rol pueda realizar las funciones que tiene asignadas pero no las del resto. Otra prueba a realizar es que los bloques solo sean visibles para los roles correspondientes. 125 126 8. Apsectos Legales En cuanto a la protección de datos de los usuarios La gran expansión de los medios digitales; Internet; la telefonía móvil y la televisión interactiva en el marco de las comunicaciones han traído consigo la proliferación de negocios a través de estos medios electrónicos. El tráfico de datos generados en las comunicaciones electrónicas aumenta diariamente poniendo en peligro la intimidad de las personas. El respeto a los usuarios de estos negocios exige que el tráfico de datos generados en todas las comunicaciones electrónicas se vea sometido a prescripciones legales para facilitar su aplicación y desarrollo. Así, una de las preocupaciones de cualquier usuario de la red, es asegurarse, cuando navega por Internet, que los datos que suministra -en un chat, en foros de discusión, o en compras- no serán capturados, copiados, modificados o utilizados sin su previo consentimiento. Por ello entiendo que la confianza del usuario en la preservación de su intimidad en la sociedad digital, es básica para el desarrollo de la sociedad de la información y de todas las actividades lícitas que se llevan en la red. El tratamiento de los datos personales esta regulado por la LOPD ( Ley Órganica de Datos de Carácter Personal). En este sitio web los únicos datos de carácter personal que se piden son el nombre (en este caso ni siquiera es obligatorio darlo), un rango de edad y la zona horaria, durante la fase de solicitud de entrada a la guild, dichos datos van a ser eliminados en cuanto la votación de la solicitud termine, por tanto a efectos prácticos los únicos datos personales que realmente se van a mantener almacenados en la base de datos son las direcciones de correo de registro de los usuarios. Por ese motivo y basándose en el articulo 3/a de la LOPD referente a las definiciones de los diferentes aspectos sobre la lay que dice: a) Datos de carácter personal: cualquier información concerniente a personas físicas identificadas o identificables. 127 Se considera que no afecta al sitio puesto que una persona no puede ser identificada únicamente por una dirección de correo electrónico. Así pues también se considera que la dirección mail de un usuario sin ningún tipo más de información a cerca del usuario tampoco es una información apta para distribuir y vender puesto que no presenta ninguna información referente a gustos, inclinaciones o otros tipos de datos de carácter personal. En cuanto al contenido publicado La propiedad intelectual protege las creaciones originales literarias, artísticas o científicas expresadas en cualquier medio, tales como libros, escritos, composiciones musicales, obras dramáticas, coreografías, obras audiovisuales, esculturas, obras pictóricas, planos, maquetas, mapas, fotografías, programas de ordenador y bases de datos. También protege las interpretaciones artísticas, los fonogramas, las grabaciones audiovisuales y las emisiones de radiodifusión. Aunque si es cierto que los usuarios pueden llegar a publicar contenidos de creación propia la mayoría de contenidos es posible que sean una reproducción de sitios como wikis o foros públicos, por tanto no se contempla en ningún momento que dichos contenidos puedan tener ningún tipo de propiedad intelectual ni copyright, ya que excepto en el caso de los videos de woes, se considera que los contenidos publicados no se corresponden con ninguno de los tipos citados anteriormente. Y en cuanto a los videos de WoEs únicamente se publicarán videos hospedados en sitios de hosting como youtube y megavideo, que ya regulan dichos derechos. 128 9. Costes Costes de distribución Dada la naturaleza del portal creado y con las herramientas que se han utilizado para desarrollarlo, la aplicación se distribuirá bajo licencia GPL. La Licencia Pública General de GNU o más conocida por su nombre en inglés GNU General Public License o simplemente sus siglas del inglés GNU GPL, es una licencia creada por la Free Software Foundation en 1989 (la primera versión), y está orientada principalmente a proteger la libre distribución, modificación y uso de software. Su propósito es declarar que el software cubierto por esta licencia es software libre y protegerlo de intentos de apropiación que restrinjan esas libertades a los usuarios. Por tanto el desarrollo, el mantenimiento y la distribución de esta aplicación no suponen ningún coste monetario, únicamente costes de índole personal, como puede ser la dedicación temporal al desarrollo de la aplicación. Costes de puesta en marcha El uso de la aplicación si podría suponer un gasto para los usuarios del portal siempre y cuando no dispongan de un servidor web propio, que dado el ámbito al que está destinada la aplicación no es descartable que ocurra. En el caso de que no se disponga de un servidor propio, los usuarios necesitarán contratar un servicio de hosting, por recomendaciones de la comunidad de usuarios de Drupal, se recomeinda el uso de la empresa de hosting https://dinahosting.com/ , la cual ofrece varias opciones de hosting válidos para nuestro tipo de aplicación, recomendando nosotros https://dinahosting.com/hosting/hosting-profesional que proporciona los requerimientos básicos necesarios como servidor php, servidor para bases de datos Mysql copias de seguridad etc. El coste de contratación del servicio depende de la cantidad de tiempo por el que la contrates, a mayor tiempo menor precio, el precio máximo del servicio es de 11,42€ mensuales en un servidor corriendo bajo Linux y de 17,43€ en un servidor corriendo bajo Windows. 129 A parte del servidor web requerimos de un dominio, este dominio podemos registrarlo con la misma compañía con la que recomendamos para el servidor web. Las tarifas para el dominio web varían según el dominio y el tiempo por el cual registremos el dominio, siendo el precio máximo a pagar de 1,17€ al mes. Así pues utilizando esta compañía de hosting el coste total de la puesta en marcha de la aplicación seria de 12.59€ mensuales en el caso de usar un servidor trabajando sobre Linux y de 18,6€ mensuales si se tratase de un servidor que funcionase sobre Windows. 130 10. Conclusiones Los CMS son potentes herramientas que nos permiten crear un sitio web muy completo sin necesidad de tener conocimientos de programación, pero aun se convierten en una herramienta mucho más potentes si se poseen dichos conocimientos, ya que pasamos de tener unas herramientas limitadas, si es que se puede considerar limitado los miles de módulos que existen para un CMS como Drupal, a tener una estructura básica sobre la que ampliar funcionalidades sin tener que preocuparnos por las funciones más básicas ya que vienen implementadas directamente por el framework. Una de las grandes ventajas de usar Drupal, que aunque quizá no sea el CMS más popular entre los usuarios de Internet, si creo que se puede decir que es el más versátil y flexible, aunque esto haga que su curva de aprendizaje sea mucho más compleja que la de otros CMS, motivo principal por el que posiblemente Drupal no es tan popular como sus competidores. Gracias a esta versatilidad y la gran cantidad de módulos ya implementados hace que se puedan crear sitios de los estilos y funcionalidades más diversas, como por ejemplo este proyecto, una aplicación a priori poco común, pero que para un sector muy específico puede ser de gran uso, sobretodo porque no existen aplicaciones de este tipo y se suele resolver siempre mediante el uso de un foro, con las limitaciones que esto conlleva. Además el sistema modular y de hooks hace que sea un entorno muy cómodo para el desarrollo de nuevas funcionalidades, ya que tienes unas pautas muy marcadas de como realizar las cosas. Además el sistema de hooks te permite dejar algunas cosas en manos del propio framework ya que si no implementas un hook específico para tu módulo simplemente se pasará a ejecutar el hook del núcleo. Otra gran ventaja del uso de Drupal es su uso por defecto de archivos CSS para sus plantillas de presentación ya que así se puede separar perfectamente la presentación de la funcionalidad de los módulos desarrollados. La mayor problemática de trabajar con Drupal es la escasa información en algunos ámbitos de su desarrollo, además de usar sintaxis propias para cosas como la creación de tablas o definición de formularios html, que si hace que sea mucho más amigable el código implementar, también provoca que en el momento que quieres realizar alguna función que se sale de lo habitual es muy complicado encontrar documentación sobre ello, lo que 131 provoca que si los conocimientos sobre programación web y funcionamiento interno de Drupal no son muy extensos aparezcan cosas que te resulten imposibles de implementar para Drupal pero que por ejemplo se realizarían fácilmente para una web realizada íntegramente en html y php. 132 11. Planificación Temporal Intervalo Tarea 8 – 26 de Octubre Familiarización con Drupal y realización de varios videotutoriales online. 27 de Octubre – 12 Noviembre Definición de especificaciones del proyecto 13 Noviembre – 17 Diciembre Implementación de tipos de contenido medienat CCK y Vistas mediante Views 10 Enero – 25 febrero Aprendizaje de desarrollo de Módulos en Drupal y realización de tutoriales sobre ello. 7 marzo – 30 de abril Implementación de módulos propios 2 – 6 Mayo Implementación del tema propio 19 – 31 Mayo Redacción de la memoria. 133 134 12. Bibliografia Libros “Pro Drupal Develoopment”, J.K.VanDyk, Apress, 2009 “Aprende Drupal 6 nivel inicial” Fran Gil y Adolfo Romaní, Forcontu, 2010 “Aprende Drupal 6 nivel intermedio” Fran Gil y Adolfo Romaní, Forcontu, 2010 “Aprende Drupal 6 nivel Avanzado” Fran Gil y Adolfo Romaní, Forcontu, 2010 Webs http://drupal.org/ http://drupal.org.es/ http://www.miguelmanchego.com http://www.documentados.com http://www.desarrolloweb.com http://www.illasaron.com/ http://api.drupal.org http://es.php.net 135 136 12. Anexos 12.1 Módulo Achievements Archivo achievements.info ; $Id$ name= "Achievements" description = "Module to add achievements to the drupal." core = 6.x php = 5.1 dependencies[] = views package = Ragnarok Guild Archivo achievements.install <?php // $Id$ /** * Implementation of hook_schema() */ function achievements_schema(){ $schema['achievements_list'] = array( 'description' => 'Stores achievements information.', 'fields' => array( 'nid' => array( // id of the node set 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'vid' => array( //version of the node set 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'description_achievement' => array( //description of the achievement 137 'type' => 'text', 'not null' => TRUE, 'size' => 'big', ), 'value' => array( //punctuation of the achievement 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'picture' => array( //picture of the achievement 'type' => 'varchar', 'length' => 255, 'default' => '', ), ), //primary key formed by nid field 'primary key' => array('nid'), ); $schema['achievements_users'] = array( 'description' => 'Stores relationship between achievements and users.', 'fields' => array( 'nid' => array( //field referred to achievement identifier 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'uid' => array( //field referred to user identifier 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), ), 138 //primary key formed by node identifier and user identifier 'primary key' => array('nid', 'uid'), ); $schema['achievements_users_punctuation'] = array( 'description' => 'Stores de punctuation of each user.', 'fields' => array( 'uid' => array( //field referred to user identifier 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'punctuation' => array( //User accumulated punctuation 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), ), //primary key formed by user identifier 'primary key' => array('uid'), ); return $schema; } /** * Implementation of hook_install() */ function achievements_install(){ drupal_install_schema('achievements'); } /** * Implementation of hook_uninstall() */ function achievements_uninstall(){ drupal_uninstall_schema('achievements'); } 139 Archivo achievements.achievements.module /** * Implementation of hook_load() */ function achievements_load($node){ $additions = db_fetch_object(db_query('SELECT description_achievement, value FROM {achievements_list} WHERE vid = %d', $node->vid)); return $additions; } /** * Implementation of hook_view() */ function achievements_view($node, $teaser = FALSE, $page = FALSE){ global $base_root, $base_path; $node = node_prepare($node, $teaser); $picture = db_result(db_query('SELECT picture FROM {achievements_list} WHERE nid = %d',$node->nid)); if($picture!=''){ $pic = $base_root.$base_path.$picture; } else{ $pic = $base_root.$base_path.'sites/default/files/noimg90x120.jpg'; } $node->content['node'] = array( '#value' => theme('achievements_node', $pic, $node>value,$node->description_achievement ), '#weight' => 1, ); return $node; } /** * Implementation of hook_theme() */ function achievements_theme(){ return array( 'achievements_node' => array( 'template' => 'achievements', 'arguments' => array('picture' => 0, 'value' => 0, 'description_achievement' => 0 ), 140 ), ); } /** * Implementation of hook_init(). */ function achievements_init() { drupal_add_css(drupal_get_path('module', 'achievements') .'/achievements.css'); } /** * Implementation of hook_perm() */ function achievements_perm(){ return array( 'create achievement', 'delete own achievement', 'delete any achievement', 'edit own achievement', 'edit any achievement', 'administer achievements', ); } /** * Implementation of hook_acces() */ function achievements_access($op, $node, $account){ switch ($op){ case 'create': return user_access('create achievement', $account) ? TRUE : NULL; case 'update': return user_access('edit any achievement', $account) || (user_access('edit own achievement', $account)&&($account->uid == $node>uid)) ? TRUE : NULL; case 'delete': return user_access('delete any achievement', $account) || (user_access('delete own achievement', $account)&&($account->uid == $node->uid)) ? TRUE : NULL; } } /** * Implementation of hook_menu() 141 */ function achievements_menu(){ $items['admin/settings/achievements'] = array( 'title' => t('Achievements settings'), 'description' => t('Select roles'), 'page callback' => 'drupal_get_form', 'page arguments' => array('achievements_select_roles'), 'access arguments' => array('administer achievements'), 'type' => MENU_NORMAL_ITEM, 'file' => 'achievements.admin.inc', ); $items['admin/build/achievements'] = array( 'title' => t('Achievements build'), 'description' => t('Assign achievements'), 'page callback' => 'drupal_get_form', 'page arguments' => array('achievements_assign_achievement'), 'access arguments' => array('administer achievements'), 'type' => MENU_NORMAL_ITEM, 'file' => 'achievements.admin.inc', ); $items['admin/build/achievements/assign_achievements'] = array( 'title' => t('Assign achievements'), 'description' => t('Assign achievements'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'file' => 'achievements.admin.inc', ); $items['admin/build/achievements/deallocated_achievements'] = array( 'title' => t('Deallocate achievements'), 'description' => 'Deallocate achievements.', 'page callback' => 'drupal_get_form', 'page arguments' => array('achievements_deallocate_achievement'), 'access arguments' => array('administer achievements'), 'type' => MENU_LOCAL_TASK, 'file' => 'achievements.admin.inc', ); return $items; } /** 142 * Implementation of hook_user() */ function achievements_user($op, &$edit, &$user, $category = NULL){ switch($op){ case 'delete': db_query('DELETE FROM {achievements_users} WHERE uid = %d', $user>uid); db_query('DELETE FROM {achievements_users_punctuation} WHERE uid = %d', $user->uid); case 'categories': $data = array(); $data[]= array( 'name' => achievements, 'title' => t('Achievements'), ); return $data; case 'view': $result = db_query("SELECT punctuation FROM {achievements_users_punctuation} WHERE uid = %d",$user->uid); $row = db_result($result); $user->content['achievements'] = array(); $user->content['achievements'] = array( '#type' => 'user_profile_category', '#attributes' => array ('class' => 'user-achievements'), '#title' => t('Achievements'), '#weight'=> 0, ); $user->content['achievements']['punctuation'] = array( '#type' => 'user_profile_item', '#title' => t('Total Achievement Punctuation'), '#value' => $row, '#weight'=> 2, ); $result = db_query("SELECT al.nid, n.title FROM ({achievements_list} al INNER JOIN {node} n ON al.nid = n.nid) INNER JOIN {achievements_users} au ON n.nid = au.nid WHERE au.uid = %d", $user->uid); while( $row = db_fetch_array($result)){ 143 $user->content['achievements'][$row['nid']] = array( '#type' => 'user_profile_item', '#value' => l($row['title'],drupal_get_path_alias('node/'.$row[nid])), '#weight'=> 1, ); } } } /** * Implementation of hook_block() */ function achievements_block($op='list', $delta=0, $edit=array()){ switch($op){ case 'list': $blocks[0]['info'] = t('Hall of Fame'); $blocks[0]['cache'] = BLOCK_NO_CACHE; return $blocks; case 'view': $result= db_query_range("SELECT u.name, aup.punctuation, u.uid FROM {users} u INNER JOIN {achievements_users_punctuation} aup on u.uid = aup.uid ORDER BY punctuation desc",0,5); $count = 1; $items = array(); while($row= db_fetch_array($result)){ $items[] = $count."- ".l($row['name'], 'user/'.$row['uid'])." ".$row['punctuation']." ".t('points'); $count+=1; } $block['subject'] = t('Hall of Fame'); $block['content'] = theme('item_list', $items); return $block; } } /** * Implementation of hook_views_api(). */ 144 function achievements_views_api() { return array( 'api' => 2, 'path' => drupal_get_path('module', 'achievements'), //'path' => drupal_get_path('module', 'achievements') . '/includes', ); } Archivo achievements.admin.inc <?php //$Id$ /** * @file * Resultado de llamadas a páginas de administración para el módulo Achievements */ /* * Funcion para guardar en bd que roles pueden tener logros */ function achievements_select_roles(){ $content_roles_list = user_roles($membersonly = TRUE); $form['achievements_role_list'] = array( '#type' => 'checkboxes', '#title' => t('To assign to a role the ability to add achievements to users of that role'), '#options' => $content_roles_list, '#default_value' => variable_get('achievements_role_list', array('3')), ); return system_settings_form($form); } /* * Funcion que devuelve un array con todos los logros creados. */ function get_achievements(){ $result = db_query("SELECT a.nid, n.title FROM {achievements_list} a INNER JOIN {node} n ON a.nid = n.nid"); while($row = db_fetch_array($result)){ $achievements[ $row['nid']]=$row['title']; 145 } return $achievements; } /* * Funcion que devuelve que usuarios pueden tener logros asignados */ function get_user_achievements(){ $var = variable_get('achievements_role_list', ''); $str; foreach ($var as $clave => $valor) { //para cada valor del array if($valor>0){ //si valor > 0 quiere decir que se le pueden asignar logros if(isset($str)){ //miramos si ya hemos añadido algo al string, en ese caso la nueva id del rol la mirarems en una or en la sentencia sql $str.=" OR ur.rid= ".$valor; } else{ // si es la primera vez no ponemos or $str="ur.rid= ".$valor; } } } $users['blanco']='Escoge un usuario'; $result = db_query("SELECT u.uid, u.name FROM {users} u INNER JOIN {users_roles} ur ON ur.uid = u.uid WHERE ".$str); //miramos el numero de logros totales creados $num_ach = db_result(db_query('SELECT COUNT(*) FROM {achievements_list}')); while($row = db_fetch_array($result)){ //para cada usuario miramos el numero total de logros que tiene asignados, si es menor al total lo añadimos a la lista $ach_user=db_result(db_query('SELECT COUNT(*) FROM {achievements_users} WHERE uid = %d',$row['uid'])); if($ach_user<$num_ach){ $users[ $row['uid']]=$row['name']; } } return $users; } /* * Funcion que implementa la cosntruccion del formulario por pasos para asignar logros 146 */ function achievements_assign_achievement(&$form_state = NULL){ //Miramos si ya estamos en un estado avanzado del formulario o si estamos en el primer paso. $step = isset($form_state['values']) ? (int)$form_state['storage']['step'] : 1; //Guardamos el siguiente paso $form_state['storage']['step'] = $step+1; //ponemos el titulo la descripción y el nombre del boton de envio correspondiente al paso en el que estamos $form['indicator'] = array( '#type' => 'fieldset', '#title'=>t('Step @number', array('@number'=>$step)) ); if($step == 1){ $us = get_user_achievements(); $form['indicator'][$step] = array( '#type' => 'select', '#title' => t('User'), '#options' => $us, '#description'=>t('Choose user.') ); $button_name = t('Next'); } else{ $form['indicator'][$step] = array( '#type' => 'select', '#title' => t('Achievements'), '#options' =>get_assign_list($form_state['values'][1]) , '#description'=>t('Choose achievement.') ); $button_name = t('Submit'); } $form['submit']= array( '#type' => 'submit', '#value' => $button_name, ); 147 if($step ==2){ $form_state['storage']['user']=$form_state['values'][1]; } return $form; } /* * Funcion de validacion del formulario por pasos de asignacion de logros */ function achievements_assign_achievement_validate($form, &$form_state){ if($form_state['values'][1]=='blanco'){ form_set_error (1, t('Choose a user')); } if($form_state['values'][2]=='sin'){ form_set_error (2, t('You cannot assign achievement to this user.')); } } /* * Funcion de submit del formulario por pasos de asignacion de logros */ function achievements_assign_achievement_submit($form, &$form_state){ if($form_state['storage']['step']<3){ return; } $result = db_query("SELECT * FROM {achievements_users_punctuation} WHERE uid = %d",$form_state['storage']['user']); $row =db_fetch_array($result); $cont =count($row); $punct = db_result(db_query("SELECT value FROM {achievements_list} WHERE nid = %d", $form_state['values'][2])); if($cont<2){ db_query("INSERT INTO {achievements_users_punctuation} (uid,punctuation) VALUES (%d, %d)",$form_state['storage']['user'],$punct ); } else{ $punct += $row['punctuation']; db_query("UPDATE {achievements_users_punctuation} SET punctuation = %d WHERE uid = %d", $punct, $form_state['storage']['user']); 148 } db_query("INSERT INTO {achievements_users} (nid,uid) VALUES (%d, %d)",$form_state['values'][2],$form_state['storage']['user']); //borramos storage unset($form_state['storage']); } /* * funcion que devuelve un array con los logros que se le pueden asignar a un usuario */ function get_assign_list($uid){ $result = db_query("SELECT al.nid, n.title FROM ({achievements_list} al INNER JOIN {node} n ON al.nid = n.nid) INNER JOIN {achievements_users} au ON n.nid = au.nid WHERE au.uid = %d", $uid); while($row = db_fetch_array($result)) { $achievements_user[$row['nid']]=$row['title']; } $achievements_list=get_achievements(); if(count($achievements_user)>0){ foreach ($achievements_list as $clave => $valor) { if(!in_array($valor, $achievements_user)){ $achievements[$clave]=$valor; } } } else{ $achievements = $achievements_list; } return $achievements; } /* * Funcion que implementa la cosntruccion del formulario por pasos para desasignar logros */ function achievements_deallocate_achievement(&$form_state = NULL){ //Miramos si ya estamos en un estado avanzado del formulario o si estamos en el primer paso. $step = isset($form_state['values']) ? (int)$form_state['storage']['step'] : 1; //Guardamos el siguiente paso 149 $form_state['storage']['step'] = $step+1; //ponemos el titulo la descripción y el nombre del boton de envio correspondiente al paso en el que estamos $form['indicator'] = array( '#type' => 'fieldset', '#title'=>t('Step @number', array('@number'=>$step)) ); if($step == 1){ $us = get_users_with_achievements(); $form['indicator'][$step] = array( '#type' => 'select', '#title' => t('User'), '#options' => $us, '#description'=>t('Choose user.') ); $button_name = t('Next'); } else{ $form['indicator'][$step] = array( '#type' => 'select', '#title' => t('Achievements'), '#options' =>get_deallocate_list($form_state['values'][1]) , '#description'=>t('Choose achievement.') ); $button_name = t('Submit'); } $form['submit']= array( '#type' => 'submit', '#value' => $button_name, ); if($step==2){ $form_state['storage']['user']=$form_state['values'][1]; } return $form; } /* 150 * Funcion de validacion del formulario para desasginar logros */ function achievements_deallocate_achievement_validate($form, &$form_state){ if($form_state['values'][1]=='sin'){ form_set_error (1, t('Choose a user')); } } /* * Funcion de submit del formulario para desasginar logros */ function achievements_deallocate_achievement_submit($form, &$form_state){ if($form_state['storage']['step']<3){ return; } $user_punct =db_result( db_query("SELECT punctuation FROM {achievements_users_punctuation} WHERE uid = %d",$form_state['storage']['user'])); $punct = db_result(db_query("SELECT value FROM {achievements_list} WHERE nid = %d", $form_state['values'][2])); $user_punct -= $punct; if($user_punct!=0){ db_query("UPDATE {achievements_users_punctuation} SET punctuation = %d WHERE uid = %d", $user_punct, $form_state['storage']['user']); } else{ db_query("DELETE FROM {achievements_users_punctuation} WHERE uid = %d",$form_state['storage']['user']); } db_query("DELETE FROM {achievements_users} WHERE nid = %d AND uid = %d",$form_state['values'][2],$form_state['storage']['user']); //borramos storage unset($form_state['storage']); } /* * Funcion que retorna un array con los usuarios que tienen logros asignados */ function get_users_with_achievements(){ $users['sin']="Escoge un usuario"; 151 $result = db_query("SELECT au.uid, u.name FROM ({achievements_users} au LEFT JOIN {users} u ON au.uid = u.uid)"); while($row = db_fetch_array($result)){ $users[$row['uid']]=$row['name']; } return $users; } /* * Funcion que devuelve array con los logros que tiene asignados un usuario */ function get_deallocate_list($uid){ $result = db_query("SELECT al.nid, n.title FROM ({achievements_list} al INNER JOIN {node} n ON al.nid = n.nid) INNER JOIN {achievements_users} au ON n.nid = au.nid WHERE au.uid = %d", $uid); while($row = db_fetch_array($result)) { $achievements_user[$row['nid']]=$row['title']; } return $achievements_user; } Archivo achievements.tpl.php <div class="achievements-value">Puntuación: <?php print $value; ?></div> <div class="achievements-picture"><img src="<?php print $picture; ?>"></div> <div class="achievements-description_achievement"><b>Descripción:</b> <?php print $description_achievement; ?></div> Archivo achievements.css .achievements-value{ float:right; border-style:dotted; border-width:1px; padding: 7px; border-color: black; font-size: 170%; font-weight: bold; } .achievements-picture{ width: 400px; } 152 12.2 Módulo Assistance Archivo assistance.info ; $Id$ name= "Assistance" description = "Module to create assistance to events" dependencies[] = event dependencies[] = views core = 6.x package = Ragnarok Guild Archivo assistance.install <?php // $Id$ /** * Implementation of hook_schema() */ function assistance_schema(){ $schema['assistance_node'] = array( 'description' => 'Store assistance info', 'fields' => array( 'nid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'vid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'uid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 153 'enid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'job' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'status' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), ), 'primary key' => array('nid'), ); $schema['assistance_job'] = array( 'description' => 'Store jobs name', 'fields' => array( 'id' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'name' => array( 'type' => 'varchar', 'length' => 255, 'default' => '', ), ), 'primary key' => array('id'), ); return $schema; 154 } /** * Implementation of hook_install() */ function assistance_install(){ drupal_install_schema('assistance'); db_query("INSERT INTO {assistance_job} (id, name) VALUES ('1' ,'Knight'), ('2' , 'Crusader'), ('3' , 'Wizard'), ('4' , 'Sage'), ('5' , 'Assassin'), ('6' , 'Rogue'), ('7' , 'Priest'), ('8' , 'Monk'), ('9' , 'BlackSmith'), ('10' , 'Alchemist'), ('11' , 'Hunter'), ('12' , 'Bard'), ('13' , 'Dancer'), ('14' , 'Soul Linker'), ('15' , 'Ninja'), ('16' , 'Super novice'), ('17' , 'Lord Knight'), ('18' , 'Paladin'), ('19' , 'High Wizard'), ('20' , 'Scholar'),('21' , 'Assasin Cross'), ('22' , 'Stalker'), ('23' , 'High Priest'), ('24' , 'Champion'), ('25' , 'Whitesmith'), ('26' , 'Creator'), ('27' , 'Sniper'), ('28' , 'Clown'), ('29' , 'Gypsy'),('30' , 'Star Gladiator'),('31' , 'Gunslinger')"); } function assistance_uninstall(){ drupal_uninstall_schema('assistance'); } Archivo assistance.module <?php // $Id$ /** * Implementation of hook_node_info() */ function assistance_node_info(){ return array( 'assistance_event' => array( 'name' => t('Assitance Event'), 'module' => 'assistance', 'description' => t('Assitance to event.'), 'title label' => t('Assistance'), 'has body' => FALSE, ), 'assistance_woe' => array( 'name' => t('Assitance Woe'), 'module' => 'assistance', 'description' => t('Assitance to woe.'), 155 'title label' => t('Assistance'), 'has body' => FALSE, ), ); } /** * Implementation of hook_form(&$node, $form_state) */ function assistance_form(&$node, $form_state){ $type = node_get_types('type', $node); if ($type->has_title){ $form['title'] = array( '#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => $node->title, ); } global $user; $form['uid'] = array( '#type' => 'hidden', '#value' => $user->uid, ); if($type->type == 'assistance_event'){ $options = get_future_events('event'); $title = 'Event'; } else{ $title = 'WoE'; $options = get_future_events('woe'); } $form['enid'] = array( '#type' => 'select', '#title' => t($title), '#default_value' => isset ($node->enid) ? $node->enid : 'null', '#options' => $options, '#description' => t('Select a event'), 156 ); $var = variable_get('assistance_types', ''); if($var!=''){ $form['job'] = array( '#type' => 'select', '#title' => t('Pj'), '#default_value' => isset ($node->job) ? $node>job : 'null', '#options' => get_node_info($var), '#description' => t('Select a pj'), ); } return $form; } /** * Implementation of hook_validate() */ function assistance_validate($node ){ if($node->enid == 'null'){ form_set_error('enid', t('Choose one')); } if($node->job == 'null'){ form_set_error('job', t('Choose one')); } } /** * Implementation of hook_insert() */ function assistance_insert($node){ $error = FALSE; $uid = db_result(db_query('SELECT uid FROM {assistance_node} WHERE enid = %d AND uid = %d', $node->enid, $node->uid)); if($uid==$node->uid){ global $base_root, $base_path; drupal_set_message(t("You've confirmed your Assistance in this event"), 'error'); db_query("DELETE FROM {node} WHERE nid = %d", $node>nid); db_query("DELETE FROM {node_revisions} WHERE nid = %d", $node->nid); $error = TRUE; 157 drupal_goto($base_root.$_SERVER['REQUEST_URI']); } $var = variable_get('assistance_types', ''); if($var!=''){ $job = db_result(db_query('SELECT job FROM {assistance_node} WHERE enid = %d AND job = %d', $node->enid, $node>job)); if($job == $node->job){ global $base_root, $base_path; drupal_set_message(t("This character is already in use"), 'error'); db_query("DELETE FROM {node} WHERE nid = %d", $node->nid); db_query("DELETE FROM {node_revisions} WHERE nid = %d", $node->nid); $error = TRUE; drupal_goto($base_root.$_SERVER['REQUEST_URI']); } } if($error == FALSE){ db_query('INSERT INTO {assistance_node} (nid, vid, uid, enid, job) VALUES (%d,%d,%d,%d,%d)', $node->nid,$node->vid,$node>uid,$node->enid,$node->job); } } /** * Implementation of hook_update() */ function assistance_update($node){ $var = variable_get('assistance_types', ''); if($var!=''){ $job = db_result(db_query('SELECT job FROM {assistance_node} WHERE enid = %d AND job = %d', $node->enid, $node>job)); if($job == $node->job){ global $base_root, $base_path; drupal_set_message(t("This character is already in use"), 'error'); $error = TRUE; drupal_goto($base_root.$_SERVER['REQUEST_URI']); } } 158 if($error == FALSE){ if(module_exists('party')){ if($node->status == 1){ $result = db_result(db_query("SELECT party_name FROM {party} WHERE anid = %d",$node->nid)); db_query("DELETE FROM {party} WHERE anid = %d",$node>nid); db_query("UPDATE {party_members} SET number = number-1 WHERE enid = %d AND party_name = '%s'", $node->enid, $result); } $num = db_result(db_query("SELECT number FROM {party_members} WHERE enid = %d AND party_name = '%s'", $node->enid, $result)); if($num==0){ db_query("DELETE FROM {party_members} WHERE enid = %d AND party_name = '%s'", $node->enid, $result); } } db_query('UPDATE {assistance_node} SET enid = %d, job = %d, status = 0 WHERE nid = %d', $node->enid,$node->job, $node->nid); } if(module_exists('party')){ if($node->status == 1){ $result = db_result(db_query("SELECT party_name FROM {party} WHERE anid = %d",$node->nid)); db_query("DELETE FROM {party} WHERE anid = %d",$node>nid); db_query("UPDATE {party_members} SET number = number-1 WHERE enid = %d AND party_name = '%s'", $node->enid, $result); } $num = db_result(db_query("SELECT number FROM {party_members} WHERE enid = %d AND party_name = '%s'", $node->enid, $result)); if($num==0){ db_query("DELETE FROM {party_members} WHERE enid = %d AND party_name = '%s'", $node->enid, $result); } } db_query('UPDATE {assistance_node} SET enid = %d, job = %d, status = 0 WHERE nid = %d', $node->enid,$node->job, $node->nid); } /** * Implementation of hook_delete() */ function assistance_delete($node){ db_query('DELETE FROM {assistance_node} WHERE nid = %d',$node>nid); 159 if(module_exists('party')){ if($node->status == 1){ $result = db_result(db_query("SELECT party_name FROM {party} WHERE anid = %d",$node->nid)); db_query("DELETE FROM {party} WHERE anid = %d",$node>nid); db_query("UPDATE {party_members} SET number = number-1 WHERE enid = %d AND party_name = '%s'", $node->enid, $result); } $num = db_result(db_query("SELECT number FROM {party_members} WHERE enid = %d AND party_name = '%s'", $node->enid, $result)); if($num==0){ db_query("DELETE FROM {party_members} WHERE enid = %d AND party_name = '%s'", $node->enid, $result); } } } /** * Implementation of hook_load() */ function assistance_load($node){ $additions = db_fetch_object(db_query('SELECT uid, enid, job FROM {assistance_node} WHERE vid = %d',$node->vid)); return $additions; } /** * Implementation of hook_view() */ function assistance_view($node, $teaser = FALSE, $page = FALSE){ $node = node_prepare($node, $teaser); $node->content['uid'] = array( '#value' => theme('assistance_uid', $node->uid), '#weight' => 1, ); $node->content['enid'] = array( '#value' => theme('assistance_enid', $node->enid), '#weight' => 2, ); $node->content['job'] = array( '#value' => theme('assistance_job', $node->job), '#weight' => 3, ); 160 return $node; } /** * Implementation of hook_theme() */ function assistance_theme(){ return array( 'assistance_uid' => array( 'arguments' => array('uid'=>0), ), 'assistance_enid' => array( 'arguments' => array('enid'=>0), ), 'assistance_job' => array( 'arguments' => array('job'=>0), ), ); } function theme_assistance_uid($uid){ $name = db_result(db_query("SELECT u.name FROM {users} u LEFT JOIN {assistance_node} pa ON u.uid = pa.uid WHERE u.uid = %d", $uid)); $output = '<div class="assistance-uid">'; $output .= 'Usuario: '.$name; $output .= '</div>'; return $output; } function theme_assistance_enid($enid){ $title = db_result(db_query("SELECT n.title FROM {node} n LEFT JOIN {assistance_node} pa ON n.nid = pa.enid WHERE n.nid = %d", $enid)); $output = '<div class="assistance-enid">'; $output .= 'Evento: '.l($title, drupal_get_path_alias('node/'.$enid)); $output .= '</div>'; return $output; } function theme_assistance_job($job){ $var = variable_get('assistance_types', ''); if($var!=''){ $user = db_result(db_query("SELECT n.title FROM {node} n LEFT JOIN {assistance_node} pa ON n.nid = pa.job WHERE n.nid = %d", $job)); 161 $output = '<div class="assistance-job">'; $output .= 'Personaje: '.l($user, drupal_get_path_alias('node/'.$job)); $output .= '</div>'; } return $output; } /** * Implementation of hook_menu() */ function assistance_menu(){ $items['admin/settings/assistance'] = array( 'title' => t('Assistance settings'), 'description' => t('Settings for module assistance'), 'page callback' => 'drupal_get_form', 'page arguments' => array('assistance_admin_settings'), 'access arguments' => array('admin assistance'), 'type' => MENU_NORMAL_ITEM, 'file' => 'assistance.admin.inc', ); return $items; } function assistance_perm(){ return array( 'admin assistance', 'create assistance', 'edit any assistance', 'edit own assistance', 'delete any assistance', 'delete own assistance', ); } /* ** Implementacion of hook_user() */ function assistance_user($op, &$edit, &$account, $category){ global $user; if($op == 'delete'){ if(!module_exists('party')){ $result = db_query("SELECT * FROM {node} WHERE (type = 'assistance_event' OR type = 'assistance_event') AND uid = 0"); 162 while($data = db_fetch_object($result)){ assistance_delete($data); db_query('DELETE FROM {node} WHERE nid = %d', $data->nid); db_query('DELETE FROM {node_revisions} WHERE nid = %d', $data->nid); } } } } /** * Implementation of hook_acces() */ function assistance_access($op, $node, $account){ switch ($op){ case 'create': return user_access('create assistance', $account) ? TRUE : NULL; case 'update': return user_access('edit any assistance', $account) || (user_access('edit own assistance', $account)&&($account->uid == $node->uid)) ? TRUE : NULL; case 'delete': return user_access('delete any assistance', $account) || (user_access('delete own assistance', $account)&&($account->uid == $node->uid)) ? TRUE : NULL; } } /** * Funcion que devuelve array con el nid y titulo del tipo de contenido que se le pasa por parametro */ function get_node_info($name){ $node['null'] = 'Escoge uno'; $result = db_query("SELECT nid, title FROM {node} WHERE type = '%s'",$name); while($row = db_fetch_array($result)){ $node[$row['nid']] = $row['title']; } return $node; } /* * Funcion que devuelve array con el nid y titulo de los eventos que aun no ha pasado la fecha de inicio */ 163 function get_future_events($type){ $time = time(); $event['null'] = 'Escoge uno'; if($type == 'woe'){ $event['null'] = 'Escoge una'; } $result = db_query("SELECT n.nid, n.title, e.event_start FROM {node} n INNER JOIN {event} e ON n.nid = e.nid WHERE n.type = '%s'",$type); while($row = db_fetch_array($result)){ if(strtotime($row[event_start])>$time){ $event[$row['nid']] = $row['title']; } } return $event; } /** * Implementation of hook_views_api(). */ function assistance_views_api() { return array( 'api' => 2, 'path' => drupal_get_path('module', 'assistance'), //'path' => drupal_get_path('module', 'assistance') . '/includes', ); } Archivo assistance.admin.inc <?php function assistance_admin_settings(){ $types = node_get_types('names'); $form['assistance_types'] = array( '#type' => 'radios', '#title' => t('Node type'), '#options' => $types, '#default_value' => variable_get('assistance_types', ''), ); return system_settings_form($form); } 164 12.3 Módulo Castle Control Archivo castle_control.info ; $Id$ name= "Castle Control" description = "Module to have information on the status of Castles." core = 6.x php = 5.1 package = Ragnarok Guild Archivo castle_control.install ?php // $Id$ /** * Implementation of hook_schema() */ function castle_control_schema(){ $schema['castle_info'] = array( 'description' => 'Stores castle information.', 'fields' => array( 'delta' => array( 'type' => 'varchar', 'length' => 255, 'default' => '', 'not null' => TRUE, ), 'economy' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'defense' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 165 'daily_chests' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'daily_cost' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'owner' => array( 'type' => 'varchar', 'length' => 255, 'default' => '', 'not null' => TRUE, ), ), 'primary key' => array('delta'), ); return $schema; } /** * Implementation of hook_install() */ function castle_control_install(){ drupal_install_schema('castle_control'); } /** * Implementation of hook_uninstall() */ function castle_control_uninstall(){ drupal_uninstall_schema('castle_control'); } 166 Archivo castle_control.module <?php function castle_control_menu(){ $items['admin/build/castle_control'] = array( 'title' => t('Castle Control'), 'page callback' => 'drupal_get_form', 'page arguments' => array('castle_control_edit'), 'access arguments' => array('administer castles'), 'type' => MENU_NORMAL_ITEM, 'file' => 'castle_control.admin.inc', ); $items['admin/build/castle_control/edit'] = array( 'title' => t('Update Castle Info'), 'description' => t('Update castle info'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 0, 'file' => 'castle_control.admin.inc', ); $items['admin/build/castle_control/add'] = array( 'title' => t('Add castle'), 'description' => t('add castle'), 'page callback' => 'drupal_get_form', 'page arguments' => array('castle_control_add'), 'access callback' => user_access('administer castles'), 'type' => MENU_LOCAL_TASK, 'weight' => 1, 'file' => 'castle_control.admin.inc', ); $items['admin/build/castle_control/delete'] = array( 'title' => t('Delete castle'), 'description' => t('delete castle'), 'page callback' => 'drupal_get_form', 'page arguments' => array('castle_control_delete'), 'access callback' => user_access('administer castles'), 'type' => MENU_LOCAL_TASK, 'weight' => 1, 'file' => 'castle_control.admin.inc', 167 ); return $items; } function castle_control_perm(){ return array( 'administer castles','view castles', ); } function castle_control_block($op = 'list', $delta = 0, $edit = array ()){ switch($op) { case 'list': $result =db_query("SELECT delta FROM {castle_info}"); while($row = db_fetch_array($result)){ $blocks[$row['delta']]['info'] = 'Info '.$row['delta']; $blocks[$row['delta']]['cache']= BLOCK_NO_CACHE; } return $blocks; case 'view': if(user_access('view castles')){ $row = db_fetch_array(db_query("SELECT * FROM {castle_info} WHERE delta = '%s'",$delta)); $cost = number_format($row['daily_cost'],0,'','.'); $items = array(); $items[] = t('Economy').': '.$row['economy']; $items[] = t('Defense').': '.$row['defense']; $items[] = t('Daily Chests').': '.$row['daily_chests']; $items[] = t('Daily Cost').': '.$cost.' zenys'; $items[] = t('Owner').': '.$row['owner']; $block['subject'] = $row['delta']; $block['content'] = theme('item_list', $items); } return $block; } } 168 Archivo castle_control.admin.inc <?php //$Id$ /** * @file * Resultado de llamadas a páginas de administración para el módulo Castle Control */ function castle_control_add(&$form_state = NULL){ //Miramos si ya estamos en un estado avanzado del formulario o si estamos en el primer paso. $step = isset($form_state['values']) ? (int)$form_state['storage']['step'] : 1; //Guardamos el siguiente paso $form_state['storage']['step'] = $step+1; //ponemos el titulo la descripción y el nombre del boton de envio correspondiente al paso en el que estamos $form['indicator'] = array( '#type' => 'fieldset', '#title'=>t('Step @number', array('@number'=>$step)) ); if($step == 1){ $form['indicator']['name'] = array( '#type' => 'textfield', '#title' => t('Castle Name'), ); $button_name = t('Next'); } else{ $form['indicator']['economy'] = array( '#type' => 'textfield', '#title' => t('Economy'), '#default_value' => 0, ); $form['indicator']['defense'] = array( '#type' => 'textfield', '#title' => t('Defense'), '#default_value' => 0, ); $form['indicator']['daily_chests'] = array( '#type' => 'textfield', '#title' => t('Daily Chests'), '#default_value' => 0, ); $form['indicator']['daily_cost'] = array( '#type' => 'textfield', '#title' => t('Daily Cost'), '#default_value' => 0, ); $form['indicator']['owner'] = array( '#type' => 'textfield', '#title' => t('Owner'), '#default_value' => '', ); $button_name = t('Submit'); } $form['submit']= array( '#type' => 'submit', '#value' => $button_name, 169 ); if($step ==2){ $form_state['storage']['name']= $form_state['values']['name']; } return $form; } function castle_control_add_validate($form, &$form_state){ if($form_state['storage']['step']==2){ $name = $form_state['values']['name']; $result =db_query("SELECT delta FROM {castle_info}"); while($row = db_fetch_array($result)){ if($row['delta']==$name) $exist = TRUE; } if(is_numeric($name)||$name=="" ){ form_set_error ('name', t('Invalid name')); } if($exist == TRUE){ form_set_error ('name', t('This castle is already in the database')); } } else{ $eco = 'a'.$form_state['values']['economy']; $pos = strpos($eco, "-"); if(!is_numeric($form_state['values']['economy'])||($pos==1)){ form_set_error ('economy', t('Economy must be a positive number')); } if($form_state['values']['economy']>100){ form_set_error ('economy', t('Economy can not be higher than').'100'); } $def = 'a'.$form_state['values']['defense']; $pos = strpos($def, "-"); if(!is_numeric($form_state['values']['defense'])||($pos==1)){ form_set_error ('defense', t('Defense must be a positive number')); } if($form_state['values']['defense']>100){ form_set_error ('defense', t('Defense can not be higher than').'100'); } $chest = 'a'.$form_state['values']['daily_chests']; $pos = strpos($chest, "-"); if(!is_numeric($form_state['values']['daily_chests'])||($pos==1)){ form_set_error ('daily_chests', t('Daily chests must be a positive number')); } $cost = 'a'.$form_state['values']['daily_cost']; $pos = strpos($cost, "-"); if(!is_numeric($form_state['values']['daily_cost'])||($pos==1)){ form_set_error ('daily_cost', t('Daily cost must be a positive number')); } if(is_numeric($form_state['values']['owner'])||$form_state['values']['own er']=="" ){ form_set_error ('owner', t('Owner must be a string')); } } 170 } function castle_control_add_submit($form, &$form_state){ if($form_state['storage']['step']<3){ return; } db_query("INSERT INTO {castle_info} (delta, economy, defense, daily_chests, daily_cost, owner) VALUES ('%s', %d, %d, %d, %d, '%s')",$form_state['storage']['name'], $form_state['values']['economy'],$form_state['values']['defense'],$form_s tate['values']['daily_chests'],$form_state['values']['daily_cost'],$form_ state['values']['owner'],$form_state['values']['castles']); unset($form_state['storage']); $form_state['redirect']='admin/build/block'; } function castle_control_edit(&$form_state = NULL){ //Miramos si ya estamos en un estado avanzado del formulario o si estamos en el primer paso. $step = isset($form_state['values']) ? (int)$form_state['storage']['step'] : 1; //Guardamos el siguiente paso $form_state['storage']['step'] = $step+1; //ponemos el titulo la descripción y el nombre del boton de envio correspondiente al paso en el que estamos $form['indicator'] = array( '#type' => 'fieldset', '#title'=>t('Step @number', array('@number'=>$step)) ); if($step == 1){ $castles = get_castles(); $form['indicator']['castles'] = array( '#type' => 'select', '#title' => t('Castle'), '#options' => $castles, ); $button_name = t('Next'); } else{ $row=db_fetch_array(db_query("SELECT * FROM {castle_info} WHERE delta = '%s'",$form_state['values']['castles'])); $form['indicator']['name'] = array( '#type' => 'textfield', '#title' => t('Name'), '#default_value' => $row['delta'], ); $form['indicator']['economy'] = array( '#type' => 'textfield', '#title' => t('Economy'), '#default_value' => $row['economy'], ); $form['indicator']['defense'] = array( '#type' => 'textfield', '#title' => t('Defense'), '#default_value' => $row['defense'], ); $form['indicator']['daily_chests'] = array( '#type' => 'textfield', '#title' => t('Daily Chests'), '#default_value' => $row['daily_chests'], ); 171 $form['indicator']['daily_cost'] = array( '#type' => 'textfield', '#title' => t('Daily Cost'), '#default_value' => $row['daily_cost'], ); $form['indicator']['owner'] = array( '#type' => 'textfield', '#title' => t('Owner'), '#default_value' => $row['owner'], ); $button_name = t('Update'); } $form['submit'] = array( '#type' => 'submit', '#value' => $button_name, ); if($step==2){ $form_state['storage']['name']= $form_state['values']['castles']; } return $form; } function castle_control_edit_validate($form, &$form_state){ if($form_state['storage']['step']==2){ if($form_state['values']['castles']=='sin'){ form_set_error ('castles', t('Choose a valid Castle')); } } else{ if(is_numeric($form_state['values']['name'])||$form_state['values']['name ']=="" ){ form_set_error ('name', t('Owner must be a string')); } $eco = 'a'.$form_state['values']['economy']; $pos = strpos($eco, "-"); if(!is_numeric($form_state['values']['economy'])||($pos==1)){ form_set_error ('economy', t('Economy must be a positive number')); } if($form_state['values']['economy']>100){ form_set_error ('economy', t('Economy can not be higher than').'100'); } $def = 'a'.$form_state['values']['defense']; $pos = strpos($def, "-"); if(!is_numeric($form_state['values']['defense'])||($pos==1)){ form_set_error ('defense', t('Defense must be a positive number')); } if($form_state['values']['defense']>100){ form_set_error ('defense', t('Defense can not be higher than').'100'); } $chest = 'a'.$form_state['values']['daily_chests']; $pos = strpos($chest, "-"); if(!is_numeric($form_state['values']['daily_chests'])||($pos==1)){ form_set_error ('daily_chests', t('Daily chests must be a positive number')); } $cost = 'a'.$form_state['values']['daily_cost']; 172 $pos = strpos($cost, "-"); if(!is_numeric($form_state['values']['daily_cost'])||($pos==1)){ form_set_error ('daily_cost', t('Daily cost must be a positive number')); } if(is_numeric($form_state['values']['owner'])||$form_state['values']['own er']=="" ){ form_set_error ('owner', t('Owner must be a string')); } } } function castle_control_edit_submit($form, &$form_state){ if($form_state['storage']['step']<3){ return; } if($form_state['storage']['name']==$form_state['values']['name']){ db_query("UPDATE {castle_info} SET economy = %d, defense = %d, daily_chests = %d, daily_cost = %d, owner = '%s' WHERE delta = '%s'", $form_state['values']['economy'],$form_state['values']['defense'],$form_s tate['values']['daily_chests'],$form_state['values']['daily_cost'],$form_ state['values']['owner'],$form_state['storage']['name']); } else{ db_query("UPDATE {castle_info} SET delta = '%s', economy = %d, defense = %d, daily_chests = %d, daily_cost = %d, owner = '%s' WHERE delta = '%s'", $form_state['values']['name'], $form_state['values']['economy'],$form_state['values']['defense'],$form_s tate['values']['daily_chests'],$form_state['values']['daily_cost'],$form_ state['values']['owner'],$form_state['storage']['name']); db_query("UPDATE {blocks} SET delta = '%s' WHERE module = 'castle_control' AND delta = '%s'",$form_state['values']['name'],$form_state['storage']['name']); // $form_state['redirect']='admin/build/block'; } unset($form_state['storage']); } function castle_control_delete(){ $castles = get_castles(); $form['castles'] = array( '#type' => 'select', '#title' => t('Castle'), '#options' => $castles, ); $form['delete'] = array( '#type' => 'submit', '#value' => t('Delete Castle'), ); return $form; } function castle_control_delete_validate($form, &$form_state){ if($form_state['values']['castles']=='sin'){ form_set_error ('castles', t('Choose a valid Castle')); } } function castle_control_delete_submit($form, &$form_state){ db_query("DELETE FROM {castle_info} WHERE delta = 173 '%s'",$form_state['values']['castles']); db_query("DELETE FROM {blocks} WHERE delta = '%s' AND module = 'castle_control'",$form_state['values']['castles']); } function get_castles(){ $result = db_query("SELECT delta FROM {castle_info}"); $castles['sin'] = 'Escoge un castillo'; while($row = db_fetch_array($result)){ $castles[ $row['delta']]=$row['delta']; } return $castles; } 12.4 Módulo Guild Shop Archivo guild_shop.info ; $Id$ name= "Guild Shop" description = "Module to add a guild shop with internal balance." dependencies[] = views dependencies[] = taxonomy core = 6.x package = Ragnarok Guild Archivo guild_shop.install <?php // $Id$ /** * Implementation of hook_schema() */ function guild_shop_schema(){ $schema['gs_internal_balance'] = array( 'description' => 'Stores internal balance of users', 'fields' => array( 'uid' => array( //field referred to user identifier 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 174 'default' => 0, ), 'internal_balance' =>array( //balance of user 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, ' default' => 0, ), ), 'primary key' => array('uid','internal_balance'), ); $schema['gs_item'] = array( 'description' => 'Stores items of guild shop', 'fields' => array( 'nid' => array( // id of the node set 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'vid' => array( //version of the node set 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'description_item' => array( //description of the item 'type' => 'text', 'not null' => TRUE, 'size' => 'big', ), 175 'type_item' => array( //type of the item 'type' => 'varchar', 'length' => 255, 'default' => '', ), 'price' => array( //price of the item 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'stock' => array( //stock of the item 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'picture' => array( //picture of the item 'type' => 'varchar', 'length' => 255, 'default' => '', ), ), //primary key formed by nid field 'primary key' => array('nid'), ); $schema['gs_purchase'] = array( 'description' => 'Stores purchases made by users', 'fields' => array( 'nid' => array( // id of the node set 'type' => 'int', 176 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'vid' => array( // version of the node set 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'user_name' => array( //name of the user making the purchase 'type' => 'varchar', 'length' => 255, 'default' => '', ), 'item_name' => array( //name of the item that was purchased 'type' => 'varchar', 'length' => 255, 'default' => '', ), 'amount' => array( //amount of items that was purchased 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), ), 'primary key' => array('nid'), ); return $schema; } 177 /** * Implementation of hook_install() */ function guild_shop_install(){ drupal_install_schema('guild_shop'); db_query("INSERT INTO {vocabulary} (name, relations, hierarchy, multiple, required, tags, module, weight)VALUES ('type item', 1,0,0,0,0,'Guild Shop',0)"); $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE name = 'type item'")); db_query("INSERT INTO {term_data}(vid, name, weight) VALUES (%d, 'Armor', 0),(%d, 'Card', 1),(%d, 'Healing Item', 2),(%d, 'Miscellaneous', 3),(%d, 'Usable Item', 4),(%d, 'Weapon', 5)",$vid,$vid,$vid,$vid,$vid,$vid); $result=db_query("SELECT tid FROM {term_data} WHERE vid = %d",$vid); while($row = db_fetch_array($result)){ db_query("INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, 0)",$row['tid']); } } /** * Implementation of hook_uninstall() */ function guild_shop_uninstall(){ drupal_uninstall_schema('guild_shop'); $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE name = 'type item'")); $result=db_query("SELECT tid FROM {term_data} WHERE vid = %d",$vid); while($row = db_fetch_array($result)){ db_query("DELETE FROM {term_hierarchy} WHERE tid = %d",$row['tid']); } db_query("DELETE FROM {term_data} WHERE vid = %d", $vid); db_query("DELETE FROM {vocabulary} WHERE module = 'Guild Shop'"); } Archivo guild_shop.module <?php // $Id$ /** * Implementation of hook_node_info() */ 178 function guild_shop_node_info(){ return array( 'gs_item' => array( 'name' => t('Shop Item'), 'module' => 'guild_shop', 'description' => t('Item for sale in the shop.'), 'title label' => t('Item name'), 'has body' => FALSE, ), 'gs_purchase' => array( 'name' => t('Purchase'), 'module' => 'guild_shop', 'description' => t('Purchase made by user.'), 'title label' => t('Purchase'), 'has body' => FALSE, ), ); } /** * Implementation of hook_form(&$node, $form_state) */ function guild_shop_form(&$node, $form_state){ $type = node_get_types('type', $node); if ($type->has_title){ $form['title'] = array( '#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => $node->title, ); } if($type->type==gs_item){ $form['#attributes'] = array('enctype' => "multipart/form-data"); $form['description_item'] = array( '#type' => 'textarea', '#title' => t('Description'), '#required' => TRUE, '#default_value' => isset($node->description_item) ? $node->description_item: '', 179 '#description' => t('Description of item.'), ); $form['type_item'] = array( '#type' => 'select', '#title' => t('Type'), '#default_value' => isset ($node->type_item) ? $node->type_item : 'armor', '#options' => array( 'card' => t('Card'), 'armor' => t('Armor'), 'weapon' => t('Weapon'), 'healing item' => t('Healing Item'), 'usable item' => t('Usable Item'), 'miscellaneous' => t('Miscellaneous'), ), '#description' => t('Select type of item'), ); $form['price'] = array( '#type' => 'textfield', '#title' => t('Price'), '#required' => TRUE, '#default_value' => isset ($node->price) ? $node->price : '0', '#description' => t('Price of item'), ); $form['picture'] = array( '#type' => 'file', '#title' => t('Picture'), '#description' => t('Picture of shop item.<br> resolution max: 90x120 pixels'), ); } if($type->type==gs_purchase){ $form['user_name'] = array( '#type' => 'textfield', '#title' => t('User name'), '#required' => TRUE, '#default_value' => isset ($node->user_name) ? $node->user_name : '', 180 '#description' => t('Name of the user making the purchase.'), ); $form['item_name'] = array( '#type' => 'textfield', '#title' => t('Item name'), '#required' => TRUE, '#default_value' => isset ($node->item_name) ? $node->item_name : '', '#description' => t('Name of the item that was purchased.'), ); $form['amount'] = array( '#type' => 'textfield', '#title' => t('Amount'), '#required' => TRUE, '#default_value' => isset ($node->amount) ? $node->amount : '', '#description' => t('Amount of items that was purchased.'), ); } return $form; } /** * Implementation of hook_validate() */function guild_shop_validate($node ){ if($node->type==gs_item){ if(!is_numeric($node->price)||($node->price<0)){ form_set_error('price', t('The value must be a positive number')); } } } function guild_shop_insert($node){ if($node->type==gs_item){ $validators = array( 'file_validate_is_image' => array(), 'file_validate_image_resolution' => array('90x120'), ); $directory = file_directory_path(); $directory .= '/pictures'; 181 if($file = file_save_upload('picture', $validators,$directory, FILE_EXISTS_RENAME)){ $file->description = $file->filename; $file->new = TRUE; file_set_status($file, FILE_STATUS_PERMANENT); } db_query("INSERT INTO {gs_item} (vid, nid, description_item, type_item, price, picture) VALUES (%d, %d, '%s', '%s', %d, '%s')", $node->vid, $node->nid, $node>description_item, $node->type_item, $node->price, $file->filepath); $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE name = 'type item'")); $tid = db_result(db_query("SELECT tid FROM {term_data} WHERE name = '%s' AND vid = %d",$node->type_item,$vid)); db_query("INSERT INTO {term_node} (nid, vid, tid) VALUES (%d,%d,%d)",$node->nid,$node->vid,$tid); } if($node->type==gs_purchase){ db_query("INSERT INTO {gs_purchase} (vid, nid, user_name, item_name, amount) VALUES (%d, %d, '%s', '%s', %d)", $node->vid, $node->nid, $node>user_name, $node->item_name, $node->amount); } } /** * Implementation of hook_update() */ function guild_shop_update($node){ if($node->type==gs_item){ $pic = db_result(db_query('SELECT picture FROM {gs_item} WHERE nid = %d',$node->nid)); $validators = array( 'file_validate_is_image' => array(), 'file_validate_image_resolution' => array('90x120'), ); $directory = file_directory_path(); $directory .= '/pictures'; if($file = file_save_upload('picture', $validators,$directory, FILE_EXISTS_RENAME)){ $file->description = $file->filename; $file->new = TRUE; 182 file_set_status($file, FILE_STATUS_PERMANENT); db_query("UPDATE {gs_item} SET description_item = '%s', type_item = '%s', price = '%d', picture = '%s' WHERE vid = %d", $node->description_item, $node>type_item, $node->price, $file->filepath, $node->vid); file_delete($pic); } else{ db_query("UPDATE {gs_item} SET description_item = '%s', type_item = '%s', price = '%d' WHERE vid = %d", $node->description_item, $node->type_item, $node>price, $node->vid); $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE name = 'type item'")); $tid = db_result(db_query("SELECT tid FROM {term_data} WHERE name = '%s' AND vid = %d",$node->type_item,$vid)); db_query("UPDATE {term_node} SET tid = %d WHERE nid = %d",$tid,$node>nid); } } } /** * Implementation of hook_delete() */ function guild_shop_delete($node){ if($node->type==gs_item){ $pic = db_result(db_query('SELECT picture FROM {gs_item} WHERE nid = %d',$node->nid)); db_query('DELETE FROM {gs_item} WHERE nid = %d', $node->nid); file_delete($pic); db_query("DELETE FROM {term_node} WHERE nid = %d",$node->nid); } if($node->type==gs_purchase){ db_query('DELETE FROM {gs_purchase} WHERE nid = %d', $node->nid); } } /** * Implementation of hook_load() */ function guild_shop_load($node){ if($node->type==gs_item){ 183 $additions = db_fetch_object(db_query('SELECT description_item, type_item, price, stock FROM {gs_item} WHERE vid = %d', $node->vid)); } if($node->type==gs_purchase){ $additions = db_fetch_object(db_query('SELECT user_name, item_name, amount FROM {gs_purchase} WHERE vid = %d', $node->vid)); } return $additions; } /** * Implementation of hook_view() */ function guild_shop_view($node, $teaser = FALSE, $page = FALSE){ global $base_root, $base_path; $node = node_prepare($node, $teaser); if($node->type==gs_item){ $picture = db_result(db_query('SELECT picture FROM {gs_item} WHERE nid = %d',$node->nid)); if($picture!=''){ $pic = $base_root.$base_path.$picture; } else{ $pic = $base_root.$base_path.'sites/default/files/noimg90x120.jpg'; } $node->content['node_item'] = array( '#value' => theme('guild_shop_node_item', $pic, $node>description_item, $node->price, $node->stock, $node->nid), '#weight' => 0, ); } if($node->type==gs_purchase){ $node->content['node_purchase'] = array( '#value' => theme('guild_shop_node_purchase', $node>user_name, $node->item_name, $node->amount), '#weight' => 0, ); } return $node; } 184 /** * Implementation of hook_theme() */ function guild_shop_theme(){ return array( 'guild_shop_node_item' => array( 'template' => 'guild_shop', 'arguments' => array('picture'=>0, 'description_item'=>0, 'price'=>0, 'stock'=>0, 'nid'=>0), ), 'guild_shop_node_purchase' => array( 'template' => 'purchase', 'arguments' => array('user_name'=>0, 'item_name'=>0, 'amount'=>0), ), ); } /** * Implementation of hook_init(). */ function guild_shop_init() { drupal_add_css(drupal_get_path('module', 'guild_shop') .'/guild_shop.css'); } function guild_shop_perm(){ return array( 'create item shop', 'delete own item shop', 'delete any item shop', 'edit own item shop', 'edit any item shop', 'administer shop', 'create purchase', 'buy item', ); } /** * Implementation of hook_acces() */ function guild_shop_access($op, $node, $account){ 185 switch ($op){ case 'create': return user_access('create item shop', $account) ? TRUE : NULL; case 'update': return user_access('edit any item shop', $account) || (user_access('edit own item shop', $account)&&($account->uid == $node->uid)) ? TRUE : NULL; case 'delete': return user_access('delete any item shop', $account) || (user_access('delete own item shop', $account)&&($account->uid == $node->uid)) ? TRUE : NULL; } } /** * Implementation of hook_menu() */ function guild_shop_menu(){ $items['admin/build/guild_shop'] = array( 'title' => t('Guild Shop'), 'description' => t('Admin for module Guild Shop'), 'page callback' => 'drupal_get_form', 'page arguments' => array('guild_shop_stocks'), 'access arguments' => array('administer shop'), 'type' => MENU_NORMAL_ITEM, 'file' => 'guild_shop.admin.inc', ); $items['admin/build/guild_shop/stocks'] = array( 'title' => t('Stocks'), 'description' => t('Update stocks'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 0, 'file' => 'guild_shop.admin.inc', ); $items['admin/build/guild_shop/balances'] = array( 'title' => t('User Balances'), 'description' => t('Update balances'), 'page callback' => 'drupal_get_form', 'page arguments' => array('guild_shop_balances'), 'access callback' => user_access('administer shop'), 'type' => MENU_LOCAL_TASK, 186 'weight' => 1, 'file' => 'guild_shop.admin.inc', ); $items['purchase_submit/%'] = array( 'title' => t('Guild Shop'), 'page callback' => 'drupal_get_form', 'page arguments' => array('guild_shop_purchase_submit'), 'access callback' => user_access('buy item'), 'type' => MENU_CALLBACK, ); $items['complete_purchase'] = array( 'title' => t('Guild Shop'), 'page callback' => 'guild_shop_complete_purchase', 'access callback' => user_access('buy item'), 'type' => MENU_CALLBACK, ); return $items; } function guild_shop_purchase_submit(){ global $user; if(!isset($_SESSION['id_item'])){ $dd = explode('/', $_SERVER['REQUEST_URI']); $_SESSION['id_item']=$dd[3]; $id = $_SESSION['id_item']; } else{ $dd = explode('/', $_SERVER['REQUEST_URI']); if($dd[3]!=$_SESSION['id_item']){ $_SESSION['id_item']=$dd[3]; } $id = $_SESSION['id_item']; } $balance_query = db_result(db_query('SELECT internal_balance FROM {gs_internal_balance} WHERE uid = %d', $user->uid)); $row = db_fetch_array(db_query("SELECT n.title, gi.price, gi.stock FROM {node} n INNER JOIN {gs_item} gi ON gi.nid=n.nid WHERE n.nid=%d", $id)); $balance = number_format($balance_query,0,'','.'); $price = number_format($row['price'],0,'','.'); 187 $stock = $row['stock']; if($row['stock'] != 0){ $form['text_balance']= array( '#value' => '<p><b>'.t('Balance').': </b>'.$balance.'</p>', ); $form['text_item'] = array( '#value' => '<p><b>'.t('Item').': </b>'.$row['title'].'</p>', ); $form['text_price'] = array( '#value' => '<p><b>'.t('Price').': </b>'.$price.'</p>', ); $form['text_stock'] = array( '#value' => '<b>'.t('Stock').': </b>'.$stock, ); $form['amount'] = array( '#type' => 'textfield', '#title' => t('Amount'), ); $form['balance'] = array( '#type' => 'hidden', '#value' => $balance_query, ); $form['stock'] = array( '#type' => 'hidden', '#value' => $stock, ); $form['price'] = array( '#type' => 'hidden', '#value' => $row['price'], ); $form['id'] = array( '#type' => 'hidden', '#value' => $id, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Purchase'), '#redirect' => 'after_purchase', 188 ); } else{ $form['text_balance']= array( '#value' => '<p><h1><b>Estás realizando una operación ilegal!!!!</h1></p>', ); } return $form; } function guild_shop_purchase_submit_validate($form, &$form_state){ $str = 'a'.$form_state['values']['amount']; $pos = strpos($str, "-"); $amount = $form_state['values']['amount']; $stock = $form_state['values']['stock']; $balance = $form_state['values']['balance']; $price = $form_state['values']['price']; if(!is_numeric($form_state['values']['amount'])||($pos==1)){ form_set_error ('amount', t('Must be a positive number')); } if($amount>$stock){ form_set_error('amount', t('you can only buy a maximum of ').$stock.t(' units')); } if($balance<$price*$amount){ form_set_error('amount', t('insufficient balance')); } } function guild_shop_purchase_submit_submit($form, &$form_state){ global $user; $name= db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $_SESSION['id_item'])); unset($_SESSION['id_item']); $actual_stock = $form_state['values']['stock']- $form_state['values']['amount']; $actual_balance = $form_state['values']['balance'] ($form_state['values']['amount']*$form_state['values']['price']); db_query('UPDATE {gs_item} SET stock = %d WHERE nid = %d', $actual_stock, $form_state['values']['id']); 189 db_query('UPDATE {gs_internal_balance} SET internal_balance = %d WHERE uid = %d', $actual_balance, $user->uid); $node->type = 'gs_purchase'; $node->title = $user->name.' purchase '.$name; $node->user_name = $user->name; $node->item_name = $name; $node->amount = $form_state['values']['amount']; $node->uid = $user->uid; $time = time(); db_query("INSERT INTO {node} (type, title, uid, status, created, changed, comment, promote) VALUES ('%s', '%s', %d, %d, %d, %d, %d, %d)",$node->type, $node->title, $user->uid, 1, $time, $time, 0,0); $id = db_result(db_query('SELECT nid FROM {node} WHERE uid = %d AND created = %d', $user->uid, $time)); db_query('UPDATE {node} SET vid = %d WHERE nid = %d', $id, $id); db_query("INSERT INTO {node_revisions} (nid, vid, uid, title, timestamp) VALUES (%d, %d, %d, '%s', %d)", $id, $id, $user->uid, $node->title, $time); $node->nid = $id; $node->vid = $id; guild_shop_insert($node); $form_state['redirect'] = 'complete_purchase'; } function guild_shop_complete_purchase(){ global $user; $balance_query = db_result(db_query('SELECT internal_balance FROM {gs_internal_balance} WHERE uid = %d', $user->uid)); $balance = number_format($balance_query,0,'','.'); $output = '<p><font size = "3" >'.t('Purchase Complete.').' </font></p>'; $output .= t('Actual Balance: ').$balance; return $output; } /** * Implementation of hook_block() */ function guild_shop_block($op = 'list', $delta = 0, $edit = array ()){ switch($op) { case 'list': $blocks[0]['info'] = t('Last items added'); 190 $blocks[0]['cache']= BLOCK_NO_CACHE; $blocks[1]['info'] = t('Latest purchases'); $blocks[1]['cache']= BLOCK_NO_CACHE; $blocks[2]['info'] = t('Guild Shop menu'); $blocks[2]['cache']= BLOCK_NO_CACHE; $blocks[3]['info'] = t('Guild Shop user info'); $blocks[3]['cache']= BLOCK_NO_CACHE; return $blocks; case 'view': if($delta == 0 && user_access('buy item')){ $result = db_query_range("SELECT title, nid FROM {node} WHERE type = 'gs_item'ORDER BY created desc",0,5); $items = array(); while($row= db_fetch_array($result)){ $items[] = l($row['title'], drupal_get_path_alias('node/'.$row[nid])); } $block['subject'] = t('last items added'); $block['content'] = theme('item_list', $items); } if($delta == 1 && user_access('administer shop')){ $result = db_query_range("SELECT title, nid FROM {node} WHERE type = 'gs_purchase'ORDER BY created desc",0,5); $items = array(); while($row= db_fetch_array($result)){ $items[] = l($row['title'], drupal_get_path_alias('node/'.$row[nid])); } $block['subject'] = t('latest purchases'); $block['content'] = theme('item_list', $items); } if($delta == 2 && user_access('buy item')){ $items = array(); $items[] = l(t('All items'), drupal_get_path_alias('guild_shop')); $items[] = l(t('Cards'), drupal_get_path_alias('guild_shop/card')); $items[] = l(t('Armors'), drupal_get_path_alias('guild_shop/armor')); $items[] = l(t('Weapons'), drupal_get_path_alias('guild_shop/weapon')); $items[] = l(t('Healing Item'), drupal_get_path_alias('guild_shop/healing_item')); 191 $items[] = l(t('Usable Item'), drupal_get_path_alias('guild_shop/usable_item')); $items[] = l(t('Miscellaneous'), drupal_get_path_alias('guild_shop/miscellaneous')); $block['subject'] = t('Guild Shop'); $block['content'] = theme('item_list', $items); } if($delta == 3 && user_access('buy item')){ global $user; $result = db_query_range("SELECT title, nid FROM {node} WHERE type = 'gs_purchase' AND uid = %d ORDER BY created desc",$user->uid,0,1); $items = array(); $row=db_fetch_array($result); if($row['title']==''){ $items[] = t('Last purchase: none'); } else{ $items[] = t('Last purchase: ').l($row['title'], drupal_get_path_alias('node/'.$row[nid])); } $punct = db_result(db_query('SELECT internal_balance FROM {gs_internal_balance} WHERE uid = %d', $user->uid)); if($punct==''){ $punct = 0; } $balance = number_format($punct,0,'','.'); $items[] = t('Balance: ').$balance; $block['subject'] = t('Guild Shop user info'); $block['content'] = theme('item_list', $items); } return $block; } } /** * Implementation of hook_views_api(). */ function guild_shop_views_api(){ return array( 192 'api' => 2, 'path' => drupal_get_path('module', 'guild_shop'), //'path' => drupal_get_path('module', 'guild_shop') . '/includes', ); } /* * Implementation of hook_user(). */ function guild_shop_user($op, &$edit, &$user, $category = NULL){ switch($op){ case 'delete': db_query('DELETE FROM {gs_internal_balance} WHERE uid = %d', $user>uid); $result = db_query("SELECT nid, vid FROM {node} WHERE uid = 0 AND type = 'gs_purchase'"); while($row = db_fetch_array($result)){ db_query("DELETE FROM {node_revisions} WHERE nid = %d AND vid = %d", $row['nid'], $row['vid']); db_query("DELETE FROM {node} WHERE nid = %d AND vid = %d", $row['nid'], $row['vid']); } } } Archivo guild_shop.admin.inc <?php //$Id$ /** * @file * Resultado de llamadas a páginas de administración para el módulo Guild Shop */ function guild_shop_stocks(){ $items = get_items(); $form['items'] = array( '#type' => 'select', '#title' => t('Item Shop'), '#options' => $items, 193 ); $form['stock'] = array( '#type' => 'textfield', '#title' => t('Amount'), ); $form['add'] = array( '#type' => 'submit', '#value' => t('Add Stock'), ); return $form; } function guild_shop_balances(){ $us = get_user_guild_shop(); $form['users'] = array( '#type' => 'select', '#title' => t('users'), '#options' => $us, ); $form['balance'] = array( '#type' => 'textfield', '#title' => t('Balance'), ); $form['add'] = array( '#type' => 'submit', '#value' => t('Add Balance'), ); return $form; } function get_items(){ $result = db_query("SELECT nid, title FROM {node} where type = 'gs_item'"); $items['blanco']='Escoge un item'; while($row = db_fetch_array($result)){ $items[ $row['nid']]=$row['title']; } return $items; } 194 function get_user_guild_shop(){ $var = user_roles($membersonly = TRUE, $permission = 'buy item'); $str; foreach ($var as $clave => $valor) { //para cada valor del array if(isset($str)){ //miramos si ya hemos añadido algo al string, en ese caso la nueva id del rol la mirarems en una or en la sentencia sql $str.=" OR r.name = '".$valor."'"; } else{ // si es la primera vez no ponemos or $str="r.name = '".$valor."'"; } } $result = db_query("SELECT u.uid, u.name FROM ({users} u INNER JOIN {users_roles} ur ON ur.uid = u.uid) INNER JOIN {role} r ON ur.rid = r.rid WHERE ".$str); $users['blanco']='Escoge un usuario'; while($row = db_fetch_array($result)){ $users[ $row['uid']]=$row['name']; } return $users; } function guild_shop_stocks_validate($form, &$form_state){ $str = 'a'.$form_state['values']['stock']; $pos = strpos($str, "-"); if(!is_numeric($form_state['values']['stock'])||($pos==1)){ form_set_error ('stock', t('Must be a positive number')); } if($form_state['values']['items']=='blanco'){ form_set_error ('items', t('Choose an item.')); } } function guild_shop_stocks_submit($form, &$form_state){ $amount = db_result(db_query("SELECT stock FROM {gs_item} where nid = %d", $form_state['values']['items'])); $amount += $form_state['values']['stock']; db_query("UPDATE {gs_item} SET stock = %d WHERE nid = %d", $amount, $form_state['values']['items']); } 195 function guild_shop_balances_validate($form, &$form_state){ $str = 'a'.$form_state['values']['balance']; $pos = strpos($str, "-"); if(!is_numeric($form_state['values']['balance'])||($pos==1)){ form_set_error ('balance', t('Must be a positive number')); } if($form_state['values']['users']=='blanco'){ form_set_error ('users', t('Choose an user.')); } } function guild_shop_balances_submit($form, &$form_state){ $info = db_fetch_array(db_query("SELECT * FROM {gs_internal_balance} WHERE uid = %d", $form_state['values']['users'])); $cont = count($info); if($cont<2){ db_query("INSERT INTO {gs_internal_balance} (uid,internal_balance) VALUES (%d, %d)", $form_state['values']['users'],$form_state['values']['balance']); } else{ $amount = $info['internal_balance']+$form_state['values']['balance']; db_query("UPDATE {gs_internal_balance} SET internal_balance = %d WHERE uid = %d", $amount, $form_state['values']['users']); } } Archivo guild_shop.tpl.php <?php global $base_root, $base_path; $price_format = number_format($price,0,'','.'); ?> <div class="gs-price"> Precio: <?php print $price_format; ?> </div> <div class="gs-picture"> <img src=" <?php print $picture; ?>"> </div> <div class="gs-description_item"> <b>Descripción:</b> <?php print $description_item; ?> </div> <?php if($stock==0){ 196 print '<div class="ninguna">Sin Stock</div>'; } ?> <?php if($stock==1){ print '<div class="ultima">Última unidad!</div>'; } ?> <?php if(($stock>1)&&($stock<=5)){ print '<div class="pocas">Solo quedan '.$stock.' unidades</div>'; }?> <?php if($stock>5){ print '<div class="normal"><b>Stock:</b> '.$stock.' unidades</div>'; }?> <?php if(user_access('buy item')&&($stock>0)){ print '<br><a href="'.$base_root.$base_path.'purchase_submit/'.$nid.'" class="comprar">Comprar</a><br>'; } ?> Archivo purchase.tpl.php <div class="gs-user" > <b>Nombre:</b> <?php print $user_name; ?> </div> <div class="gs-item" > <b>Item:</b> <?php print $item_name; ?> </div> <div class="gs-amount" > <b>Cantidad:</b> <?php print $amount; ?> </div> Archivo guild_shop.css .ninguna{ color: red; font-size: 170%; } .ultima{ color: orange; font-size: 16px; } .pocas{ color: #A69A00; font-size: 16px; } 197 .normal{ color: green; font-size: 16px; } .gs-price{ float:right; border-style:dotted; border-width:1px; padding: 7px; border-color: black; font-size: 170%; font-weight: bold; } .comprar{ font-size: 22px; } 12.5 Módulo Party Archivo party.info ; $Id$ name= "Party" description = "Module to create partys to events" dependencies[] = assistance core = 6.x package = Ragnarok Guild Archivo party.install <?php // $Id$ /** * Implementation of hook_schema() */ function party_schema(){ $schema['party'] = array( 'description' => 'Store party info', 'fields' => array( 'enid' => array( 'type' => 'int', 198 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'party_name' => array( 'type' => 'varchar', 'length' => 255, 'default' => '', ), 'type_event' => array( 'type' => 'varchar', 'length' => 255, 'default' => '', ), 'anid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), ), 'primary key' => array('anid'), ); $schema['party_members'] = array( 'description' => 'Store party info', 'fields' => array( 'enid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'party_name' => array( 'type' => 'varchar', 'length' => 255, 'default' => '', ), 'number' => array( 'type' => 'int', 199 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), ), 'primary key' => array('enid','party_name'), ); return $schema; } /** * Implementation of hook_install() */ function party_install(){ drupal_install_schema('party'); } /** * Implementation of hook_uninstall() */ function party_uninstall(){ drupal_uninstall_schema('party'); } Archivo party.module <?php // $Id$ drupal_add_js(drupal_get_path('module', 'party') .'/js/party_event.js', 'module'); drupal_add_js(drupal_get_path('module', 'party') .'/js/party_build.js', 'module'); /* ** Implementacion de hook_menu() */ function party_menu(){ $items['party']= array( 'title' => t('Partys'), 'page callback' => 'party_page', 'description' => t('To manage partys'), 'access arguments' => array('view party'), 'type' => MENU_NORMAL_ITEM, ); 200 $items['party/add_party_event']= array( 'title' => t('Create Party Event'), 'description' => t('Content to add party for event'), 'page callback' => 'drupal_get_form', 'page arguments' => array('party_build_form'), 'access arguments' => array('create party event'), 'type' => MENU_NORMAL_ITEM, ); $items['party/add_party_woe']= array( 'title' => t('Create Party WoE'), 'description' => t('Content to add party for event'), 'page callback' => 'drupal_get_form', 'page arguments' => array('party_build_form'), 'access arguments' => array('create party woe'), 'type' => MENU_NORMAL_ITEM, ); $items['party/add_member_party_event']= array( 'title' => t('Add Member to Party Event'), 'description' => t('to add a member to a party event created'), 'page callback' => 'drupal_get_form', 'page arguments' => array('party_member_form'), 'access arguments' => array('create party event'), 'type' => MENU_NORMAL_ITEM, ); $items['party/add_member_party_woe']= array( 'title' => t('Add Member to Party WoE'), 'description' => t('to add a member to a party woe created'), 'page callback' => 'drupal_get_form', 'page arguments' => array('party_member_form'), 'access arguments' => array('create party woe'), 'type' => MENU_NORMAL_ITEM, ); $items['party/delete_member_party_event']= array( 'title' => t('Delete Member of Party Event'), 'description' => t('to delete a member of party event created'), 'page callback' => 'drupal_get_form', 'page arguments' => array('party_member_form'), 'access arguments' => array('create party event'), 'type' => MENU_NORMAL_ITEM, 201 ); $items['party/delete_member_party_woe']= array( 'title' => t('Delete Member of Party WoE'), 'description' => t('to delete a member of party woe created'), 'page callback' => 'drupal_get_form', 'page arguments' => array('party_member_form'), 'access arguments' => array('create party woe'), 'type' => MENU_NORMAL_ITEM, ); $items['party/event_view'] = array( 'title' => t('View Events Partys'), 'description' => t('View woe partys'), 'page callback' => 'drupal_get_form', 'page arguments' => array('party_view_js'), 'access arguments' => array('view party'), 'type' => MENU_NORMAL_ITEM, ); $items['party/woe_view'] = array( 'title' => t('View WoEs Partys'), 'description' => t('View event partys'), 'page callback' => 'drupal_get_form', 'page arguments' => array('party_view_js'), 'access arguments' => array('view party'), 'type' => MENU_NORMAL_ITEM, ); $items['partys/get_partys'] = array( 'page callback' => 'get_partys', 'access arguments' => array('view party'), 'type' => MENU_CALLBACK, ); $items['partys/get_partys_build'] = array( 'page callback' => 'get_partys_build', 'access arguments' => array('view party'), 'type' => MENU_CALLBACK, ); return $items; } /* 202 ** Implementacion de hook_perm() */ function party_perm(){ return array( 'create party event', 'create party woe', 'view party', ); } /** ** Funcion que crea el menu de las paginas del modulo */ function party_page(){ $list = party_menu(); $out .='<dl>'; foreach($list as $clave => $valor){ if($clave!='party'){ if(user_access($valor['access arguments']['0'])){ $out .= '<dt>'.l($valor['title'], $clave, array('attributes' => array('title' => $valor['description'],))).'</dt>'; $out .= '<dd>'.$valor['description'].'</dd>'; } } } $out.='</dl>'; return $out; } /* ** Implementacion of hook_user() */ function party_user($op, &$edit, &$account, $category){ global $user; $id = $account->uid; if($op == 'delete'){ $result=db_query('SELECT p.anid FROM {party} p INNER JOIN {assistance_node} an ON p.anid=an.nid WHERE an.uid = %d', $id); while($row=db_fetch_array($result)){ db_query('DELETE FROM {party} WHERE anid = %d',$row['anid']); } 203 $result = db_query("SELECT * FROM {node} WHERE (type = 'assistance_event' OR type = 'assistance_event') AND uid = 0"); while($data = db_fetch_object($result)){ assistance_delete($data); db_query('DELETE FROM {node} WHERE nid = %d', $data>nid); db_query('DELETE FROM {node_revisions} WHERE nid = %d', $data->nid); } } } /* ** Funcion que crea el formulario para crear una party para eventos */ function party_build_form( &$form_state = NULL){ global $base_path; //Miramos si ya estamos en un estado avanzado del formulario o si estamos en el primer paso. $step = isset($form_state['values']) ? (int)$form_state['storage']['step'] : 1; //Guardamos el siguiente paso $form_state['storage']['step'] = $step+1; //ponemos el titulo la descripción y el nombre del boton de envio correspondiente al paso en el que estamos $form['indicator'] = array( '#type' => 'fieldset', '#title'=>t('Step @number', array('@number'=>$step)) ); switch($step){ case 1: $type = 'event'; $title = 'Event'; if($_SERVER['REQUEST_URI']==$base_path.'party/add_party_woe'){ $type = 'woe'; $title = 'WoE'; } $form_state['storage']['type']=$type; $form['indicator']['event']= array( '#title' => t($title), '#type' => 'select', '#options' => party_get_event($type), 204 ); $form['indicator']['submit']= array( '#type' => 'submit', '#value' => t('next'), ); break; case 2: if(!isset($form_state['storage']['enid'])){ $form_state['storage']['enid'] = $form_state['values']['event']; } $form['indicator']['name']= array( '#title' => t('Party name'), '#type' => 'textfield', ); $form['indicator']['submit']= array( '#type' => 'submit', '#value' => t('next'), ); break; case 3: if(!isset($form_state['storage']['party_name'])){ $form_state['storage']['party_name'] = $form_state['values']['name']; } for($x=1; $x<16; $x++){ $var = variable_get('assistance_types', ''); $no_rebirth = db_result(db_query("SELECT name FROM {assistance_job} WHERE id = %d",$x)); $rebirth =db_result(db_query("SELECT name FROM {assistance_job} WHERE id = %d",$x+16)); if($var!=''){ $str = party_get_assistances($form_state['storage']['enid'], $no_rebirth, $rebirth); } if($str!=""){ $form['indicator']['job'.$x] = array( '#type' => 'fieldset', '#title'=>$rebirth."/".$no_rebirth, '#collapsible' => TRUE, 205 '#collapsed' => TRUE, ); $form['indicator']['job'.$x][$x]= array( '#type' => 'checkboxes', '#options' => $str, ); } } $str = party_get_assistances($form_state['storage']['enid'], 16,16); if($str!=""){ $form['indicator']['job16'] = array( '#type' => 'fieldset', '#title'=> 'supernovice', '#collapsible' => TRUE, '#collapsed' => TRUE, ); $form['indicator']['job16']['16']= array( '#type' => 'checkboxes', '#options' => $str, ); } $form['indicator']['save']= array( '#type' => 'submit', '#value' => t('Save party'), ); break; } return $form; } /* * Funcion de validacion del formulario para crear partys de evento */ function party_build_form_validate($form, &$form_state){ if($form_state['storage']['step']==2){ if($form_state['values']['event']=='null'){ form_set_error('event', t('Choose one')); } return; } 206 if($form_state['storage']['step']==3){ $result = db_query("SELECT DISTINCT party_name FROM {party} WHERE enid = %d",$form_state['storage']['enid']); if($form_state['values']['name']==''){ form_set_error('name', t('Enter name')); } while($row = db_fetch_array($result)){ if($row['party_name']==$form_state['values']['name']){ form_set_error('name', t('Name already in use on this event')); break; } } return; } if($form_state['storage']['step']==4){ $marcados = 0; for($x=1; $x<16; $x++){ if(is_array($form_state['values'][$x])){ foreach($form_state['values'][$x] as $clave => $valor){ if($valor==$clave){ $marcados++; } } } } if(is_array($form_state['values']['16'])){ foreach($form_state['values']['16'] as $clave => $valor){ if($valor==$clave){ $marcados++; } } } if($marcados==0){ drupal_set_message('Has de añadir minimo 1 persona a la party', 'error'); $form_state['storage']['step']=3; return; } if($marcados>12){ 207 drupal_set_message('Solo puedes añadir 12 personas a una party', 'error'); $form_state['storage']['step']=3; return; } } } /* * Funcion de proceso del formulario para crear partys de evento */ function party_build_form_submit($form, &$form_state){ if($form_state['storage']['step']<4){ return; } db_query("INSERT INTO {party_members} (enid, party_name, number) VALUES (%d, '%s', 0)",$form_state['storage']['enid'],$form_state['storage']['party_name']); for($x=1; $x<16; $x++){ if(is_array($form_state['values'][$x])){ foreach($form_state['values'][$x] as $clave => $valor){ if($valor==$clave){ db_query("UPDATE {assistance_node} SET status = 1 WHERE nid = %d",$valor); db_query("UPDATE {party_members} SET number = number+1 WHERE enid = %d AND party_name = '%s'",$form_state['storage']['enid'],$form_state['storage']['party_name'] ); db_query("INSERT INTO {party} (enid, party_name, type_event, anid) VALUES (%d, '%s', '%s', %d)",$form_state['storage']['enid'],$form_state['storage']['party_name'], $form_state['storage']['type'],$clave); } } } } if(is_array($form_state['values']['16'])){ foreach($form_state['values']['16'] as $clave => $valor){ if($valor==$clave){ db_query("UPDATE {assistance_node} SET status = 1 WHERE nid = %d",$valor); db_query("INSERT INTO {party} (enid, party_name, type_event, anid) VALUES (%d, '%s', '%s', %d)",$form_state['storage']['enid'],$form_state['storage']['party_name'], $form_state['storage']['type'],$clave); 208 db_query("UPDATE {party_members} SET number = number+1 WHERE enid = %d AND party_name = '%s'",$form_state['storage']['enid'],$form_state['storage']['party_name'] ); } } } unset($form_state['storage']); } /* ** Funcion que crea el formulario para eliminar un miembro de una party */ function party_member_form( &$form_state = NULL){ global $base_path; //Miramos si ya estamos en un estado avanzado del formulario o si estamos en el primer paso. $step = isset($form_state['values']) ? (int)$form_state['storage']['step'] : 1; //Guardamos el siguiente paso $form_state['storage']['step'] = $step+1; //ponemos el titulo la descripción y el nombre del boton de envio correspondiente al paso en el que estamos $form['indicator'] = array( '#type' => 'fieldset', '#title'=>t('Step @number', array('@number'=>$step)) ); switch($step){ case 1: switch($_SERVER['REQUEST_URI']){ case $base_path.'party/add_member_party_event': $title = 'Event'; $type = 'event'; $op = 'add'; $options = party_get_event_nofull($type); break; case $base_path.'party/delete_member_party_event': $title = 'Event'; $type = 'event'; $op = 'del'; $options = party_get_event_js($type); break; 209 case $base_path.'party/add_member_party_woe': $title = 'WoE'; $type = 'woe'; $op = 'add'; $options = party_get_event_nofull($type); break; case $base_path.'party/delete_member_party_woe': $title = 'WoE'; $type = 'woe'; $op = 'del'; $options = party_get_event_js($type); break; } $form_state['storage']['type']=$type; $form['indicator']['event']= array( '#title' => t($title), '#type' => 'select', '#options' => $options, ); $form['indicator']['operation']= array( '#type' => 'hidden', '#value' => $op, ); $form['indicator']['submit']= array( '#type' => 'submit', '#value' => t('next'), ); break; case 2: if(!isset($form_state['storage']['enid'])){ $form_state['storage']['enid'] = $form_state['values']['event']; } if(!isset($form_state['storage']['op'])){ $form_state['storage']['op'] = $form_state['values']['operation']; } switch($_SERVER['REQUEST_URI']){ case $base_path.'party/add_member_party_event': $options = party_get_partys_nofull($form_state['storage']['enid']); 210 break; case $base_path.'party/delete_member_party_event': $options = party_get_partys($form_state['storage']['enid']); break; case $base_path.'party/add_member_party_woe': $options = party_get_partys_nofull($form_state['storage']['enid']); break; case $base_path.'party/delete_member_party_woe': $options = party_get_partys($form_state['storage']['enid']); break; } $form['indicator']['partyjs']= array( '#title' => t('Party name'), '#type' => 'select', '#options' => $options, ); if($form_state['storage']['op']=='add'){ $form['indicator']['party'] = array( '#value' => '<script type="text/javascript" src="'.$base_root.$base_path.'/misc/drupal.js"> </script><script type="text/javascript" src="'.$base_root.$base_path.'/misc/collapse.js"></script> <form name="form1" method="post" action="www.site.com"><div id="partyshow" style="padding: 10px;"></div>', ); } $form['indicator']['submit']= array( '#type' => 'submit', '#value' => t('next'), ); break; case 3: if(!isset($form_state['storage']['party'])){ $party = explode("/",$form_state['values']['partyjs']); $form_state['storage']['party'] = $party[1]; } switch($_SERVER['REQUEST_URI']){ case $base_path.'party/add_member_party_event': 211 for($x=1; $x<16; $x++){ $var = variable_get('assistance_types', ''); $no_rebirth = db_result(db_query("SELECT name FROM {assistance_job} WHERE id = %d",$x)); $rebirth =db_result(db_query("SELECT name FROM {assistance_job} WHERE id = %d",$x+16)); if($var!=''){ $str = party_get_assistances($form_state['storage']['enid'], $no_rebirth, $rebirth); } if($str!=""){ $form['indicator']['job'.$x] = array( '#type' => 'fieldset', '#title'=>$rebirth."/".$no_rebirth, '#collapsible' => TRUE, '#collapsed' => TRUE, ); $form['indicator']['job'.$x][$x]= array( '#type' => 'checkboxes', '#options' => $str, ); } } $str = party_get_assistances($form_state['storage']['enid'], 16,16); if($str!=""){ $form['indicator']['job16'] = array( '#type' => 'fieldset', '#title'=> 'supernovice', '#collapsible' => TRUE, '#collapsed' => TRUE, ); $form['indicator']['job16']['16']= array( '#type' => 'checkboxes', '#options' => $str, ); } 212 $form['indicator']['save']= array( '#type' => 'submit', '#value' => t('Add members'), ); break; case $base_path.'party/delete_member_party_event': $form['indicator']['members']= array( '#type' => 'checkboxes', '#options' => get_members_party($form_state['storage']['enid'] , $form_state['storage']['party']), ); $form['indicator']['save']= array( '#type' => 'submit', '#value' => t('Delete members'), ); break; case $base_path.'party/add_member_party_woe': for($x=1; $x<16; $x++){ $var = variable_get('assistance_types', ''); $no_rebirth = db_result(db_query("SELECT name FROM {assistance_job} WHERE id = %d",$x)); $rebirth =db_result(db_query("SELECT name FROM {assistance_job} WHERE id = %d",$x+16)); if($var!=''){ $str = party_get_assistances($form_state['storage']['enid'], $no_rebirth, $rebirth); } if($str!=""){ $form['indicator']['job'.$x] = array( '#type' => 'fieldset', '#title'=>$rebirth."/".$no_rebirth, '#collapsible' => TRUE, '#collapsed' => TRUE, ); $form['indicator']['job'.$x][$x]= array( '#type' => 'checkboxes', 213 '#options' => $str, ); } } $str = party_get_assistances($form_state['storage']['enid'], 16,16); if($str!=""){ $form['indicator']['job16'] = array( '#type' => 'fieldset', '#title'=> 'supernovice', '#collapsible' => TRUE, '#collapsed' => TRUE, ); $form['indicator']['job16']['16']= array( '#type' => 'checkboxes', '#options' => $str, ); } $form['indicator']['save']= array( '#type' => 'submit', '#value' => t('Add members'), ); break; case $base_path.'party/delete_member_party_woe': $form['indicator']['members']= array( '#type' => 'checkboxes', '#options' => get_members_party($form_state['storage']['enid'] , $form_state['storage']['party']), ); $form['indicator']['save']= array( '#type' => 'submit', '#value' => t('Delete members'), ); break; } break; } return $form; } /* 214 * Funcion de validacion del formulario para añadir y eliminar miembros de party */ function party_member_form_validate($form, &$form_state){ if($form_state['storage']['step']==2){ if($form_state['values']['event']=='null'){ form_set_error('event', t('Choose one')); } return; } if($form_state['storage']['step']==3){ if($form_state['values']['partyjs']=='null'){ form_set_error('party', t('Choose one')); } return; } if($form_state['storage']['step']==4){ if($form_state['storage']['op']=='add'){ $marcados = 0; for($x=1; $x<16; $x++){ if(is_array($form_state['values'][$x])){ foreach($form_state['values'][$x] as $clave => $valor){ if($valor==$clave){ $marcados++; } } } } if(is_array($form_state['values']['16'])){ foreach($form_state['values']['16'] as $clave => $valor){ if($valor==$clave){ $marcados++; } } } if($marcados==0){ drupal_set_message('Has de añadir minimo 1 persona a la party', 'error'); $form_state['storage']['step']=3; return; 215 } $actuales = db_result(db_query("SELECT number FROM {party_members} WHERE enid = %d AND party_name = '%s'", $form_state['storage']['enid'],$form_state['storage']['party'])); if(($marcados+$actuales)>12){ $num = 12-$actuales; drupal_set_message('Solo puedes añadir '.$num.' personas más a esta party', 'error'); $form_state['storage']['step']=3; return; } } else{ foreach($form_state['values']['members'] as $clave => $valor){ if($valor==$clave){ $marcados++; } } if($marcados==0){ drupal_set_message('Has de eliminar minimo 1 persona a la party', 'error'); $form_state['storage']['step']=3; return; } } return; } } /* * Funcion de proceso del formulario para añadir y eliminar miembros de party */ function party_member_form_submit($form, &$form_state){ if($form_state['storage']['step']<4){ return; } if($form_state['storage']['op']=='add'){ for($x=1; $x<16; $x++){ if(is_array($form_state['values'][$x])){ foreach($form_state['values'][$x] as $clave => $valor){ if($valor==$clave){ 216 db_query("UPDATE {assistance_node} SET status = 1 WHERE nid = %d",$valor); db_query("UPDATE {party_members} SET number = number+1 WHERE enid = %d AND party_name = '%s'",$form_state['storage']['enid'],$form_state['storage']['party']); db_query("INSERT INTO {party} (enid, party_name, type_event, anid) VALUES (%d, '%s', '%s', %d)",$form_state['storage']['enid'],$form_state['storage']['party'],$form _state['storage']['type'],$clave); } } } } if(is_array($form_state['values']['16'])){ foreach($form_state['values']['16'] as $clave => $valor){ if($valor==$clave){ db_query("UPDATE {assistance_node} SET status = 1 WHERE nid = %d",$valor); db_query("INSERT INTO {party} (enid, party_name, type_event, anid) VALUES (%d, '%s', '%s', %d)",$form_state['storage']['enid'],$form_state['storage']['party'],$form _state['storage']['type'],$clave); db_query("UPDATE {party_members} SET number = number+1 WHERE enid = %d AND party_name = '%s'",$form_state['storage']['enid'],$form_state['storage']['party']); } } } } else{ foreach($form_state['values']['members'] as $clave => $valor){ if($valor==$clave){ db_query("UPDATE {assistance_node} SET status = 0 WHERE nid = %d",$valor); db_query("DELETE FROM {party} WHERE anid = %d",$valor); db_query("UPDATE {party_members} SET number = number-1 WHERE enid = %d AND party_name = '%s'",$form_state['storage']['enid'],$form_state['storage']['party']); } } $num = db_result(db_query("SELECT number FROM {party_members} WHERE enid = %d AND party_name = '%s'",$form_state['storage']['enid'],$form_state['storage']['party'])); if($num == 0){ 217 db_query("DELETE FROM {party_members} WHERE enid = %d AND party_name = '%s'",$form_state['storage']['enid'],$form_state['storage']['party']); } } unset($form_state['storage']); } /* * Funcion que implementa la vista de las partys de eventos usando ajax */ function party_view_js(){ global $base_root, $base_path; $type = 'event'; $title = 'Event'; if($_SERVER['REQUEST_URI']==$base_path.'party/woe_view'){ $type = 'woe'; $title = 'WoE'; } $form['events'] = array( '#type' => 'select', '#title' => t($title), '#options' => party_get_event_js($type), ); $form['partys'] = array( '#value' => '<script type="text/javascript" src="'.$base_root.$base_path.'/misc/drupal.js"> </script><script type="text/javascript" src="'.$base_root.$base_path.'/misc/collapse.js"></script> <form name="form1" method="post" action="www.site.com"><div id="capa" style="padding: 10px;"></div>', ); return $form; } /* ** Funcion que devuelve los eventos que tienen alguna asistencia sin asignar aun, son del tipo pasado por parametro y la fecha de inicio es mayor que la fecha actual */ function party_get_event($type){ $time = time(); 218 $event['null'] = 'Escoge uno'; if($type == 'woe'){ $event['null'] = 'Escoge una'; } $result = db_query("SELECT n.nid, n.title, e.event_start FROM {node} n INNER JOIN {event} e ON e.nid = n.nid WHERE n.type = '%s'",$type); while($row = db_fetch_array($result)){ if(strtotime($row[event_start])>$time){ $result2= db_query("SELECT status FROM {assistance_node} WHERE enid = %d",$row['nid']); $asistencia = FALSE; while($row2 = db_fetch_array($result2)){ if($row2['status']==0){ $asistencia = TRUE; break; } } if($asistencia== TRUE){ $event[$row['nid']] = $row['title']; } } } return $event; } /* ** Funcion que devuelve el listado de asistencias sin asignar para un evento pasado por parametro y para unos jobs concretos */ function party_get_assistances($nid, $job1, $job2){ $var = variable_get('assistance_types', ''); if($var!=''){ $result = db_query("SELECT an.nid, u.name, an.job FROM ({assistance_node} an INNER JOIN {users} u ON an.uid = u.uid) INNER JOIN {content_field_job} pj ON an.job=pj.nid WHERE an.enid = %d AND an.status = 0 AND (pj.field_job_value='%s' OR pj.field_job_value='%s')", $nid, $job1, $job2); while($row = db_fetch_array($result)){ $assistance[$row['nid']] = l($row['name'],drupal_get_path_alias('node/'.$row[job]),array('attributes ' => array('target' => '_blank',))); } } return $assistance; 219 } /* * Funcion que devuelve el codigo html que mostrará todas las partys de un evento pasado por parametro */ function get_partys($id){ global $base_root, $base_path; $result = db_query("SELECT DISTINCT (party_name) FROM {party} WHERE enid = %d",$id); $out.='<script type="text/javascript" src="'.$base_root.$base_path.'/misc/drupal.js"> </script><script type="text/javascript" src="'.$base_root.$base_path.'/misc/collapse.js"></script> <form name="form1" method="post" action="www.site.com">'; while($row = db_fetch_array($result)) { $x=0; $out.='<fieldset class=" collapsible collapsed"><legend>'.$row['party_name'].'</legend><table border="0">'; $result2 = db_query("SELECT u.name, p.anid FROM {party} p INNER JOIN {assistance_node} an ON p.anid=an.nid INNER JOIN {users} u ON an.uid=u.uid WHERE p.party_name = '%s' AND p.enid = %d", $row['party_name'], $id); while($row = db_fetch_array($result2)) { $x++; $out.='<tr><td width="20%">Miembro '.$x.': </td><td align="left">'.l($row['name'],drupal_get_path_alias('node/'.$row[anid])). '</td></tr>'; } $out.='</table></fieldset>'; } return drupal_json(array('set'=>$out)); return $out; } /* * Funcion que devuelve los eventos que tienen alguna party creada */ function party_get_event_js($type){ $time = time(); $event['null'] = 'Escoge uno'; if($type == 'woe'){ $event['null'] = 'Escoge una'; } 220 $result = db_query("SELECT n.nid, n.title, e.event_start FROM {node} n INNER JOIN {party} p ON n.nid =p.enid INNER JOIN {event} e ON e.nid = n.nid WHERE n.type = '%s'",$type); while($row = db_fetch_array($result)){ if(strtotime($row[event_start])>$time){ $event[$row['nid']] = $row['title']; } } return $event; } /* * Funcion que devuelve los eventos que tienen alguna party creada sin llenar y asistencias pendientes */ function party_get_event_nofull($type){ $time = time(); $event['null'] = 'Escoge uno'; if($type == 'woe'){ $event['null'] = 'Escoge una'; } $result = db_query("SELECT n.nid, n.title, e.event_start FROM {node} n INNER JOIN {event} e ON e.nid = n.nid WHERE n.type = '%s'",$type); while($row = db_fetch_array($result)){ if(strtotime($row[event_start])>$time){ $num_partys = count(party_get_partys_nofull($row['nid'])); if($num_partys>1){ $result2 = db_result(db_query("SELECT COUNT(an.nid) FROM ({assistance_node} an INNER JOIN {users} u ON an.uid = u.uid) WHERE an.enid = %d AND an.status = 0",$row['nid'])); if($result2>0){ $event[$row['nid']] = $row['title']; } } } } return $event; } /* * Funcion que devuelve el nombre de las partys de un evento */ function party_get_partys($id){ 221 $partys['null'] = 'Escoge una'; $result = db_query("SELECT DISTINCT (party_name) FROM {party} WHERE enid = %d",$id); while($row = db_fetch_array($result)) { $partys[$id.'/'.$row['party_name']]=$row['party_name']; } return $partys; } /* * Funcion que devuelve el nombre de las partys de un evento que no esten llenas */ function party_get_partys_nofull($id){ $partys['null'] = 'Escoge una'; $result = db_query("SELECT DISTINCT party_name FROM {party} WHERE enid = %d", $id); while ($row = db_fetch_array($result)){ $result2 = db_result(db_query("SELECT number FROM {party_members} WHERE enid = %d AND party_name = '%s'",$id, $row['party_name'])); if($result2<12){ $partys[$id.'/'.$row['party_name']]=$row['party_name']; } } return $partys; } /* * Funcion que devuelve el codigo html que mostrará la party del evento pasada por parametro */ function get_partys_build($eid , $pid=0){ global $base_root, $base_path; $result = db_query("SELECT DISTINCT (party_name) FROM {party} WHERE enid = %d AND party_name = '%s'",$eid,$pid); $out.='<script type="text/javascript" src="'.$base_root.$base_path.'/misc/drupal.js"> </script><script type="text/javascript" src="'.$base_root.$base_path.'/misc/collapse.js"></script> <form name="form1" method="post" action="www.site.com">'; while($row = db_fetch_array($result)) { $x=0; $out.='<fieldset class=" collapsible collapsed"><legend>'.$row['party_name'].'</legend><table border="0">'; 222 $result2 = db_query("SELECT u.name, p.anid FROM {party} p INNER JOIN {assistance_node} an ON p.anid=an.nid INNER JOIN {users} u ON an.uid=u.uid WHERE p.party_name = '%s' AND p.enid = %d", $row['party_name'], $eid); while($row = db_fetch_array($result2)) { $x++; $out.='<tr><td width="14%">Miembro '.$x.': </td><td align="left">'.l($row['name'],drupal_get_path_alias('node/'.$row[anid])). '</td></tr>'; } $out.='</table></fieldset>'; } return drupal_json(array('set'=>$out)); } /* * Funcion que devuelve los miembros de una party de un evento */ function get_members_party($eid , $pid){ $result = db_query("SELECT an.nid, u.name, an.job FROM ({party} p INNER JOIN {assistance_node} an ON p.anid=an.nid) INNER JOIN {users} u ON u.uid=an.uid WHERE p.enid = %d AND p.party_name='%s'",$eid,$pid); while($row = db_fetch_array($result)) { $members[$row['nid']] = l($row['name'],drupal_get_path_alias('node/'.$row[job]),array('attributes ' => array('target' => '_blank',))); } return $members; } Archivo party_build.js /* *Jquery para el modulo party */ // $Id$ // Global killswitch: only run if we are in a supported browser. //comprobamos que este activado javascript if (Drupal.jsEnabled) { //si esta funcionando definimos la función $(function(){ //Este código nos permite comprobar cada vez que seleccionamos un elemento del select. $('#edit-partyjs-wrapper').change(function(){ 223 //Cada vez que seleccionamos un elemento del select recuperamos el valor del atributo value. $('#edit-partyjs').each(function () { var valor = $(this).val(); //alert (valor); //Montamos una función para crear la url mediante la cual pasaremos los datos. var ruta = 'partys/get_partys_build/'; var url2 = Drupal.settings.basePath + ruta + valor; // alert (url2); //La función que ejecutará el cambio de html con la información recuperada. var updateUsers = function(data) { $('#partyshow').html(data.set); } //La magia de jquery con ajax, las explicaciones son de mucha ayuda $.ajax({ type: 'POST', url: url2, // Which url should be handle the ajax request. This is the url defined in the <a> html tag success: updateUsers, // The js function that will be called upon success request dataType: 'json', //define the type of data that is going to get back from the server data: 'js=1' //Pass a key/value pair }); return false; // return false so the navigation stops here and not continue to the page in the link }); }); }); } Archivo party_event.js /* *Jquery para el modulo party */ // $Id$ // Global killswitch: only run if we are in a supported browser. 224 //comprobamos que este activado javascript if (Drupal.jsEnabled) { //si esta funcionando definimos la función $(function(){ //Este código nos permite comprobar cada vez que seleccionamos un elemento del select. $('#edit-events').change(function(){ //Cada vez que seleccionamos un elemento del select recuperamos el valor del atributo value. $(this).each(function () { var valor = $(this).val(); // alert (valor); //Montamos una función para crear la url mediante la cual pasaremos los datos. var ruta = 'partys/get_partys/'; var url2 = Drupal.settings.basePath + ruta + valor; //alert (url2); //La función que ejecutará el cambio de html con la información recuperada. var updateUsers = function(data) { $('#capa').html(data.set); } //La magia de jquery con ajax, las explicaciones son de mucha ayuda $.ajax({ type: 'POST', url: url2, // Which url should be handle the ajax request. This is the url defined in the <a> html tag success: updateUsers, // The js function that will be called upon success request dataType: 'json', //define the type of data that is going to get back from the server data: 'js=1' //Pass a key/value pair }); return false; // return false so the navigation stops here and not continue to the page in the link }); }); }); 225 } 12.6 Tema Propio Archivo ragnarok.info name = Ragnarok description = sin screenshot = screenshot.gif core = "6.x" engine = phptemplate regions[header] = Header regions[left] = First sidebar regions[right] = Second sidebar regions[footer_block] = Footer features[] features[] features[] features[] features[] features[] features[] features[] = = = = = = = = logo name node_user_picture comment_user_picture search favicon primary_links secondary_links stylesheets[all][] = css/style.css Archivo page.tpl.php <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language ?>" lang="<?php print $language->language ?>" dir="<?php print $language->dir ?>"> <head> <title><?php print $head_title; ?></title> <?php print $head; ?> <?php print $styles; ?> <?php print $scripts; ?> </head> <body class="<?php print $body_classes; ?>"> <div id="page"> <div id="header"><!--header --> <div id="logo-title"> <?php if ((!empty($logo))&&(!empty($site_name))): ?> <a href="<?php print $front_page; ?>" title="<?php print t('Home'); ?>" rel="home" id="logo"> <img src="<?php print $logo; ?>" alt="<?php print t('Home'); ?>"/> </a> <!-- <h1 id="site-name"> <a href="<?php print $front_page ?>" title="<?php print t('Home'); ?>" rel="home"><span><?php print $site_name; ?></span></a> </h1>--> <?php endif; ?> 226 </div> <!-- /logo-title --> <?php if ($header): ?> <div id="header-region"> <?php print $header; ?> </div> <?php endif; ?> </div> <!-- /header --> <div id="main"> <!-- main --> <div id="content"> <div id="content-in" class="column"> <?php if ($breadcrumb || $messages || $help || $title || $tabs): ?> <div id="content-header"> <!-- content-header --> <?php print $breadcrumb; ?> <?php if ($title): ?> <h1 class="title"><?php print $title; ?></h1> <?php endif; ?> <?php if ($tabs): ?> <div class="tabs"><?php print $tabs; ?></div> <?php endif; ?> <?php print $messages; ?> <?php print $help; ?> </div> <!-- /content-header --> <?php endif; ?> <div id="content-area"> <?php print $content; ?> </div> <!-- /content-area --> </div> </div> <!-- /content-inner /content --> <?php if (!empty($primary_links) || !empty($secondary_links)): ?> <div id="navigation" class="menu <?php if (!empty($primary_links)) { print "with-main-menu"; } if (!empty($secondary_links)) { print " with-sub-menu"; } ?>"> <?php if (!empty($primary_links)){ print theme('links', $primary_links, array('id' => 'primary', 'class' => 'links main-menu')); } ?> <?php if (!empty($secondary_links)){ print theme('links', $secondary_links, array('id' => 'secondary', 'class' => 'links submenu')); } ?> </div> <!-- /navigation --> <?php endif; ?> <?php if ($left): ?> <div id="left" class="sidebar"><!-- sidebar-left --> <?php print $left; ?> </div> <?php endif; ?> <!-- /sidebar-left --> <?php if ($right): ?> <div id="right" class="sidebar"><!-- /sidebar-right --> <?php print $right; ?> </div> <?php endif; ?> <!-- /sidebar-right --> </div> <!-- /main --> <?php if(!empty($footer_message) || !empty($footer_block)): ?> <!-footer --> <div id="footer"> <?php print $footer_message; ?> <?php print $footer_block; ?> </div> <!-- /footer --> 227 <?php endif; ?> </div> <!-- /page --> <?php print $closure; ?> </body> </html> Archivo node.tpl.php <div id="node-<?php print $node->nid; ?>" class="node<?php if ($sticky) { print ' sticky'; } ?><?php if (!$status) { print ' node-unpublished'; } ?> clear-block"> <?php print $picture ?> <?php if (!$page): ?> <h2><a href="<?php print $node_url ?>" title="<?php print $title ?>"><?php print $title ?></a></h2> <?php endif; ?> <div class="meta"> <?php if ($submitted): ?> <span class="submitted"><?php print $submitted ?></span> <?php endif; ?> </div> <div class="content"> <?php print $content ?> </div> <?php print $links; ?> </div> Archivo template.php <?php function ragnarok_breadcrumb($breadcrumb){ $char[0] = ' <(o.o<) '; $char[1] = ' ^(o.o)^ '; $char[2] =' (>o.o)> '; if(!empty($breadcrumb)){ $length= count($breadcrumb); for($x=0; $x<$length; $x++){ $string .= $breadcrumb[$x].$char[$x%3]; /*else{ $string .= $breadcrumb[$x]; }*/ } } return '<div class="breadcrumb-ro">'.$string.'</div>'; } 228 Archivo style.css body{ font-family:helvetica; background-color:#92c489; font-size:12px; } .sidebar a:link, .sidebar a:visited,.node a:link, .node a:visited{ color: #92c489; text-decoration:none; } .sidebar a:hover,.node a:hover{ color: #92c489; text-decoration:none; font-weight:bold; } a:link, a:visited{ color: #32562b; text-decoration:none; } a:hover{ color: #32562b; text-decoration:none; font-weight:bold; } #page { width: 960px; margin: 0 auto; } #header{ height:150px; border-bottom:1px; border-bottom-style:solid; background-color:#7688a7; } #logo-title{ text-align:center; } #content { float: left; width: 100%; margin-right: -100%; padding: 0; } #content-header{ padding:10px; background-color:#589a4d; } .sidebar { float: left; background-color:#32562b; } 229 #right { padding:10px; width: 200px; float: right; } #left { padding:10px; width: 190px; margin-right:-190px; } .column, .sidebar left{ margin-left: 220px; } .column, .sidebar right{ margin-right: 230px; } #footer { float: none; clear: both; } #header,#footer,.mission,.breadcrumb,.node { clear: both; } #navigation { float: left; margin-left: 0; margin-right: -100%; padding: 0; width: 100%; height: 40px; } .with-navigation #content, .with-navigation .sidebar { margin-top: 40px; } .node{ padding:0px 10px 10px 10px; border-bottom: 1px; border-bottom-style: solid; background-color:#589a4d; } 230