La consola web de Opegnsys 2 está diseñada para ser altamente extensible mediante el desarrollo de plugins. Existe un conjunto de puntos de extensión, en los que cada plugin puede añadir nuevas características.
Básicamente hay dos puntos de extensión principalmente: el Panel Principal y el Panel Contextual. La consola web muestra en la parte superior de cada página una serie de pestañas. Una de ellas siempre es el Panel principal o de acciones, llamado simplemente "Panel" de aquí en adelante. Dentro de ese panel aparecen iconos/acciones genéricas que los plugins actualmente activos han introducido. Por ejemplo, algunas acciones de ejemplo podrían ser "Buscar Ordenador" o "Administrar usuarios".
El otro punto de extensión es el Panel Contextual que aparece en muchas páginas de la consola web, y que muestra acciones relacionadas con lo que actualmente se está mostrando al usuario. Esas acciones son puestas ahí por los plugins, dependiendo de la url o vista que actualmente se esté mostrando. Por ejemplo, si en el "navegador" se está mostrando actualmente un ordenador, el plugin de Inventorio de Hardware añade la acción de "Ver Perfil de Hardware" al panel contextual, que muestra el perfil de hardware del ordenador actual.
Todos los plugins se localizan dentro del árbol de directorios de Opengnsys en web/plugins/. El plugin sabrá su nombre UNIX por la carpeta en la que está localizada. Dentro de esa carpeta debe definir el fichero !init!.py que contendrá la clase Plugin, que heredará de !PluginBase, y es la que define la funcionalidad principal del plugin. La clase !PluginBase define todas las funciones que un plugin puede definir. Por ejemplo existe una función llamada "install" que permite al plugin ejecutar el código que necesite a la hora de crear un plugin.
Además, también es necesario definir el fichero plugin.conf, que contiene la configuración más básica del plugin, y está en formato INI. Esto incluye el nombre visible en la aplicación, una pequeña descripción y la versión. Además de eso, también contiene más o menos esa misma información sobre cada acción que defina el plugin, tanto si son acciones que vayan a aparecer en el Panel Principal como si van a aparecer en el Panel Contextual.
Vamos a crear un plugin muy sencillo, que mostrará una acción en el panel principal que mostrará una página que diga "Hola Mundo!". Nuestra filosofía de desarrollo incluye utilizar el Inglés como idioma tanto para el código como para los comentarios/documentación del mismo, y todo el proyecto está diseñado de esta manera. La consola de Opengnsys 2 tiene soporte de internacionalización, y por lo anteriormente explicado las cadenas base están en inglés. También las urls que generamos incluyen únicamente palabras en inglés. Y el nombre de los plugins también. Nuestro plugin por tanto se llamará "hello_world". Crea en web/pluginmanager/plugins/ el directorio "hello_world", y a partir de ahora trabajaremos desde ahí.
Dentro, crearemos el fichero plugin.conf, con el siguiente contenido:
[plugin]
human_name = Hello World! Tutorial 1
version = 1.0.0
[action/hello]
description = Displays the text Hello World! to the user
human_name = Hello
appear_in_main_panel = Yes
Lo que acabamos de hacer es describir nuestro plugin, y la acción "hello" (el nombre UNIX se obtiene por el nombre de la sección), en el formato INI y con la nomenclatura de Opengnsys. Pero sólo hemos descrito cierta información, aun no hemos programado cómo funcionará. Esto lo haremos en Python dentro de !init!.py:
#!python
'''
Hello World! plugin example
'''
from ..pluginbase import PluginBase
import hello
class Plugin(PluginBase):
'''
'''
def enable(self):
self.urls = ('action/hello',
hello.HelloView)
Como puedes observar, este código también ha sido muy escueto. Realmente sólo hemos definido la clase Plugin que hereda de !PluginBase y únicamente redefinimos la función enable. Por supuesto nuestro plugin tiene muchas más funciones definidas, pero están definidas en !PluginBase, y la mayoría se encuentran vacías: por ejemplo igual que existe la función enable, también existen disable, install, y uninstall.
En la función enable hemos definido un array de urls (self.urls). Es un array definido por parejas de url y vista que mostrar para dicha url. En nuestro caso, hemos definido una única url, 'action/hello', y esa url es manejada por la clase !HelloView del fichero hello dentro de nuestro plugin. Eso significa que cuando se acceda a 'http://direccion/de/nuestro/servidor/plugin/hello_world/action/hello', será la clase !HelloView del fichero pluginmanager/plugins/hello_world/hello.py quien se encargará de manejar y renderizar la página.
El siguiente paso es definir hello.py:
#!python
'''
Hello action
'''
import web
class HelloView:
def GET(self):
return web.ctx.render.plugins.hello_world.helloview()
Si has utilizado antes el framework para realizar aplicaciones web.py, es fácil darse cuenta de que la consola web lo utiliza: el formato del array de urls que vimos en el fichero !init!.py es el de web.py, y la vista definida en hello.py de nuevo usa el mismo sistema que web.py. En web.py, cada vista es una clase que puede tener definidos los métodos GET y/o POST. cuando una url sólo recibe argumentos por GET, se define el método GET, como es el caso.
Podríamos haber hecho un return "Hello World!" directamente dentro de GET(), pero entonces hubiese sido un Hello World! demasiado insulso. En vez de ello, hemos delegado a un template que imprima dicha cadena. De nuevo, usamos los templates de web.py. Hemos definido en web.ctx.render un "renderizador" de templates/plantillas que utiliza una plantilla maestra en la cual se mete la que nosotros definamos. Hemos usado la plantilla "hello" de nuestro plugin, que debemos definir dentro de un subdirectorio "templates" dentro de web/plugin_manager/plugins/hello_world/, y ahí crear el fichero hello.html siguiente:
$def with ()
$var title = _p("Hello World!")
$var tab: panel
$var hierarchy = []
$_p("Hello World!")
Este formato de templates de nuevo es el que usa web.py. No te asustes. la primera linea indica que está definiendo una función con cero argumentos ("def with ()"). Luego definimos tres variables que son usadas por la plantilla maestra, como es el título de la página, el tab que ha de mostrarse seleccionado, y la jerarquía de navegación que por simplicidad hemos dejado en blanco.
Para hacer uso de traducciones utilizamos la función _p() que está siempre accesible dentro de los templates de los plugins. Y precisamente hacemos uso de ella al imprimir, finalmente la cadena "Hello world".
Para usar el plugin, tenemos que arrancar el servidor de opengnsys si no lo tenías ya arrancado, y activarlo pinchando en "Enable" al lado del plugin, en la pestaña "Plugins" con un usuario que tenga permisos de administración. Luego, puedes ir al panel y verás que aparece nuestra acción, "Hello", y al hacer clic te aparecerá una página con la misma apariencia que el resto de la web donde aparece el mensaje "Hello World".