0.00%
Search · Index

Weblog Page

Filtered by date 2008-02-17, 51 - 60 of 111 Postings (all, summary)

Groups Design

Created by Gustaf Neumann, last modified by Gustaf Neumann 17 Feb 2008, at 07:08 AM

By Rafael H. Schloming and Mark Thomas

OpenACS docs are written by the named authors, and may be edited by OpenACS documentation staff.

Almost all database-backed websites have users, and need to model the grouping of users. The OpenACS 4 Parties and Groups system is intended to provide the flexibility needed to model complex real-world organizational structures, particularly to support powerful subsite services; that is, where one OpenACS installation can support what appears to the user as distinct web services for different user communities.

The primary limitation of the OpenACS 3.x user group system is that it restricts the application developer to representing a "flat group" that contains only users: The user_groups table may contain the group_id of a parent group, but parent-child relationship support is limited because it only allows one kind of relationship between groups to be represented. Moreover, the Oracle database's limited support for tree-like structures makes the queries over these relationships expensive.

In addition, the Module Scoping design in OpenACS 3.0 introduced a party abstraction - a thing that is a person or a group of people - though not in the form of an explicit table. Rather, the triple of scope, user_id, and group_id columns was used to identify the party. One disadvantage of this design convention is that it increases a data model's complexity by requiring the programmer to:

  • add these three columns to each "scoped" table

  • define a multi-column check constraint to protect against data corruption (e.g., a row with a scope value of "group" but a null group_id)

  • perform extra checks in Tcl and PL/SQL functions and procedures to check both the user_id and group_id values

...

The core of the Group Systems data model is quite simple, but it was designed in the hopes of modeling "real world" organizations which can be complex graph structures. The Groups System only considers groups that can be modeled using directed acyclic graphs, but queries over these structures are still complex enough to slow the system down. Since almost every page will have at least one membership check, a number of triggers, views, and auxiliary tables have been created in the hopes of increasing performance. To keep the triggers simple and the number of triggers small, the data model disallows updates on the membership and composition tables, only inserts and deletes are permitted.

The data model has tried to balance the need to model actual organizations without making the system too complex or too slow. The added triggers, views, and tables and will increase storage requirements and the insert and delete times in an effort to speed access time. The limited flexibility (no updates on membership) trades against the complexity of the code.

The Group System data model consists of the following tables:

parties

The set of all defined parties: any person, user, or group must have a corresponding row in this table.

persons

The set of all defined persons. To allow easy sorting of persons, the name requirement 30.10 is met by splitting the person's name into two columns: first_names and last_name.

users

The set of all registered users; this table includes information about the user's email address and the user's visits to the site.

user_preferences

Preferences for the user.

groups

The set of all defined groups.

group_types

When a new type of group is created, this table holds additional knowledge level attributes for the group and its subtypes.

membership_rels

The set of direct membership relationships between a group and a party.

group_member_index

A mapping of a party P to the groups {Gi }the party is a member of; this mapping includes the type of relationship by including the appropriaterel_id from the membership_rels table.

composition_rels

The set of direct component relationships between a group and another group.

group_component_index

A mapping of a group Gto the set of groups {Gi } that G is a component of; this mapping includes the type of relationship by including the appropriaterel_id from the composition_rels table.

New groups are created through the group.new constructor. When a specialized type of group is required, the group type can be extended by an application developer. Membership constraints can be specified at creation time by passing a parent group to the constructor.

The membership_rels and composition_rels tables indicate a group's direct members and direct components; these tables do not provide a record of the members or components that are in the group by virtue of being a member or component of one of the group's component groups. Site pages will query group membership often, but the network of component groups can become a very complex directed acyclic graph and traversing this graph for every query will quickly degrade performance. To make membership queries responsive, the data model includes triggers (described in the next paragraph) which watch for changes in membership or composition and update tables that maintain the group party mappings, i.e., group_member_index and group_component_index. One can think of these tables as a manually maintained index.

The following triggers keep the group_*_index tables up to date:

membership_rels_in_tr

Is executed when a new group/member relationship is created (an insert on membership_rels)

membership_rels_del_tr

Is executed when a group/member relationship is deleted (a delete on membership_rels)

composition_rels_in_tr

Is executed when a new group/component relationship is created (an insert on composition_rels)

composition_rels_del_tr

Is executed when a group/component relationship is deleted (a delete on composition_rels)

The data model provides the following views onto the group_member_index and group_component_index tables. No code outside of Groups System should modify the group_*_index tables.

group_member_map

A mapping of a party to the groups the party is a member of; this mapping includes the type of relationship by including the appropriaterel_id from the membership_rels table.

group_approved_member_map

A mapping of a party to the groups the party is an approved member of (member_state is 'approved'); this mapping includes the type of relationship by including the appropriaterel_id from the membership_rels table.

group_distinct_member_map

A person may appear in the group member map multiple times, for example, by being a member of two different groups that are both components of a third group. This view is strictly a mapping of approved members to groups.

group_component_map

A mapping of a group Gto the set of groups {Gi } group G is a component of; this mapping includes the type of relationship by including the appropriaterel_id from the composition_rels table.

party_member_map

A mapping of a party P to the set of parties {Pi } party P is a member of.

party_approved_member_map

A mapping of a party P to the set of parties {Pi } party P is an approved member of.

The API consists of tables and views and PL/SQL functions.

The group_types table is used to create new types of groups.

The group_member_map, group_approved_member_map, group_distinct_member_map, group_component_map, party_member_map, and party_approved_member_map views are used to query group membership and composition.

Person

person.new creates a new person and returns the person_id. The function must be given the full name of the person in two pieces: first_names and last_name. All other fields are optional and default to null except for object_type which defaults to person and creation_date which defaults to sysdate. The interface for this function is:

