Writing an ACS Module
part of the ArsDigita Community System
by Tarik Alatovic
As of ACS 3.3, this document is obsoleted
by the ACS Package Manager
The Big Picture
An ACS module is a self-contained subsystem delivering a service
visible to the end-user. From a programmer's point of view, it is a
collection of sql table definitions supported by HTML interface
generated by a scripting language such as Tcl. In order for a module
to become a part of
, it must implement common and reusable
functions. Examples of modules in the
, Address Book
Module Implementation Checklist
- put the SQL data model in /doc/sql/module-name.sql
- put an HTML file explaining how to configure and use the module in /doc/module-name
- put the user scripts in /module-name/
- put the module administrator scripts in /module-name/admin/
- put the site-wide administrator scripts in /admin/module-name/
- put commonly called procedures in the private tcl directory (/web/yourdomain/tcl) as module-name-defs
- if your module results in content being posted to the site, write a procedure to interface to the new
stuff system and put it in your defs file, along with some in-line code to add it to the ns_share'd variable
- write a procedure to interface to the /tcl/ad-user-contributions-summary system (similar to the new stuff system but for stuff you want to show up on the
/shared/community-member and /admin/users/one pages)
- Directory Structure
Let's take faq module (Frequently Asked
Questions) as an example. Its files are stored in three directories:
/faq, /faq/admin and /admin/faq. We need these three separate
directories in order to support three levels of access to the module
data: public, module administrator and site-wide administrator. Pages
in /faq are public pages showing questions and answers. Pages in
/faq/admin are used by module admistrator to add, edit or remove
faq questions and answers. Pages in /admin/faq are provided for
site-wide administrator who may need to add or delete whole faqs,
collect faq module statistics (e.g, how many people used the faq module in
the previous month) and be able to do other operations refering to the
whole faq module and not just to an instance of it.
- Data Model
Data model for the module should reside in /doc/sql directory
(e.g. /doc/sql/faq.sql for faq module).
Data modeling is the
hardest and most important part of any module. If you get the data
model wrong, your module might not do what users need, it might be
unreliable, and it might fill up the database with garbage. In order
to support module customization and module scoping, your data model
needs to conform with ACS standards (see Module Customization and
Scoping sections bellow for the changes you need to make to your
data model). All tables, sequences, functions, triggers, views, etc.
should be prefixed with the module name or its abbreviation in order
to avoid naming conflicts (e.g. faq_q_and_a for faq questions and answers).
- Utility Functions
All the Tcl functions you write for your module should go to your
private tcl directory (e.g. faq module has its Tcl functions stored
in faq-defs). Procedures should be prefixed with the module name
or its abbreviation (e.g. faq_maintainer_p), in order to avoid naming
conflicts. If you think you wrote a generic Tcl function that
everybody can use, then you can send e-mail to
email@example.com and suggest that your function becomes a part of ArsDigita Toolkit
Every module should have its documentation in HTML format in the /doc
directory (e.g. /doc/faq for faq module). This documentation is
primarily intended for programmers and should be brief and technical
as necesssary. It should list the features that this module provides,
explain purpose of the module, possible uses and discuss design decisions.
For good example of documentation, take a look at
Chat Module Documentation
A good, reusable module will be used in many ArsDigita installations
and it may be required to perform a slightly different funcionality
then the default one. A way to customize a module, so that it can be
configured to support several different modes of operation is through
usage of parameters.
There are two levels at which the module can be customized: module and instance level.
- Module Level Customization
Module customization includes parameters that are used by the whole module.
These parameters should be put in configuration file your_server_name.ini
in the parameters directory.
For download module, parameters in configuration file look like this:
These parameters can be accessed from within the code using the
; root directory of the downloadable files
- Instance Level Customization
An ACS module is a collection of sql table definitions supported by
HTML interface. An instance of a module uses this table definitions
and HTML interface to present a module functionality to the user. For
example, an instance of chat module is a chat room, an instance
of a bulletin board is a bulletin board for a certain topic and an
instance of faq module is a faq collection, such as, AOL Server FAQ or
Novice Photographers FAQ.
Note that not all modules support multiple instances (e.g.
eccomerce module has only one instance). Modules supporting
multiple instances should have parameters kept in columns of
the table where module instances are defined. For chat module,
instances are chat rooms and parameters are columns in the chat_rooms
table. For example, parameter that determines whether chat room should be
moderated is kept in the moderated_p column of the chat_rooms table.
Parameter moderated_p configures an instance of the chat module
and not the whole chat module. When using parameters, you should
make decision whether parameter should be associated with module and
be put in parameters file or associated with a particular
instance of the module and be stored in the database.
Standards for module scoping have been introduced in ArsDigita toolkit
release 3.0. Before this release, very few modules supported
scoping. For example, the address book module provided an address book
for each user
ArsDigita standard defines three scopes: public, group and user.
- Public instance of module is associated with the whole installation and
it serves all users (e.g. photo.net/news is public instance of news module
and it provides news items of interest to all users).
- Group instance of module is associated with particular group. For example,
news section of Novice Photogaphers provides news of interest for novice photographers
only. News items are administered by the Novice Photographers group administrator.
- User instance of module is associated with particular user. For example,
Philip owns his address book and he is the only person who has the right to
view, add and edit records in that address book.
Notice that scoping makes sense only for the modules supporting
, since scoping concept applies only to the
of the module and not the module itself.
In order to support scoping, your data model should include columns:
scope, user_id and group_id in the table where module instances
are defined. If user scope is not supported, then you don't need to
have user_id column, and if group scope is not supported then you don't
need to have group_id column. Here is the example of the faq data model,
which supports scoping:
create table faqs (
faq_id integer primary key,
-- name of the FAQ.
faq_name varchar(250) not null,
-- group the viewing may be restricted to
group_id integer references user_groups,
-- permissions can be expanded to be more complex later
-- insure consistant state
constraint faq_scope_check check ((scope='group' and group_id is not null)
or (scope='public' and group_id null))
Notice that faqs don't support user scope, so user_id is ommited and faq_scope_check
restricts scope to public and group only.
If module supports multiple instances
and scoping, you can decide to
implement ACS interface for associating module with groups and users
(i will refer to modules implementing this interface embeddable
modules). Examples of embeddable
modules are faq, news and address
book module. Embeddable
modules can be easily associated with groups
through admin user group pages in /admin/ug.
There are two steps in making an embeddable
- Register the module in acs_modules table.
For example, news module registration is the following insert statement:
insert into acs_modules
(module_key, pretty_name, public_directory, admin_directory, site_wide_admin_directory, module_type, supports_scoping_p, documentation_url, data_model_url)
('news', 'News', '/news', '/news/admin', '/admin/news', 'system', 't', '/doc/news', '/doc/sql/news.sql');
- Handle all scopes that this module supports in the Tcl files. You
will need to use calls to database (select, insert, delete)
appropriate for each scope. Display elements, such as headers,
footers and context bars should also be customized based on the
scope. This will be an easy process if you use utility functions
defined in ad-scope. For example, you will use
ad_scope_context_bar instead of
ad_context_bar. ad-scope also defines a powerful
authorizes a user for specified action and specified scope. If you
don't understand this scope stuff, take a look at files in /faq
and /faq/admin directories for examples of files implementing
Finally, to see your embeddable
module work, you should create a test
group and associate module with that group through files in /admin/ug.
For Novice Photographers group, faq module public pages will be
located at /groups/novice-photographers/faq and faq module
administration pages at /groups/admin/novice-photographers/faq.
For more information on associating embeddable
modules with user groups,
take a look at User Groups Documentation