blob: 0845eeb66133c3a283bcbfbf5275ee154b48e9e8 [file] [log] [blame]
Platform Tour
=================
Introduction
---------------
Allura is implemented as a collection of tool applications on top of a
robust and open platform. Some of the services provided by the platform include:
- Indexing and search
- Authentication and Authorization
- Email integration (every tool application gets its own email address)
- Asynchronous processing with background tasks and events
- `Markdown <http://daringfireball.net/projects/markdown/>`_ markup formatting
- Simple autolinking between different artifacts in the forge
- Attachment handling
- Tool administration
Tools, on the other hand, provide the actual user interface and logic to
manipulate forge artifacts. Some of the tools currently impemented include:
admin
This tool is installed in all projects, and allows the administration of the
project's tools, authentication, and authorization
Git, Hg, SVN
These tools allow you to host a version control system in Allura.
They also provides the ability to "fork" Git and Hg repos in order to
provide your own extensions.
Wiki
This tool provides a basic wiki with support for comments, attachments, and
notifications.
Tracker
This tool provides an extensible ticketing system for tracking feature
requests, defects, or support requests.
Discussion
This tool provides a forum interface with full email integration as well.
The forum also handles attachments to posts either via the web interface or via email.
The Context Object
---------------------------------------------------
The Pylons "context" object `c` has several properties which are automatically
set for each request:
project
The current project
app
The current tool application object.
user
The current user
Allura platform provides the following functions to manage the context object,
if you need to change the context for some situation:
.. function:: allura.lib.helpers.push_config(obj, **kw)
:noindex:
Context manager (used with the `with` statement) used to temporarily set the
attributes of a particular object, resetting them to their previous values
at the end of the `with` block. Used most frequently with the context object
`c`::
c.project = some_project
with push_config(c, project=other_project):
...
# code in this block will have c.project == other_project
# code here will have c.project == some_project
.. function:: allura.lib.helpers.set_context(project_id, mount_point=None, app_config_id=None)
:noindex:
Set the context object `c` according to the given `project_id` and optionally either a
`mount_point`, an `app_config_id`. `c.project` is set to the corresponding
project object. If the mount_point or app_config_id is
specified, then `c.app` will be set to the corresponding tool application
object.
Artifacts
-------------
We've mentioned artifacts a couple of times now without definition. An artifact,
as used in Allura, is some object that a tool needs to store in the
forge. The platform provides facilities for controlling access to individual
artifacts if that's what the tool designer favors. For instance, the Discussion
tool allows a user to edit or delete their own posts, but not to edit or delete
others (unless the user has the 'moderate' permission on the forum itself).
Some examples of artifacts in the current tools:
- Discussion: Forum, Thread, Post, Attachment
- Wiki: Page, Comment, Attachment
- Tracker: Ticket, Comment, Attachment
- SCM: Repository, Commit, Patch
In order to implement your own artifact, you should override at least a few of
the methods of the `allura.model.artifact.Artifact` class::
from ming.orm.property import FieldProperty
from allura.model import Artifact
class NewArtifact(Artifact):
class __mongometa__:
name='my_new_artifact' # collection where this artifact is stored
type_s = 'My Artifact' # 'type' of the artifact used in search results
# Add your own properties here (beyond those provided by Artifact)
shortname = FieldProperty(str)
def url(self):
'Each artifact should have its own URL '
return self.app.url + self.shortname + '/'
def index(self):
'Return the fields you want indexed on this artifact'
result = Artifact.index(self)
result.update(type_s=self.type_s,
name_s=self.shortname,
text=self.shortname)
return result
def shorthand_id(self):
'Used in the generation of short links like [my_artifact]'
return self.shortname
Platform services provided for artifacts
---------------------------------------------------
Whenever you create, modify, or delete an artifact, the platform does a couple of
things for you:
- The artifact is added to the index and will appear in searches
- A shortlink is generated for the artifact (e.g. [MyWikiPage] or [#151]). This allows you
to reference the artifact from other artifacts. Whenever the commit message
is displayed in the SCM tool, any references to `[#151]` will be
automatically linked to that Ticket's page.
Shortlinks work only within a project hierarchy (in order to link to some other project's
page, you'll have to use the full URL). Sometimes, a shortlink may need to be
differentiated based on its location in a subproject or in one of many tools of
the same type within a project. In order to do this, shortlinks may be prefixed
by either the tool mount point or a project ID and tool mount point.
For instance, suppose we have an ticket tracker called 'features' and one called 'bugs'.
They both have many tickets in them. To distinguish, use the tracker mount point
within the reference. For example [features:#3] or [bugs:#3]
Asynchronous Processing
-----------------------------------------
Much of the actual functionality of Allura comes from code that runs
*outside* the context of a web request, in the `taskd` server (invoked by
running `paster taskd development.ini`. Asynchronous processing is performed
by two types of functions, *tasks* and *events*, differentiated as follows:
Task
Tasks are module-level global functions. They are annotated with the `@task`
decorator and are invoked with the `.post` method. For instance, to schedule
a task `foobar` to execute in the `taskd` context, you would write::
@task
def foobar(a,b,c=5): ...
foobar.post(9,1,c=15)
Event
Events are intended for "fan-out" types of events. Events have a string
name, and are "listened" for by using the `@event_handler` decorator. The
`g.post_event()` helper is provided to run the event handlers for a
particular event in the `taskd` context. Multiple event handlers can be
registered for each event::
@event_handler('event_name')
def handler1(topic, *args, **kwargs): ...
@event_handler('event_name')
def handler2(topic, *args, **kwargs): ...
g.post_event('event_name', 1,2,3, a=5)
Email Integration
-----------------------------------------
The Allura platform provides easy-to-use email integration. Forge email addresses
are of the form
<topic>@<mount_point>[.<subproject>]*.<subproject>.projects.sourceforge.net.
When a message is received on such an email address, the address is parsed and
the sending user is identified (if possible). Based on the parsed address, the
pylons context attributes `c.project` and `c.app` are set, and the application is
queried to determine whether the identified user has authority to send an email
to the given app/topic combination by calling `c.app.has_access(user, topic)`.
If the user has access, the message is decomposed into its component parts (if a
multipart MIME-encoded message) and `c.app.handle_message(topic, message)` is
called for each part with the following components to the `msg` dict:
headers
The actual headers parsed from the body of the message
message_id
The `Message-ID` header (which should be universally
unique and is
generated by the email client), used for determining which messages are replies
to which other messages
in_reply_to
The `In-Reply-To` header, used for determining which messages are replies to
which other messages
references
The `References` header, used for determining which messages refer to
which other messages
filename
Optional, if the part is an attachment with a filename, this will be populated
content_type
The MIME content_type of the message part
payload
The actual content of the message part
The Allura platform also provides full support for *sending* email without
worrying about the specifics of SMTP or sendmail handling. In order to send an
email, simply post a task for `allura.tasks.mail_tasks.sendmail` with the
following arguments:
fromaddr
Return address on the message (usually the topic@tool_name that generated
it)
destinations
List of email addresses and/or :class:`bson.ObjectId` s for
:class:`allura.model.auth.User` objects
text
Markdown-formatted body of the message (If the user has requested html or
combined text+html messages in their preferences, the Markdown will be so
rendered. Otherwise a plain text message will be sent.)
reply_to
Address to which replies should be sent
subject
Subject of the message
message_id
Value to put in the `Message-ID` header (the `_id` field of a
:class:`allura.model.artifact.Message` is suitable for this)
in_reply_to (optional)
Value to put in the `In-Reply-To` header (the `parent_id` field of a
:class:`allura.model.artifact.Message` is suitable for this)