function person.new (
  person_id          persons.person_id%TYPE,
  object_type        acs_objects.object_type%TYPE,
  creation_date      acs_objects.creation_date%TYPE,
  creation_user      acs_objects.creation_user%TYPE,
  creation_ip        acs_objects.creation_ip%TYPE,
  email              parties.email%TYPE,
  url                parties.url%TYPE,
  first_names        persons.first_names%TYPE,
  last_name          persons.last_name%TYPE
) return persons.person_id%TYPE;

person.delete deletes the person whose person_id is passed to it. The interface for this procedure is:

procedure person.delete (
  person_id     persons.person_id%TYPE
);

person.name returns the name of the person whose person_id is passed to it. The interface for this function is:

function person.name (
  person_id     persons.person_id%TYPE
) return varchar;

User

acs_user.new creates a new user and returns the user_id. The function must be given the user's email address and the full name of the user in two pieces: first_names and last_name. All other fields are optional. The interface for this function is:

function acs_user.new (
  user_id            users.user_id%TYPE,
  object_type        acs_objects.object_type%TYPE,
  creation_date      acs_objects.creation_date%TYPE,
  creation_user      acs_objects.creation_user%TYPE,
  creation_ip        acs_objects.creation_ip%TYPE,
  email              parties.email%TYPE,
  url                parties.url%TYPE,
  first_names        persons.first_names%TYPE,
  last_name          persons.last_name%TYPE
  password           users.password%TYPE,
  salt               users.salt%TYPE,
  password_question  users.password_question%TYPE,
  password_answer    users.password_answer%TYPE,
  screen_name        users.screen_name%TYPE,
  email_verified_p   users.email_verified_p%TYPE
) return users.user_id%TYPE;

acs_user.delete deletes the user whose user_id is passed to it. The interface for this procedure is:

procedure acs_user.delete (
  user_id       users.user_id%TYPE
);

acs_user.receives_alerts_p returns 't' if the user should receive email alerts and 'f' otherwise. The interface for this function is:

function acs_user.receives_alerts_p (
  user_id       users.user_id%TYPE
) return varchar;

Use the procedures acs_user.approve_email and acs_user.unapprove_email to specify whether the user's email address is valid. The interface for these procedures are:

procedure acs_user.approve_email (
  user_id       users.user_id%TYPE
);

procedure acs_user.unapprove_email (
  user_id       users.user_id%TYPE
);

Group

acs_group.new creates a new group and returns the group_id. All fields are optional and default to null except for object_type which defaults to 'group', creation_date which defaults to sysdate, and group_name which is required. The interface for this function is:

function acs_group.new (
  group_id           groups.group_id%TYPE,
  object_type        acs_objects.object_type%TYPE,
  creation_date      acs_objects.creation_date%TYPE,
  creation_user      acs_objects.creation_user%TYPE,
  creation_ip        acs_objects.creation_ip%TYPE,
  email              parties.email%TYPE,
  url                parties.url%TYPE,
  group_name         groups.group_name%TYPE
) return groups.group_id%TYPE;

acs_group.name returns the name of the group whose group_id is passed to it. The interface for this function is:

function acs_group.name (
  group_id      groups.group_id%TYPE
) return varchar;

acs_group.member_p returns 't' if the specified party is a member of the specified group. Returns 'f' otherwise. The interface for this function is:

function acs_group.member_p (
  group_id      groups.group_id%TYPE,
  party_id      parties.party_id%TYPE,
) return char;

Membership Relationship

membership_rel.new creates a new membership relationship type between two parties and returns the relationship type's rel_id. All fields are optional and default to null except for rel_type which defaults to membership_rel. The interface for this function is:

function membership_rel.new (
  rel_id             membership_rels.rel_id%TYPE,
  rel_type           acs_rels.rel_type%TYPE,
  object_id_one      acs_rels.object_id_one%TYPE,
  object_id_two      acs_rels.object_id_two%TYPE,
  member_state       membership_rels.member_state%TYPE,
  creation_user      acs_objects.creation_user%TYPE,
  creation_ip        acs_objects.creation_ip%TYPE,
) return membership_rels.rel_id%TYPE;

membership_rel.ban sets the member_state of the given rel_id to 'banned'. The interface for this procedure is:

procedure membership_rel.ban (
  rel_id           membership_rels.rel_id%TYPE
);

membership_rel.approve sets the member_state of the given rel_id to 'approved'. The interface for this procedure is:

procedure membership_rel.approve (
  rel_id           membership_rels.rel_id%TYPE
);

membership_rel.reject sets the member_state of the given rel_id to 'rejected. The interface for this procedure is:

procedure membership_rel.reject (
  rel_id           membership_rels.rel_id%TYPE
);

membership_rel.unapprove sets the member_state of the given rel_id to an empty string ''. The interface for this procedure is:

procedure membership_rel.unapprove (
  rel_id           membership_rels.rel_id%TYPE
);

membership_rel.deleted sets the member_state of the given rel_id to 'deleted'. The interface for this procedure is:

procedure membership_rel.deleted (
  rel_id           membership_rels.rel_id%TYPE
);

membership_rel.delete deletes the given rel_id. The interface for this procedure is:

procedure membership_rel.delete (
  rel_id           membership_rels.rel_id%TYPE
);

Composition Relationship

composition_rel.new creates a new composition relationship type and returns the relationship's rel_id. All fields are optional and default to null except for rel_type which defaults to composition_rel. The interface for this function is:

function membership_rel.new (
  rel_id             composition_rels.rel_id%TYPE,
  rel_type           acs_rels.rel_type%TYPE,
  object_id_one      acs_rels.object_id_one%TYPE,
  object_id_two      acs_rels.object_id_two%TYPE,
  creation_user      acs_objects.creation_user%TYPE,
  creation_ip        acs_objects.creation_ip%TYPE,
) return composition_rels.rel_id%TYPE;

