ArsDigita Archives

Writing an ACS Module

part of the ArsDigita Community System by Tarik Alatovic
Note: 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 ArsDigita toolkit, it must implement common and reusable functions. Examples of modules in the ArsDigita toolkit are News, Bulletin Board, Address Book and Ecommerce.

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 ad_new_stuff_module_list
  • 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)

Module Architecture

  • 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 and suggest that your function becomes a part of ArsDigita Toolkit utility functions.

Module Documentation

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.

Module Customization

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:

    ; root directory of the downloadable files
    These parameters can be accessed from within the code using the ad_parameter function.

  • 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.

Module Scoping

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 instance 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. 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 multiple instances, since scoping concept applies only to the instances 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
        scope		varchar(20),
        -- 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.

Module Integration

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 module:
  • 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_header, ad_scope_footer and ad_scope_context_bar instead of ad_header, ad_footer and ad_context_bar. ad-scope also defines a powerful security procedure ad_scope_authorize, which 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 embedding interface.
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.