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