composition_rel.delete deletes the given rel_id. The interface for this procedure is:

procedure membership_rel.delete (
  rel_id           composition_rels.rel_id%TYPE
);

Describe the admin pages.

...

...

...

System creator

Rafael H. Schloming

System owner

Rafael H. Schloming

Documentation author

Mark Thomas

Document Revision # Action Taken, Notes When? By Whom?
0.1 Creation 08/22/2000 Rafael H. Schloming
0.2 Initial Revision 08/30/2000 Mark Thomas
0.3 Additional revisions; tried to clarify membership/compostion 09/08/2000 Mark Thomas

OpenACS Packages

Created by Gustaf Neumann, last modified by Gustaf Neumann 17 Feb 2008, at 07:08 AM

By Pete Su and Bryan Quinn

OpenACS docs are written by the named authors, and may be edited by OpenACS documentation staff.

This document is a guide on how to write a software package for OpenACS. OpenACS packages are installed and maintained with the OpenACS Package Manager (APM) which is part of the acs-admin package. This document presents reasons for packaging software, conventions for the file system and naming that must be followed, and step by step instructions for creating a new package for the "Notes" example package.

Here is how an OpenACS 5 server is laid out starting from the Server root (ROOT):

Figure11.1.Server file layout diagram

ROOT/
    bin/
        Various executables and scripts for server maintanence.
    content-repository-content-files/
        content repository content stored in the filesystem.
    etc/
        Installation scripts and configuration files.
    packages/
        acs-admin/
        acs-api-browser/
        ... many many more...
        workflow/
    log/
        Server error and access logs
    tcl/
        bootstrap code
    www/
        Pages not in packages (static content, customized pages)

Each package encapsulates all of its data model, library code, logic, adminstration pages and user pages in a single part of the file tree. This means developers can track down everything that is related to a particular package without hunting all over the file system. Encapsulating everything about a package in one place also makes it much easier to distribute packages independently from the OpenACS Core.

In order to make this work, we need a system that keeps track of the packages that have been installed in the server, where those packages have been installed, and a standard way to map URLs that a client sends to our server to the right page in the appropriate package. While we're at it, this tool should also automate package installation, dependency checking, upgrades, and package removal. In OpenACS 5, this tool is called the APM.

To illustrate the general structure of a package, let's see what the package for the "notes" application should look like.

Figure11.2.Package file layout diagram

ROOT/
  +-- packages/    APM Root
        |
        +-- notes/  Package Root
        |     |
        |     +-- notes.info                              Package Specification File
        |     +-- sql/
        |     |     |
        |     |     +-- oracle/
        |     |     |        |
        |     |     |        +-- notes-create.sql         Data Model Creation Script for Oracle
        |     |     |        +-- notes-drop.sql           Data Model Drop Script
        |     |     |        +-- *.sql                    Data Model Files
        |     |     |        +-- upgrade/
        |     |     |            +-- upgrade-4.1-4.5.sql  Data Model Upgrade Scripts
        |     |     +-- postgresql/
        |     |     |        |
        |     |     |        +-- notes-create.sql         Data Model Creation Script for PostgreSQL
        |     |     |        +-- notes-drop.sql           Data Model Drop Script
        |     |     |        +-- *.sql                    Data Model Files
        |     |     |        +-- upgrade/
        |     |     |            +-- upgrade-4.1-4.5.sql  Data Model Upgrade Scripts
        |     +-- tcl/
        |     |     |
        |     |     +-- notes-procs.tcl                   Tcl Library
        |     |     +-- notes-procs.xql                   SQL92 Queries for notes-procs.tcl
        |     |     +-- notes-procs-oracle.xql            Oracle-specific queries for notes-procs.tcl
        |     |     +-- notes-procs-postgresql.xql        PostgreSQL-specific Queries for notes-procs.tcl
        |     |     +-- notes-init.tcl                    Tcl Initialization
        |     |     +-- notes-init.xql                    Queries for notes-init.tcl (work in all DBs)
        |     |     +-- *.tcl                             Tcl Library Files
        |     +-- lib/
        |     |     |
        |     |     +-- *.tcl                             Includable page logic
        |     |     +-- *.adp                             Includable page templates
        |     +-- www/
        |     |     |
        |     |     +-- admin/                            Administration UI
        |     |     |     +-- tests/                      Regression Tests
        |     |     |     |     +-- index.tcl             Regression Test Index Page
        |     |     |     |     +-- ...                   Regression Tests
        |     |     |     +-- index.tcl                   Administration UI Index Page
        |     |     |     +-- ...                         Administration UI Pages
        |     |     |
        |     |     +-- doc/                              Documentation
        |     |     |     +-- index.html                  Documentation Index Page
        |     |     |     +-- ...                         Administration Pages
        |     |     +-- resources/                        Static Content
        |     |     |     +-- ...                         Static Content files
        |     |     +-- index.tcl                         UI Index Page
        |     |     +-- index.adp                         UI Index Template
        |     |     +-- index.xql                         Queries for UI Index page
        |     |     +-- *.tcl                             UI Logic Scripts
        |     |     +-- *.adp                             UI Templates
        |     |     +-- *-oracle.xql                      Oracle-specific Queries
        |     |     +-- *-postgresql.xql                  PostgreSQL-specific Queries
        +-- Other package directories.

All file locations are relative to the package root, which in this case is ROOT/packages/notes. The following table describes in detail what each of the files up in the diagram contain.

A special note on the PACKAGE-KEY/www/resources directory. Files in this directory are available at http://yourserver/resources/PACKAGE-KEY/... and are returned without any permissions checking or even checks that the package is installed or mounted. Files are returned directly, so .tcl or .adp files are not sourced in these directories. This makes it suitable for storing icons, css files, javascript, and other static content which can be treated this way.

