67
by mattgiuca
doc: Added app_howto doc, a guide on IVLE apps interface. |
1 |
IVLE - App Authoring Guide |
2 |
========================== |
|
3 |
||
4 |
Author: Matt Giuca |
|
5 |
Date: 17/12/2007 |
|
6 |
||
7 |
Intended audience: IVLE developers who wish to write a new application for |
|
8 |
IVLE's plugin interface. |
|
9 |
||
10 |
IVLE's modular architecture allows new applications ("apps") to be easily |
|
11 |
written and added to the site. An app is just a Python program which conforms |
|
12 |
to a small API, plus a few additional configurations. |
|
13 |
||
14 |
Application Specification |
|
15 |
------------------------- |
|
16 |
||
17 |
An application consists of two parts: |
|
18 |
||
19 |
* A Python package in the `apps` directory of IVLE. (That is, a directory with |
|
20 |
the name of the application containing a file `__init__.py`). |
|
21 |
* An entry in the applications database, stored in the file `conf/apps.py`. |
|
22 |
||
23 |
The entry in the apps database allows IVLE to locate and run the application. |
|
24 |
The package contains the application's code. |
|
25 |
||
26 |
### App name ### |
|
27 |
||
28 |
Applications may be known by three distinct names: |
|
29 |
||
30 |
* The "directory name" ("`dir`") is the most common identifier used for an |
|
31 |
app. This is the name of the app's package directory. |
|
32 |
* The "URL name" is the URL path segment used to publically identify the |
|
33 |
application. Is is usually the same as the dir name but may be distinct. |
|
34 |
* The "friendly name" ("`name`") is the name shown to users, eg, in the title |
|
35 |
bar and in the tabs. |
|
36 |
||
37 |
Applications Database Entry |
|
38 |
--------------------------- |
|
39 |
||
40 |
The file `conf/apps.py` is the applications database. (No, it isn't a real |
|
41 |
database, just a Python file with a dictionary in it). Each application is |
|
42 |
defined in a variable. This is merely a convenience so they don't all have |
|
43 |
to be defined inside the dictionary. |
|
44 |
||
45 |
Each application should be created by calling the App constructor, with the |
|
46 |
following fields: |
|
47 |
||
48 |
* `dir` : string - The "directory name" of the app. |
|
49 |
* `name` : string - The "friendly name" of the app. |
|
50 |
* `requireauth` : bool - If True, will automatically require authentication |
|
51 |
(but not authorization). |
|
52 |
* `hashelp` : bool - If True, this app will be given a help entry. |
|
53 |
||
54 |
Each application should be given an entry in the `app_url` dict, mapping its |
|
55 |
"url name" to the App variable. |
|
56 |
||
57 |
Applications which require a tab in the IVLE interface should have their "url |
|
58 |
names" added to the `apps_in_tabs` list as well. |
|
59 |
||
60 |
Application Interface |
|
61 |
--------------------- |
|
62 |
||
68
by mattgiuca
app_howto: Details of application interface. |
63 |
The application directory must have two special files: |
64 |
||
65 |
* `__init__.py` is the application entrypoint, discussed below. |
|
66 |
* `help.html` is the application's help file. Only required if `hashelp` is |
|
67 |
set to True in the application database. |
|
68 |
||
69 |
The directory may contain any other files you wish, including other Python |
|
70 |
modules to import. Note that no files places in the application directory or |
|
71 |
its subdirectories will be directly visible from the web. |
|
72 |
||
73 |
If you wish to make static files such as images, JavaScript and CSS content |
|
74 |
available, they must be placed in `media/apps/yourapp`. Import common.util and |
|
75 |
use `util.make_path` to generate URLs which point to the media directory. |
|
76 |
||
77 |
`__init__.py` must contain a function `handle(req)` which takes 1 argument. |
|
78 |
The argument, "req", will be passed an IVLE Request object. This is very |
|
79 |
similar to the mod_python/Apache Request object but has an |
|
80 |
independently-defined interface. The Request object provides input data in its |
|
81 |
fields. It also provides a `write` method through which the application sends |
|
82 |
its output. |
|
83 |
||
84 |
The `handle` function has no return value. |
|
85 |
||
86 |
The application should operate by reading input from req, setting its fields |
|
87 |
accordingly, then writing the output data. The HTTP response status is set by |
|
88 |
one of the fields of the Request. It also provides error and redirection |
|
89 |
functions which halt execution by throwing an exception. |
|
90 |
||
91 |
See the documentation on `dispatch.request` for the details of this object. |
|
92 |
||
93 |
### Help file ### |
|
94 |
||
95 |
Applications with `hashelp` set to True in the database are required to have |
|
96 |
an additional file, "help.html". This file's contents are displayed by the |
|
97 |
Help app. |
|
98 |
||
99 |
This is not an ordinary HTML file. It should be a valid XHTML file except |
|
100 |
should not contain html or body tags (its contents should just be the inside |
|
101 |
of a body tag). This is equivalent to the output of an app which has |
|
102 |
`write_html_head_foot` set to True. Note that this means it is not a valid XML |
|
103 |
file, but it will be valid once rendered by the Help app. |
|
104 |
||
105 |
The help file will be styled by IVLE's default style sheet. Please use h2 for |
|
106 |
headings (h1 will be used for the main page heading). Use other HTML elements |
|
107 |
in a natural way and they will be styled accordingly. |
|
108 |
||
109 |
Important notes |
|
110 |
--------------- |
|
111 |
||
112 |
* The settings of the Request object can only be set before any writing takes |
|
113 |
place. This is necessary to ensure the correct HTTP and HTML headers can be |
|
114 |
written before the first actual piece of data is written. Any settings which |
|
115 |
are set after the first write will be ignored. |
|
116 |
* Similarly, throwing errors or redirects after the first write will not have |
|
117 |
the intended effects, as the HTTP headers will not be written. |
|
118 |
* Never generate absolute URLs directly (either site-absolute or |
|
119 |
world-absolute). Applications should not guess where IVLE is located in the |
|
120 |
site's URL hierarchy. Instead use `common.util.make_path`, and supply it |
|
121 |
with a path relative to the IVLE site root. |
|
122 |
* All HTML pages generated by the app should set `req.write_html_head_foot` to |
|
123 |
True (which will decorate the page in the IVLE theme and interface). All |
|
124 |
non-HTML pages should set it to False or the output will be corrupted by |
|
125 |
HTML headers. An exception to this rule is an app such as "serve" which |
|
126 |
serves user applications which should not be decorated by the IVLE |
|
127 |
interface. |
|
128 |
* Applications which wish to access the student's file system or subversion |
|
129 |
dynamically (using Ajax) can do so through the `fileservice` app. This will |
|
130 |
be described in detail in another document. See the `files` app for an |
|
131 |
example. |
|
132 |
||
67
by mattgiuca
doc: Added app_howto doc, a guide on IVLE apps interface. |
133 |
Example Application |
134 |
------------------- |
|
135 |
||
136 |
This section shows the creation of a "Hello World" application which simply |
|
137 |
prints some text, inside the IVLE interface. The app's name will be "hello", |
|
138 |
"hello" and "Hello World" respectively. |
|
139 |
||
140 |
Firstly, create a directory, `apps/hello`. Create a file |
|
141 |
`apps/hello/__init__.py` with the following contents: |
|
142 |
||
143 |
def handle(req): |
|
144 |
req.content_type = "text/html" |
|
145 |
req.write_html_head_foot = True |
|
146 |
req.write("<p>Hello, IVLE!</p>\n") |
|
147 |
||
148 |
Now, edit the file `conf/apps.py`, and add the following lines: |
|
149 |
||
150 |
app_hello = App() |
|
151 |
app_hello.dir = "hello" |
|
152 |
app_hello.name = "Hello World" |
|
153 |
app_hello.requireauth = False |
|
154 |
app_hello.hashelp = False |
|
155 |
||
68
by mattgiuca
app_howto: Details of application interface. |
156 |
Add `"hello" : app\_hello,` to the app\_url dictionary. Add `"hello"` to the |
157 |
apps\_in\_tabs list. |
|
67
by mattgiuca
doc: Added app_howto doc, a guide on IVLE apps interface. |
158 |
|
159 |
Now restart the web server, and the "Hello World" tab should appear. Click |
|
160 |
the tab to call your new app. |
|
161 |
||
162 |
Note that the page output includes the standard IVLE interface and style |
|
163 |
theme (by virtue of setting `req.write_html_head_foot` to True). The data that |
|
164 |
the Hello World app itself outputs should be written assuming it is inside an |
|
165 |
XHTML body element. The final output will be valid XHTML 1.0 Strict if the |
|
166 |
application's output is. |
|
167 |
||
168 |
### Making a file dump ### |
|
169 |
||
170 |
You can modify the application to dump files from the students directories |
|
171 |
easily. You may wish to set `requireauth` to True, which will require that a |
|
172 |
user is logged in. (Note that it doesn't say anything about which user must be |
|
173 |
logged in - any student will still be able to read any other student's files). |
|
174 |
||
175 |
You will need to import the `studpath` module from `common` - this provides |
|
176 |
utilities for accessing student files. |
|
177 |
||
178 |
from common import studpath |
|
179 |
||
180 |
def handle(req): |
|
181 |
req.content_type = "text/html" |
|
182 |
req.write_html_head_foot = True |
|
183 |
||
184 |
(user, path) = studpath.url_to_local(req.path) |
|
185 |
req.write("<p>") |
|
186 |
try: |
|
187 |
req.sendfile(path) |
|
188 |
except IOError, msg: |
|
189 |
req.write("Error: %s" % msg) |
|
190 |
req.write("</p>") |
|
191 |
||
192 |
`studpath.url_to_local` gives you a path on the local file system which the |
|
193 |
file corresponds to. (It also gives you the name of the user or group who owns |
|
194 |
the file, though we don't use that here). |
|
195 |
||
196 |
**Important**: This simple example does not escape characters for HTML, so it |
|
197 |
will not display some files correctly, and could be vulnerable to JavaScript |
|
198 |
injections. In a real app, characters (at the very least, '<', '>' and '&') |
|
199 |
should be escaped correctly. |