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