Table11.1.Package files

File Type Its Use Naming Convention
Package Specification File The package specification file is an XML file generated and maintained by the OpenACS Package Manager (APM). It specifies information about the package including its parameters and its files. notes.info
Data Model Creation Script Contains the SQL that creates the necessary data model and PL/SQL packages (or PL/pgSQL or whatever) to support the package. The name must match the convention below or the package will not be installed correctly. Notice that the script must be under the appropriate directory for the database you are developing your package for (hopefully all OpenACS-supported databases :-)) sql/<database>/notes-create.sql
Data Model Drop Script Contains the SQL that removes the data model and PL/SQL packages generated by the creation script. The name must match the convention below or the package will not be installed correctly. sql/<database>/notes-drop.sql
Data Model File Any .sql file that does not match the naming convention above is recognized as a data model file. It is useful to separate the SQL in the creation and drop scripts into several files and then have the scripts source the other data model files. In Oracle this can be done by including @@ filename in the creation or drop scripts. See the Oracle FAQ for examples. In PostgreSQL the same is acomplished by including \i filename. sql/<database>/*.sql
Data Model Upgrade Scripts Contain changes to the data model between versions. The APM can automatically load the appropriate upgrade scripts when upgrading to a new version of a package. sql/<database>/upgrade/upgrade-<old>-<new>.sql
SQL92 Query Files Files with queries that are supported by all databases. These are usually SQL92 queries. Notice that the .xql filename must match the name of the .tcl file that uses those queries. *.xql
Oracle-specific Query Files Files with queries that are Oracle-specific. Notice that the .xql filename must match the name of the .tcl file that uses those queries. *-oracle.xql
PostgreSQL-specific Query Files Files with queries that are PostgreSQL-specific. Notice that the .xql filename must match the name of the .tcl file that uses those queries. *-postgresql.xql
Tcl Library Files The Tcl library files include a set of procedures that provide an application programming interface (API) for the package to utilize. tcl/notes-procs.tcl
Tcl Initialization The initialization files are used to run Tcl procedures that should only be sourced once on startup. Examples of statements to put here are registered filters or procedures. Tcl initialization files are sourced once on server startup after all of the Tcl library files are sourced. tcl/notes-init.tcl
Administration UI The administration UI is used to administer the instances of the package. For example, the forums administration UI is used to create new forums, moderate postings, and create new categories for forums postings. www/admin/*
Administration UI Index Page Every package administration UI must have an index page. In most cases, this is index.tcl but it can be any file with the name index, such as index.html or index.adp. www/admin/index.tcl
Regression Tests Every package should have a set of regression tests that verify that it is in working operation. These tests should be able to be run at any time after the package has been installed and report helpful error messages when there is a fault in the system. www/admin/tests/
Regression Test Index Page The regression test directory must have an index page that displays all of the tests available and provides information on how to run them. This file can have any extension, as long as its name is index. www/admin/tests/index.html
Documentation Every package must include a full set of documentation that includes requirements and design documents, and user-level and developer-level documentation where appropriate. www/doc/
Documentation Index Page The documentation directory must include a static HTML file with the name of index.html. www/doc/index.html
UI Logic Scripts Packages provide a UI for users to access the system. The UI is split into Logic and Templates. The logic scripts perform database queries and prepare variables for presentation by the associated templates. www/*.tcl
UI Templates Templates are used to control the presentation of the UI. Templates receive a set of data sources from the logic scripts and prepare them for display to the browser. www/*.adp
UI Index Page The UI must have an index page composed of a logic script called index.tcl and a template called index.adp. www/index.tcl

The APM is used to create, maintain, and install packages. It takes care of copying all of the files and registering the package in the system. The APM is responsible for:

  1. Package registration

  2. Automatic installation of packages: loading data models, code libraries, and so on.

  3. Checking what packages depend on what other packages.

  4. Storing information on the package including ownership and a file list.

In addition for packages that are applications, the APM is responsible for keeping track of where in the site a user must go in order to use the application. To do this, the APM defines a set of objects that we call package instances. Once a package is loaded, the administrator can create as many instances of the package as she likes, and map these instances to any URL in the site that she wants. If packages are analogous to executable programs in an operating system, then package instances are analgous to multiple running copies of a single program. Each instance can be independently administered and each instance maintains its own set of application parameters and options.

The following sections will show you how to make a package for the Notes application. In addition, they will discuss some site management features in OpenACS 5 that take advantage of the APM's package instance model. The two most important of these are subsites, and the site map tool, which can be used to map applications to one or more arbitrary URLs in a running site.

We will also discuss how to organize your files and queries so they work with the OpenACS Query Dispatcher.

Here is how you make a package.

  1. Login as a site-wide administrator on your web service.

  2. Go to the package manager on your server. The URL is /acs-admin/apm.

  3. Click on the link /acs-admin/apm/package-add.

  4. Fill out the form for adding a new package. The form explains what everything means, but we'll repeat the important bits here for easy reference:

    Package Key

    This is a short text string that should uniquely name your package to distinguish it from all the others. It is used as a database key to keep track of the package and as the name of the directory in the file system where all the files related to your package will live. Example package keys in the current system include: forums, acs-kernel and so on. For the example application, we will use the package key notes.

    Package Name

    This is a short human readable name for your package. For our example, we will use the name "Notes".

    Package Plural

    If your package name is a nice singular noun, this should be the plural form of it. I assume the plural form is used when multiple instances of the package are used by a single service. We'll talk more about package instances later. Our example apllication doesn't really have a good plural name. So just make it also be "Notes".

    Package Type

    Generally we think of packages as either being applications, meaning that the package is meant primarily for use by end-users, or services meaning that the package is meant to be a reusable library of code, to be used by other packages. forums is a good example of an application, while acs-templating is a good example of a service. Our example is an application, so pick that.

    Package URL

    The URL from which people will download your package when it is done. Just use the default for this, you can change it later.

    Initial Version

    Just use the default here, which by convention is 0.1d.

    Version URL

    Just use the default here.

    Summary and Description

    Enter a short summary and longer description of what the Notes application will do. That is, something like "this application keeps short textual notes in the database", and so on.

  5. Click the button "Create Package".

  6. At this point, APM will create a directory called ROOT/packages/notes.

  7. The directory that APM created will be empty except for the notes.info file. Create a file called ROOT/packages/notes/sql/oracle/notes-create.sql. We'll fill this file with our data model very soon. Create a file called ROOT/packages/notes/sql/oracle/notes-drop.sql. This will contain the instructions to drop the data model. To be complete, you would also create the PostgreSQL versions of these files as well in ROOT/packages/notes/sql/postgresql/notes-create.sql and ROOT/packages/notes/sql/postgresql/notes-drop.sql.

    After you do this, go back to the main APM page. From there, click the link called "notes" to go to the management page for the new package. Now click the link called "Manage file information", then the "Scan the packages/notes directory for additional files in this package" link on that page to scan the file system for new files. This will bring you do a page that lists all the files you just added and lets you add them to the notes package.

    Note that while the .sql files have been added to the packge, they have not been loaded into the database. For the purposes of development, you have to load the data model by hand, because while OpenACS has automatic mechanisms for loading and reloading .tcl files for code, it does not do the same thing for data model files.

  8. Now go back to the main management page for the notes If your package has parameters, create them using the "Manage Parameter Information" link. Define package callbacks via the "Tcl Callbacks (install, instantiate, mount)" link.

  9. The new package has been created and installed in the server. At this point, you should add your package files to your CVS repository. I'll assume that you have set up your development repository according to the standards described in this appendix. If so, then you just do this:

    % cd ROOT/packages
    % cvs add notes
    % cd notes
    % cvs add notes.info
    % cvs add sql
    % cd sql
    % cvs add *.sql
    % cd ROOT/packages/notes
    % cvs commit -m "add new package for notes"
        
  10. Now you can start developing the package. In addition to writing code, you should also consider the tasks outlined in the package development tutorial.

At this point, you are probably excited to see your new package in action. But, we haven't added any user visible pages yet. By convention, user visible pages go in the ROOT/packages/notes/www directory. So go there and add a file called hello.html with some text in it. Now we have to make the user pages visible in the site. Since we didn't put the pages underneath ROOT/www they will not appear on their own. What we have to do is mount the application into the site map. That is, we have to define the URL from which the application will serve its pages.

In OpenACS 5, administrators can define an arbitrary mapping between the URLs the user types and the actual file in the file system that is served. This mapping is called the site map and entries in the site map are called site nodes. Each site node maps a URL to an OpenACS object. Since package instances are objects, the site map allows us to easily map package instances to URLs. As we said before, each instance of an application has its own set of parameters and runs from its own URL within the site. What this means is that even though all the code for the notes application lives in ROOT/packages/notes, the application itself can run from any number of locations in the site. This allows developers and administrators to build sites that look to the user like a collection of many indedendent applications that actually run on a single shared code base. The request-processor document shows you how OpenACS figures out which instance of your application was requested by the user at any given time. The page development tutorial shows you how to use this information in your user interface.

In order to make the new notes application visible to users, we have to mount it in the site map. You do this by going to the Site Map page, which is by default available at /acs-admin/site-map. Use the interface here to add a new sub-folder called notes to the root of the site, then click "new application" to mount a new instance of the notes application to the site. Name the new instance notes-1.

Then type this URL into your browser: http://yourserver/notes/hello.html

Now you should see the contents of the page that you added. What has happened is that all URLs that start with /notes have been mapped in such a way as to serve content from the directory ROOT/packages/notes/www. At this point, you can experiment with the site map by mounting multiple instances of the not yet written Notes application at various places in the site. In a later document, we'll see how to write your application so that the code can detect from what URL it was invoked. This is the key to supporting subsites.

The APM performs the following tasks in an OpenACS site:

  • Manages creation, installation, and removal of packages from the server. Also keeps track of what files belong to which packages.

  • Manages package upgrades.

  • Manages information on all package instances in a site. For correctly written application packages, this allows the site administrator to map multiple instances of a package to URLs within a site.

  • Writes out package distribution files for other people to download and install. We'll cover this later.

Future Topics

Created by Gustaf Neumann, last modified by Gustaf Neumann 17 Feb 2008, at 07:08 AM

  • How to enforce security so that users can't change other users records

  • How to use the content management tables so that ... what?

  • How to change the default stylesheets for Form Builder HTML forms.

  • How to make your package searchable with OpenFTS/Oracle

  • How to prepare pagelets for inclusion in other pages

  • How and when to put procedures in a tcl procedure library

  • More on ad_form - data validation, other stuff. (plan to draw from Jon Griffin's doc)

  • partialquery in xql

  • How to use the html/text entry widget to get the "does this look right" confirm page

  • APM package dependencies

See also the OpenACS Programming FAQ

Prepare the package for distribution.

Created by Gustaf Neumann, last modified by Gustaf Neumann 17 Feb 2008, at 07:08 AM

Browse to the package manager. Click on tutorialapp.

Click on Generate a distribution file for this package from the filesystem.

Click on the file size (37.1KB) after the label Distribution File: and save the file to /var/tmp.

Package development guidelines

Vacuum Postgres nightly

Created by Gustaf Neumann, last modified by Gustaf Neumann 17 Feb 2008, at 07:08 AM

The "vacuum" command must be run periodically to reclaim space in versions of PostgreSQL before 7.4. The "vacuum analyze" form additionally collects statistics on the disbursion of columns in the database, which the optimizer uses when it calculates just how to execute queries. The availability of this data can make a tremendous difference in the execution speed of queries. This command can also be run from cron, but it probably makes more sense to run this command as part of your nightly backup procedure - if "vacuum" is going to screw up the database, you'd prefer it to happen immediately after (not before!) you've made a backup! The "vacuum" command is very reliable, but conservatism is the key to good system management. So, if you're using the export procedure described above, you don't need to do this extra step.

Edit your crontab:

[joeuser ~]$ crontab -e

We'll set vacuum up to run nightly at 1 AM. Add the following line:

0 1 * * * /usr/local/pgsql/bin/vacuumdb $OPENACS_SERVICE_NAME

Notifications

Created by Gustaf Neumann, last modified by Gustaf Neumann 17 Feb 2008, at 07:08 AM

by David Bell and Simon Carstensen

OpenACS docs are written by the named authors, and may be edited by OpenACS documentation staff.

The notifications package allows you to send notifications through any defined communications medium (e.g. email, sms) upon some event occuring within the system.

This tutorial steps through the process of integrating the notifications package with your package.

First step is to create the notification types. To do this a script similar to the one below needs to be loaded into Postgresql. I create this script in a package-name/sql/postgresql/package-name-notifications-init.sql file. I then load this file from my create sql file. The following code snippet is taken from Weblogger. It creates a lars_blogger_notif notification type (which was created above).

    create function inline_0() returns integer as '
    declare
            impl_id integer;
            v_foo   integer;
    begin
        -- the notification type impl
        impl_id := acs_sc_impl__new (
                      ''NotificationType'',
                      ''lars_blogger_notif_type'',
                      ''lars-blogger''
        );

        v_foo := acs_sc_impl_alias__new (
                    ''NotificationType'',
                    ''lars_blogger_notif_type'',
                    ''GetURL'',
                    ''lars_blogger::notification::get_url'',
                    ''TCL''
        );

        v_foo := acs_sc_impl_alias__new (
                    ''NotificationType'',
                    ''lars_blogger_notif_type'',
                    ''ProcessReply'',
                    ''lars_blogger::notification::process_reply'',
                    ''TCL''
        );

        PERFORM acs_sc_binding__new (
                    ''NotificationType'',
                    ''lars_blogger_notif_type''
        );

        v_foo:= notification_type__new (
	        NULL,
                impl_id,
                ''lars_blogger_notif'',
                ''Blog Notification'',
                ''Notifications for Blog'',
		now(),
                NULL,
                NULL,
		NULL
        );

        -- enable the various intervals and delivery methods
        insert into notification_types_intervals
        (type_id, interval_id)
        select v_foo, interval_id
        from notification_intervals where name in (''instant'',''hourly'',''daily'');

        insert into notification_types_del_methods
        (type_id, delivery_method_id)
        select v_foo, delivery_method_id
        from notification_delivery_methods where short_name in (''email'');

        return (0);
    end;
    ' language 'plpgsql';

    select inline_0();
    drop function inline_0();
    

You also need a drop script. This is untested for comptability with the above script.

      -- @author gwong@orchardlabs.com,ben@openforce.biz
      -- @creation-date 2002-05-16
      --
      -- This code is newly concocted by Ben, but with significant concepts and code
      -- lifted from Gilbert's UBB forums. Thanks Orchard Labs.
      -- Lars and Jade in turn lifted this from gwong and ben.

create function inline_0 ()
returns integer as '
declare
    row                             record;
begin
    for row in select nt.type_id
               from notification_types nt
               where nt.short_name in (''lars_blogger_notif_type'',''lars_blogger_notif'')
    loop
        perform notification_type__delete(row.type_id);
    end loop;

    return null;
end;' language 'plpgsql';

select inline_0();
drop function inline_0 ();

--
-- Service contract drop stuff was missing - Roberto Mello
--

create function inline_0() returns integer as '
declare
        impl_id integer;
        v_foo   integer;
begin

        -- the notification type impl
        impl_id := acs_sc_impl__get_id (
                      ''NotificationType'',		-- impl_contract_name
                      ''lars_blogger_notif_type''	-- impl_name
        );

        PERFORM acs_sc_binding__delete (
                    ''NotificationType'',
                    ''lars_blogger_notif_type''
        );

        v_foo := acs_sc_impl_alias__delete (
                    ''NotificationType'',		-- impl_contract_name	
                    ''lars_blogger_notif_type'',	-- impl_name
                    ''GetURL''				-- impl_operation_name
        );

        v_foo := acs_sc_impl_alias__delete (
                    ''NotificationType'',		-- impl_contract_name	
                    ''lars_blogger_notif_type'',	-- impl_name
                    ''ProcessReply''			-- impl_operation_name
        );

	select into v_foo type_id
	  from notification_types
	 where sc_impl_id = impl_id
	  and short_name = ''lars_blogger_notif'';

	perform notification_type__delete (v_foo);

	delete from notification_types_intervals
	 where type_id = v_foo
	   and interval_id in (
		select interval_id
		  from notification_intervals
		 where name in (''instant'',''hourly'',''daily'')
	);

	delete from notification_types_del_methods
	 where type_id = v_foo
	   and delivery_method_id in (
		select delivery_method_id
		  from notification_delivery_methods
		 where short_name in (''email'')
	);

	return (0);
end;
' language 'plpgsql';

select inline_0();
drop function inline_0();
    

The next step is to setup our notification creation. A new notification must be added to the notification table for each blog entry added. We do this using the notification::new procedure

        notification::new  -type_id [notification::type::get_type_id  -short_name lars_blogger_notif]  -object_id $blog(package_id)  -response_id $blog(entry_id)  -notif_subject $blog(title)  -notif_text $new_content
    

This code is placed in the tcl procedure that creates blog entries, right after the entry gets created in the code. The $blog(package_id) is the OpenACS object_id of the Weblogger instance to which the entry has been posted to and the $new_content is the content of the entry. This example uses the package_id for the object_id, which results in setting up notifications for all changes for blogger entries in this package. However, if you instead used the blog_entry_id or something like that, you could set up per-item notifications. The forums packages does this -- you can look at it for an example.

The final step is to setup the notification subscription process. In this example we want to let a user find out when a new entry has been posted to the blog. To do this we put a link on the blog that allows them to subscribe to notifications of new entries. The notifications/requests-new page is very handy in this situation.

Such a link can be created using the notification::display::request_widget proc:

    set notification_chunk [notification::display::request_widget  -type lars_blogger_notif  -object_id $package_id  -pretty_name [lars_blog_name]  -url [lars_blog_public_package_url]  ]
    

which will return something like

    You may <a href="/notifications/request-new?...">request notification</a> for Weblogger.

which can be readily put on the blog index page. The pretty_name parameter is what appears at the end of the text returned (i.e. "... request notification</a> for pretty_name"), The url parameter should be set to the address we want the user to be redirected to after they have finished the subscription process.

This should be all you need to implement a notification system. For more examples look at the forums package.

Security Requirements

Created by Gustaf Neumann, last modified by Gustaf Neumann 17 Feb 2008, at 07:08 AM

By Richard Li

OpenACS docs are written by the named authors, and may be edited by OpenACS documentation staff.

This document lists the requirements for the security system for the OpenACS.

Virtually all web sites support personalized content based on user identity. The level of personalization may be as simple as displaying the name of the user on certain pages or can be as sophisticated as dynamically recommending sections of site that the user may be interested in based on prior browsing history. In any case, the user's identity must be validated and made available to the rest of the system. In addition, sites such as ecommerce vendors require that the user identity be securely validated.

The security system consists of a number of subsystems.

Signed Cookies

Cookies play a key role in storing user information. However, since they are stored in plaintext on a user's system, the validity of cookies is an important issue in trusting cookie information. Thus, we want to be able to validate a cookie, but we also want to validate the cookie without a database hit.

  • 10.0 Guaranteed Tamper Detection Any tampering of cookie data should be easily detectable by the web server.

  • 10.1 Performance and Scalability Validation and verification of the cookie should be easily scalable and should not require a database query on every hit.

Session Properties

Applications should be able to store session-level properties in a database table.

  • 11.0 Storage API Session-level data should be accessible via an API.

  • 11.1 Purge Mechanism An efficient pruning mechanism should be used to prevent old session level properties from filling up the table.

Login

The security system should support the concept of persistent user logins. This persistence takes several forms.

  • 12.0 Permanent Login Users should be able to maintain a permanent user login so that they never need to type their password.

  • 12.1 Session Login The security system should support the concept of a session, with authentication tokens that become invalid after a certain period of time.

  • 12.2 Session Definition A session is a sequence of clicks by one user from one browser in which no two clicks are separated by more than some constant (the session timeout).

  • 12.3 Stateless The security system should not require state that is stored in the server. Required state may reside only in the user request (including cookies), and in the database. A single user should be able to log in to the system even if the user is sent to a different AOLserver for each step of the login process (e.g., by a load balancer).

  • 12.4 Secure The security system should not store passwords in clear text in the database.

  • 13.0 SSL Hardware The system must work when the SSL processing occurs outside of the web server (in specialized hardware, in a firewall, etc.).

Adding in parameters for your package

Created by Gustaf Neumann, last modified by Gustaf Neumann 17 Feb 2008, at 07:08 AM

Each instance of a package can have paramaters associated with it. These are like preferences, and they can be set by the administrator for each application to change the behavior of your application.

To add parameters for your package, go to the Automatic Package Manager (/acs-admin/apm)

Click on your package

Under the Manage section, click on Parameters

It's fairly self-explanatory at this point. Create the parameters you want, and then access them in your code using the parameter::get procedure.

Install Full Text Search using Tsearch2

Created by Gustaf Neumann, last modified by Gustaf Neumann 17 Feb 2008, at 07:08 AM

By Dave Bauer, Joel Aufrecht and Malte Sussdorff with help from Tsearch V2 Introduction by Andrew J. Kopciuch

OpenACS docs are written by the named authors, and may be edited by OpenACS documentation staff.

If you want full text search, and you are running PostgreSQL, install this module to support FTS. Do this step after you have installed both PostgreSQL and AOLserver. You will need the tseach2 module form PostgreSQL contrib. This is included with the PostgreSQL full source distribution. It is also available with the PostgreSQL contrib package provided by most distribution packages. On debian it is called postgresql-contrib.

  1. For PostgreSQL 7.3 or 7.4, download the http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/regprocedure_7.4.patch.gz tsearch2 patch to correctly restore from a pg_dump backup. If you installed tsearch2 from a package, you can use the http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/regprocedure_update.sql regprocedure script to update the database after tsearch2 is installed into it. TODO link to section decribing how to fix an existing tsearch2 database with this patch.

  2. As of May 9, 2004 there is a source patch available for tsearch2. The patch provides changes to the pg_ts_ configuration tables to allow for easy dump and restore of a database containing tsearch2. The patch is available here : [http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/regprocedure_7.4.patch.gz]

    To apply this patch, download the mentioned file and place it in your postgreSQL source tree ($PGSQL_SRC). This patch makes the backup and restore procedures very simple.

                [postgres pgsql]$ cd /tmp
                [postgres tmp]$ wget http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/regprocedure_7.4.patch.gz
                [postgres pgsql]$ cd /usr/local/src/postgresql-7.4.5/
                [postgres postgresql-7.4.5] gunzip /tmp/regprocedure_7.4.patch.gz
                [postgres postgresql-7.4.5] patch -b -p1 < regprocedure_7.4.patch
    

    If you have a working version of tsearch2 in your database, you do not need to re-install the tsearch2 module. Just apply the patch and run make. This patch only affects the tsearch2.sql file. You can run the SQL script found : [right here] This script will make the modifications found in the patch, and update the fields from the existing data. From this point on, you can dump and restore the database in a normal fashion. Without this patch, you must follow the instructions later in this document for backup and restore.

    This patch is only needed for tsearch2 in PostgreSQL versions 7.3.x and 7.4.x. The patch has been applied to the sources for 8.0.

  3. Install Tsearch2. This is a PostgreSQL module that the tsearch2-driver OpenACS package requires. These instructions assume you are using the latest point release of PostgreSQL 7.4.5.

    [root root]# su - postgres
    [postgres pgsql]$ cd /usr/local/src/postgresql-7.4.5/contrib/tsearch2/
    [postgres tsearch2]$ make
    [postgres tsearch2]$ make install
    mkdir /usr/local/pgsql/share/contrib
    mkdir /usr/local/pgsql/doc/contrib
    (2 lines omitted)
    /bin/sh ../../config/install-sh -c -m 755 libtsearch.so.0.0 /usr/local/pgsql/lib/tsearch.so
    [postgres tsearch]$ exit
    logout
    
    [root root]#
    su - postgres
    cd /usr/local/src/postgresql-7.4.5/contrib/tsearch2
    make
    make install
    exit
    
  1. Click Admin on the top of the default home page. If prompted, log in with the account and password you entered during install.

  2. Click on the Install software link.

  3. Click on the Install new service link.

  4. Click on the Install link next to Tsearch2 Driver. If you have installed tsearch2 into your PostgreSQL database, the installer will automatically enable tsearch in your OpenACS database instance.

  5. Restart the service.

    [$OPENACS_SERVICE_NAME $OPENACS_SERVICE_NAME]$ svc -t /service/$OPENACS_SERVICE_NAME
    
    [$OPENACS_SERVICE_NAME $OPENACS_SERVICE_NAME]$
  6. Wait a minute, then browse back to the home page.

  7. Click on Admin on the top of the screen.

  8. Click on Main Site Administration in the "Subsite Administration" section.

  9. Click on Site Map in the "Advanced Features" section.

  10. Mount the Search interface in the site map.

    1. Click the new sub folder link on the Main Site line.

    2. Type search and click New.

    3. Click the new application link on the search line.

    4. Type search where it says untitled, choose search from the drop-down list, and click New.

    5. Click the Parameters link next to the Search package istance.

    6. Type tsearch2-driver where it says openfts-driver in the FtsEngineDriver parameter.

  11. Restart the service.

    [$OPENACS_SERVICE_NAME $OPENACS_SERVICE_NAME]$ svc -t /service/$OPENACS_SERVICE_NAME
    
    [$OPENACS_SERVICE_NAME $OPENACS_SERVICE_NAME]$
  12. Wait a minute, then click on Main Site at the top of the page.

Enabling Full Text Search in packages at the moment is not trivial. It involves a couple of steps, which I will illustrate taking lars-blogger as an example package

  1. Install the package.

    1. Click Admin on the top of the default home page. If prompted, log in with the account and password you entered during install.

    2. Click on the Install software link.

    3. Click on the Install new application link.

    4. Click on the Install link next to Weblogger.

    5. Install all required packages as well (always say okay until you shall restart the server)

  2. Load the service contracts datamodell and enable the service contract

    [$OPENACS_SERVICE_NAME $OPENACS_SERVICE_NAME]$ cd packages/lars-blogger/sql/postgresql
    [$OPENACS_SERVICE_NAME postgresql]$ psql $OPENACS_SERVICE_NAME -f lars-blogger-sc-create.sql

    Note: Usually this script is called package_name-sc-create.sql

  3. Restart the service.

    [$OPENACS_SERVICE_NAME postgresql]$ svc -t /service/$OPENACS_SERVICE_NAME
    
                    [$OPENACS_SERVICE_NAME postgresl]$

If you are lucky, Full Text Search is enabled now, if not consult http://openacs.org/forums/message-view?message_id=154759. This link also contains some hints on how to make sure it is enabled.

Unpack the OpenACS tarball

Created by Gustaf Neumann, last modified by Gustaf Neumann 17 Feb 2008, at 07:08 AM

The OpenACS tarball contains sample configuration files for some of the packages listed below. In order to access those files, unpack the tarball now.

[root root]# cd /tmp
[root tmp]# tar xzf openacs-5.2.3rc1.tgzcd /tmp
tar xzf openacs-5.2.3rc1.tgz

If you are installing from a different method and just need the configuration files, you can instead get them from CVS:

[root root]# cd /tmp
[root tmp]# cvs -d :pserver:anonymous@cvs.openacs.org:/cvsroot co openacs-4/packages/acs-core-docs/www/files/
cvs checkout: warning: failed to open /root/.cvspass for reading: No such file or directory
cvs server: Updating openacs-4/packages/acs-core-docs/www/files
U openacs-4/packages/acs-core-docs/www/files/README.TXT
(many lines omitted)
U openacs-4/packages/acs-core-docs/www/files/template-ini.ini
U openacs-4/packages/acs-core-docs/www/files/winnsd.txt
[root tmp]# mv openacs-4 openacs-5.2.3rc1cd /tmp
cvs -d :pserver:anonymous@cvs.openacs.org:/cvsroot co openacs-4/packages/acs-core-docs/www/files/
mv openacs-4 openacs-5.0.0a4

Next Page