<html>
<!--AD_DND-->
<head>
<title>Writing an ACS Module</title>
</head>

<body bgcolor=#ffffff text=#000000>
<h2>Writing an ACS Module</h2>

part of the <a href="index.html">ArsDigita Community System</a>
by <a href="mailto:tarik@arsdigita.com">Tarik Alatovic </a> 

<hr>

<h3>The Big Picture</h3>

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 <a href=http://photo.net/wtr/using-the-acs.html>
ArsDigita toolkit</a>, it must implement <i>common and reusable</i> 
functions. Examples of modules in the <a href=http://photo.net/wtr/using-the-acs.html>
ArsDigita toolkit</a> are <a href=news.html>News</a>, <a href=bboard.html>
Bulletin Board</a>, <a href=address-book.html>Address Book</a> and 
<a href=ecommerce.html>Ecommerce</a>.

<h3>Module Implementation Checklist</h3>
<ul>
<li>put the SQL data model in /doc/sql/module-name.sql
<li>put an HTML file explaining how to configure and use the module in /doc/module-name.html
<li>put the user scripts in /module-name/
<li>put the module administrator scripts in /module-name/admin/
<li>put the site-wide administrator scripts in /admin/module-name/
<li>put commonly called procedures in the private tcl directory (/web/yourdomain/tcl) as module-name-defs.tcl
<li>if your module results in content being posted to the site, write a procedure to interface to the <a href="new-stuff.html">new
stuff</a> system and put it in your defs.tcl file, along with some in-line code to add it to the ns_share'd variable <code>ad_new_stuff_module_list</code>
<li>write a procedure to interface to the /tcl/ad-user-contributions-summary.tcl system (similar to the new stuff system but for stuff you want to show up on the
/shared/community-member.tcl and /admin/users/one.tcl pages)
</ul>


<h3>Module Architecture</h3>

<ul>
<li><b>Directory Structure</b>
<p>
Let's take <a href=faq.html>faq module</a> (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.
<p>
<li><b>Data Model</b>
<p>
Data model for the module should reside in /doc/sql directory
(e.g. /doc/sql/faq.sql for faq module).  
<a href=http://photo.net/sql/data-modeling.html>Data modeling</a> 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).
<p> 
<li><b>Utility Functions</b>
<p>
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.tcl). 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 <a href=mailto:jsc@mit.edu>
jsc@mit.edu</a> and suggest that your function becomes a part of ArsDigita Toolkit
 utility functions.
</ul>

<h3>Module Documentation</h3>

Every module should have its documentation in HTML format in the /doc
directory (e.g. /doc/faq.html 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 <a href=/doc/chat.html>
Chat Module Documentation</a>.

<h3>Module Customization</h3>

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

<p>
<li><b>Module Level Customization</b>
<p>
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:


<code>
<pre>
[ns/server/yourservername/acs/download]
; root directory of the downloadable files
DownloadRoot=/web/photonet-dev/download/
</pre>
</code>

These parameters can be accessed from within the code using the
<code>ad_parameter</code> function.
<p>
<li><b>Instance Level Customization</b>
<p>
 
An ACS module is a collection of sql table definitions supported by
HTML interface.  An <i>instance</i> of a module uses this table definitions
and HTML interface to present a module functionality to the user. For
example, an <i>instance</i> of chat module is a chat room, an <i>instance</i>
of a bulletin board is a bulletin board for a certain topic and an
<i>instance</i> of faq module is a faq collection, such as, AOL Server FAQ or
Novice Photographers FAQ.

Note that not all modules support multiple <i>instances</i> (e.g.
eccomerce module has only one <i>instance</i>).  Modules supporting
multiple <i>instances</i> should have parameters kept in columns of
the table where module <i>instances</i> 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 <i>instance</i> 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
<i>instance</i> of the module and be stored in the database.
</ul>

<h3>Module Scoping</h3>

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
<i>instance</i> for each user
<p>
ArsDigita standard defines three scopes: public, group and user.
<ul>
<li>Public <i>instance</i> of module is associated with the whole installation and 
it serves all users (e.g. photo.net/news is public <i>instance</i> of news module
and it provides news items of interest to all users).
<li>Group <i>instance</i> 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.
<li>User <i>instance</i> 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.
</ul>

Notice that scoping makes sense only for the modules supporting 
multiple <i>instances</i>, since scoping concept applies only to the 
<i>instances</i> 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 <i>instances</i> 
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:
 
<code>
<pre>
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))
);
</pre>
</code>

Notice that faqs don't support user scope, so user_id is ommited and faq_scope_check
restricts scope to public and group only.

<h3>Module Integration</h3>  

If module supports multiple <i>instances</i> and scoping, you can decide to
implement ACS interface for associating module with groups and users
(i will refer to modules implementing this interface <i>embeddable</i>
modules). Examples of <i>embeddable</i> modules are faq, news and address
book module. <i>Embeddable</i> modules can be easily associated with groups
through admin user group pages in /admin/ug. 

There are two steps in making an <i>embeddable</i> module:
<ul>
<li>Register the module in <a href="/doc/sql/display-sql.tcl?url=/doc/sql/modules.sql">acs_modules table</a>.
    For example, news module registration is the following insert statement:
    <code>
    <pre>
    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)
    values
    ('news', 'News', '/news', '/news/admin', '/admin/news', 'system', 't', '/doc/news.html', '/doc/sql/news.sql');
    </pre>
    </code>

<li>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.tcl.  For example, you will use
    <code>ad_scope_header</code>, <code>ad_scope_footer</code> and
    <code>ad_scope_context_bar</code> instead of
    <code>ad_header</code>, <code>ad_footer</code> and
    <code>ad_context_bar</code>. ad-scope.tcl also defines a powerful
    security procedure <code>ad_scope_authorize</code>, 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.
</ul>

Finally, to see your <i>embeddable</i> 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 <i>embeddable</i> modules with user groups,
take a look at <a href=user-groups.html>User Groups Documentation</a>.

<hr>
<a href=mailto:tarik@arsdigita.com><address>tarik@arsdigita.com</address></a>
</body>
</html>