Fedora Community/PluginDesign

From FedoraProject

Jump to: navigation, search

Contents

MyFedora Plugin Design Document

Abstract

This document describes how MyFedora plugins are put together. The intent is to be a starting point to describe how each individual piece fits so that it is possible to develop extensions to MyFedora. Note that this document and the implementation may change as we refine the design.

Concepts

MyFedora is a tool for bringing together the Fedora infrastructure under one roof in an interface that is designed for usability. There are two central parts of the MyFedora design, the widget system portal and the toolbox. Plugins concern themselves with the toolbox. This is where systems like koji and bodhi are tied together for efficient access to the fedora infrastructure. The plugin system consists of three main concepts that work together to form a framework for building the toolbox. They are:

  • Resources
  • Tools
  • Data IDs

MyFedora is a TurboGears application which uses both the standard controller model and the routes model for plugins.

Resources

This is the starting point for MyFedora plugins. A resource is any abstract grouping such as "packages", "people" and "projects" which contain tools for viewing and manipulating data within the resource's context.

Resources are a self contained directory structure placed within a resource/ directory containing an object which inherits from the myfedora.plugins.Resource object. Things that are defined by the resource:

  • a master template, inherited by the tools, for showing an integrated interface no matter what tool is being used
  • template global variables for injecting variables used by the master template as well as useful variables which the tool can use
  • tool routes for creating urls for each of the registered tools

Example

myfedora/resources/testresource/testresource.py

#!python
from myfedora.plugin import Resource

class TestResource(Resource):
"""Test is an example resource"""

def __init__(self):
Resource.__init__(self, 'Test', # display name
'test', # resource id
'Test resource', # short description
'''Test resource for testing out the loaders
and acting as an example
''')

self.set_master_template('master')

def get_template_globals(self, *args, **kwargs):
result = Resource.get_template_globals(self, *args, **kwargs)

data = kwargs.get('data','')

tool_list = self.get_tool_list()

tool_urls = [] 
for tool in tool_list:
tool_urls.append((self.get_tool_url(tool.get_id(), data),
tool.get_display_name()))

result.update({'tool_urls': tool_urls,
'resource_name': self.get_id()
})

return result

def set_tool_route(self, route_map, tool):
tool_id = tool.get_id()
resource_id = self.get_id()

controller_id = self._namespace_id(tool_id)

if tool.is_default():
route_map.connect(self.url(resource_id),
contoller = controller_id,
resource = self)

r = self._route_cat(self.url(resource_id),
':data',
tool_id)

route_map.connect(r,
controller = controller_id,
data = '')

return controller_id

myfedora/resources/testresource/templates/master.html

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/">
<body>
<h1>Test Master Template</h1>
<ul>
<li py:for="turl in tool_urls">
<a href="${turl[0] }">${turl[1] }</a>
</li>
</ul>
</body>
</html>

Tools

A tool is a web app for viewing or manipulating data. For example Builds would be a tool for the package resource. Tools are implemented as self contained TurboGears controllers.

Tools have a self contained directory structure placed within a tools/ directory containing this structure:

<toolname>/
__init__.py
<classfiles>.py
templates/
__init__.py
<genshi template>.html
static/
images/
js/
css/

Tools inherit from myfedora.plugin.Tool which itself inherits from turbogears.Controller. All standard controller features are supported according to the resource's route configuration which the tool registers with.

Example

myfedora/tools/helloworldtool/helloworldtool.py

#!python
from myfedora.plugin import Tool
from turbogears import expose
import random

class HelloWorldTool(Tool):
def __init__(self, parent_resource):
Tool.__init__(self, parent_resource, # just pass in the parent_resource and the base class does the rest
'Hello World', # display name
'helloworld', # tool id
'Prints out hello', # short description
'Take the data and prints out hello data',  # long description
['test'] , # registers with the test resource
['test'] ) # is the default for the test resource

@expose(template='myfedora.tools.helloworldtool.templates.helloworld', allow_json=True)
def default(self, data=''):
result = self.get_parent_resource().get_template_globals()

result.update({'data': data})
return result

myfedora/tools/helloworldtool/templates/helloworld.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">

<!-- resource.get_master_template takes care of finding the master template no matter where it is on the system -->
<xi:include href="${myfedora.resource.get_master_template()}"/>
<body>
Hello ${data} how do you like resource ${resource_name}
</body>
</html>

This will print out the test resource's header and registered tool links as well as the Hello message. Notice we mapped the data variable from the resource route so that if I used a url such as /myfedora/test/John/helloworld it would print out:

Hello John how do you like resource Test

Data IDs

The data id is a pointer to a specific dataset the tools work on. For example the package resource considers each fedora package name to be a data id. Data id's are by standard in between the resource and the tool.

Why put data id's between the resource and the tool? Doesn't it make things harder?

Well yes and no. It does make us have to use routes and makes it harder to deal with the case where no data is given which could mean show me all packages in the build tool for example. We do it this way because while the current Fedora infrastructure is based off of an application centric model, you go to koji for build and bodhi for updates, in MyFedora we are data centric. What this url, /myfedora/package/dbus/builds, says in plain english is go to the package dbus and look at it's builds. A common misconception is that the builds tool is koji. It is actually a mashup of koji and bodhi as well as other infrastructure bits in the future. While koji supplies the basis of the data via the data_id, the builds tool then goes out and queries bodhi to see if the build has been pushed or requested somewhere. One can then request or rescind a push from the builds tools if they have the right permissions in FAS.

There needs to be a little more look at routes so that we can drop you off into a controller and the controller works just like any other controller. I think right now it is slightly broken in that only exact matches will be routed correctly so for instance /myfedora/package/dbus/builds/push_to_testing wouldn't work if you had added a push_to_testing controller.


Conclusion

The plugin system allows a person or group of people to write integration points for various interesting pieces of our infrastructure using similar techniques to writing a standalone TurboGears app along with some boilerplate glue code. In fact once it is done it should be easy enough to modify an existing project to integrate with MyFedora. That is not the goal of MyFedora however. While a quick first pass of a tool such as transflex could be that easy it should evolve into integrating with the existing tools and infrastructure. For instance it would be nice on the builds page to see what percentage of a package is currently translated and provide a link for translators to do more translation. Standard libraries and documentation will be developed for this purpose.

Another advantage of this system is the ability to write one tool and hook it up to multiple resources. In the tutorial that is being written it will show how the build tool hooks into the packages resource and then the small changes needed to be made to hook into the newly created peoples resource.