In Zadig, objects can be contained in other objects. For example,
you can have page
/mypage/hobbies, and it can contain
/mypage/hobbies/django. There is no separate “folder” object;
instead, any object can act as a container of other objects, in
addition to having its own content. If a page has no content, then it
shows an index of its contained pages (like Plone does for folders
when no page has been set as the folder’s default view).
Why don’t we specifically use folders, as Plone does? The reason is
that it conflicts with the URL scheme, where any resource can have
subresources. Because of this conflict, it is difficult for users to
understand the notion of a folder and a page. For example, a user
would normally create a page
/mypage/hobbies, and then
typically ask: how do I now create
Plone reply (hobbies must be made a folder, the current hobbies page
must become a page inside the folder, and must be set as default, and
a django [page or folder?] must be created inside the folder) is
complicated and unintuitive. It also poses a practical problem: the
user needs to decide a priori whether the page he is about to create
will contain subpages or whether it is likely to remain a single page.
On the other hand, other web management systems, like MoinMoin,
have pages and subpages, and each page can have attachments. This
is also unintuitive, because, for example, an image does not
always belong to a “page”; it rather belongs to a folder.
/myfolder/myimage is much better and intuitive than
Also note that, in Moin,
/mypage/mysubpage is a completely
different and unrelated page to
/mypage: it’s just a name that
contains a slash. But in Zadig,
/mypage/mysubpage is a page
that is contained in
/mypage, as if the latter were a folder.
Moin is even simpler and maps even better on the URL scheme, but
Zadig makes property inheritance (e.g. permissions and skins) much
simpler and foolproof: any object simply inherits the properties
of its container objects.
Entries and vobjects¶
The end user uses the word page (and the more general object) with two meanings, owing to the fact that we do automatic versioning like that of a wiki. When we say “delete this page”, we mean delete an entire sequence of pages; when we talk about the content of a page, we mean the content of a specific page version.
When we need to distinguish between the two (and this is when we are writing code), we use the word entry for a sequence of versioned objects, and the word vobject for each specific version of an entry. An entry can be a page entry, an image entry, etc., and a vobject can be a vpage, a vimage, etc. Developers should be very clear about that. The end users should not be bothered, and the word object should be used for everything.
The user can select the preferred language from the language selector, which on the default template is on the top left, below login. However, the effective language is determined by the object being viewed. If the object being viewed is in a specific language (e.g. it is a page in Greek), then the effective language is the object’s language (Greek in our case); but if the object has no associated language (e.g. it is an image), then the effective language is the preferred language.
The translatable strings (i.e. the elements of the user interface) are shown in the effective language, except for messages that intend to notify the user about the languages chosen and why what he sees is in another language (this is so that the user understands at least that message).
When two or more objects contain the same content in different languages, we say that they form one multilingual group. In that case, when the user changes the preferred language, the system transfers him to the alternative page (which, thus, also becomes the effective language). When an object exists in the preferred language and in another language, the navigation shows only the preferred language; but if an object exists in a language which is not the preferred language and does not have an equivalent in the preferred language, the navigation shows it anyway.
There are two ways to “delete” an object: one is to mark it as deleted. In this case, the entry history is retained, and the “deletion” is an event recorded in the entry history. Marking an entry as deleted is creating a new vobject. Since the entry exists, another entry with the same name cannot be created.
The second way to delete is to remove the entry, in which case the entry, including the history, is permanently and irrevocably deleted.
Not implemented yet
Removing the entry is not yet implemented in the user interface.
There are several things you can do with an entry or vobject: view it, edit it, view its contents or its history, delete it, change its state, and so on. We call these operations actions: there is the “show action”, the “edit action”, the “contents action”, the “change state action”, and so on.
The show action is the normal mode of viewing an entry; for an image, for example, the show action results in a “image/jpeg” or similar response that contains binary data. The info action results in a page that includes information about the image, buttons for initiating editing, and so on, and is intended mainly for users who have permissions to modify the image. For some object types, such as pages, the show action and the info action may be identical. http://localhost/myobject/ produces the show action for myobject, whereas http://localhost/myobject/__info__/ provides the info action.
In GET requests, the action is specified by an item in the URL path which has a prefix and a suffix of two underscores; for example, http://localhost/mypage/__edit__/ is a request to edit mypage. If there is no such item in the URL, then the request is to view the entry, that is, for the show action. In POST requests, the action is specified by the action parameter.
Permissions and workflows¶
There are users, and each user can belong to one or more groups. When someone has not logged on then we say that they are the anonymous user. Each user and group has certain permissions on each object. There are five kinds of permissions: view, edit, admin, search, and delete. Someone with edit permission, besides editing the entry, can also mark the entry as deleted, view the list of all subobjects regardless of whether they have any permission on them, and add subobjects to the entry; you also need edit permission to view the entry history; with view permission you can only view the current entry version (the last vobject). Someone with admin permissions can modify the object’s permissions and state. Someone with delete permission can remove the entry, recursively including its subentries (irrespective of their permissions). Someone with search permission can see the entry in listings; e.g. the anonymous user has view permission on a public draft, but not search permission; you can view it if you know its URL, but it won’t show in indexes or searches. Each entry has an owner, and the owner has all permissions on the object.
Why do you need edit permission to view the entry history? This can be useful if old versions contain confidential information. There are alternative ways of doing it, such as adding a view_history permission, or allowing individual vobjects to have separate view permissions. This, however, adds complication. Zadig is not primarily a wiki, and the possibility for unprivileged users to view the history is not considered essential. Therefore, we chose to keep it simple for the moment, and leave it for the future to find a way around this problem.
At a given time, each entry is in a state. A state is a collection of permissions. For example, these are common states:
- Logged on users can view.
- Public draft
- Anonymous user can view.
- Anonymous user can view and search.
A workflow is a collection of states and state-transition rules. A state-transition rule is a many-to-one relationship between states: it shows which are the possible states that follow a given state. Each state-transition rule can be followed by a specific user or group. Usually the owner has permissions to follow any state-transition rule; but this is not true in all workflows. For example, in some blogs, the blog editor, and not the post author, has permission to publish the post.
In the default installation, two states are created, private and published, two state-transition rules (from private to published and vice-versa), and one workflow named “Simple publication workflow” containing all those. The private state has the view and search permissions on logged on users; the published state has the view and search permissions on the anonymous user. There are no other permissions defined, which means that only the owner can edit, admin and delete.
Not implemented yet
Currently only the owner and the state can be changed through the user interface. The user interface for directly assigning permissions to users has not been developed yet.
Applets and portlets¶
An applet is essentially a Django custom template tag. The
breadcrumbs, for example, is an applet; when you include
breadcrumbs %} in a template, this is replaced by the breadcrumbs.
The reason we don’t call them simply tags is that applets also have
standard ways of storing configuration options; they can have
configuration options per entry, or global. But don’t worry if you
don’t understand this yet; just think of “applet” as a synonym for a
custom template tag.
A portlet is a special kind of applet. There are two things that are special about portlets: the first one is that they have a certain kind of look: they have a title and a body, and the body consists of items (some portlets, like navigation, have only one item in the body). The template tag of the portlet renders the portlet in a very specific manner:
<dl class="portlet"> <dt>Portlet title</dt> <dd class="odd"> First item title <span class="details">Details</span> </dd> <dd class="even">Second item</dd> ... </dl>
Because portlets conform to that specification, the CSS style sheet can define a common look for all portlets. (Some portlets might contain only one item, and others might not contain items consisting of title and details; but all portlets are DL elements containing a DT and at least one DD.)
The second thing that is special about portlets is that they are registered: Zadig knows what portlets are available; this makes possible to have user interface where the user can select portlets from a list, instead of having to specify them in a Django template.
Not implemented yet
The portlets for recent changes, pending events, news, and calendar have not been implemented yet. In addition, no functionality that uses registered portlets has been implemented. I think it might be nice if the left and right panes, where portlets are shown, were applets themselves.