- I OpenACS For Everyone
- I.1 High level information: What is OpenACS?
- I.1.1 Overview
- I.1.2 OpenACS Release Notes
- I.2 OpenACS: robust web development framework
- I.2.1 Introduction
- I.2.2 Basic infrastructure
- I.2.3 Advanced infrastructure
- I.2.4 Domain level tools
- I.1 High level information: What is OpenACS?
- II Administrator's Guide
- II.2 Installation Overview
- II.2.1 Basic Steps
- II.2.2 Prerequisite Software
- II.3 Complete Installation
- II.3.1 Install a Unix-like system and supporting software
- II.3.2 Install Oracle 10g XE on debian
- II.3.2.1 Install Oracle 8.1.7
- II.3.3 Install PostgreSQL
- II.3.4 Install AOLserver 4
- II.3.5 Quick Install of OpenACS
- II.3.5.1 Complex Install OpenACS 5.3
- II.3.6 OpenACS Installation Guide for Windows2000
- II.3.7 OpenACS Installation Guide for Mac OS X
- II.4 Configuring a new OpenACS Site
- II.4.1 Installing OpenACS packages
- II.4.2 Mounting OpenACS packages
- II.4.3 Configuring an OpenACS package
- II.4.4 Setting Permissions on an OpenACS package
- II.4.5 How Do I?
- II.4.6 Configure OpenACS look and feel with templates
- II.5 Upgrading
- II.5.1 Overview
- II.5.2 Upgrading 4.5 or higher to 4.6.3
- II.5.3 Upgrading OpenACS 4.6.3 to 5.0
- II.5.4 Upgrading an OpenACS 5.0.0 or greater installation
- II.5.5 Upgrading the OpenACS files
- II.5.6 Upgrading Platform components
- II.6 Production Environments
- II.6.1 Starting and Stopping an OpenACS instance.
- II.6.2 AOLserver keepalive with inittab
- II.6.3 Running multiple services on one machine
- II.6.4 High Availability/High Performance Configurations
- II.6.5 Staged Deployment for Production Networks
- II.6.6 Installing SSL Support for an OpenACS service
- II.6.7 Set up Log Analysis Reports
- II.6.8 External uptime validation
- II.6.9 Diagnosing Performance Problems
- II.7 Database Management
- II.7.1 Running a PostgreSQL database on another server
- II.7.2 Deleting a tablespace
- II.7.3 Vacuum Postgres nightly
- II.8 Backup and Recovery
- II.8.1 Backup Strategy
- II.8.2 Manual backup and recovery
- II.8.3 Automated Backup
- II.8.4 Using CVS for backup-recovery
- II.A Install Red Hat 8/9
- II.B Install additional supporting software
- II.B.1 Unpack the OpenACS tarball
- II.B.2 Initialize CVS (OPTIONAL)
- II.B.3 Add PSGML commands to emacs init file (OPTIONAL)
- II.B.4 Install Daemontools (OPTIONAL)
- II.B.5 Install qmail (OPTIONAL)
- II.B.6 Install Analog web file analyzer
- II.B.7 Install nspam
- II.B.8 Install Full Text Search
- II.B.9 Install Full Text Search using Tsearch2
- II.B.10 Install Full Text Search using OpenFTS (deprecated see tsearch2)
- II.B.11 Install nsopenssl
- II.B.12 Install tclwebtest.
- II.B.13 Install PHP for use in AOLserver
- II.B.14 Install Squirrelmail for use as a webmail system for OpenACS
- II.B.15 Install PAM Radius for use as external authentication
- II.B.16 Install LDAP for use as external authentication
- II.B.17 Install AOLserver 3.3oacs1
- II.C Credits
- II.C.1 Where did this document come from?
- II.C.2 Linux Install Guides
- II.C.3 Security Information
- II.C.4 Resources
- II.2 Installation Overview
- III For OpenACS Package Developers
- III.9 Development Tutorial
- III.9.1 Creating an Application Package
- III.9.2 Setting Up Database Objects
- III.9.3 Creating Web Pages
- III.9.4 Debugging and Automated Testing
- III.10 Advanced Topics
- III.10.1 Write the Requirements and Design Specs
- III.10.2 Add the new package to CVS
- III.10.3 OpenACS Edit This Page Templates
- III.10.4 Adding Comments
- III.10.5 Admin Pages
- III.10.6 Categories
- III.10.7 Profile your code
- III.10.8 Prepare the package for distribution.
- III.10.9 Distributing upgrades of your package
- III.10.10 Notifications
- III.10.11 Hierarchical data
- III.10.12 Using .vuh files for pretty urls
- III.10.13 Laying out a page with CSS instead of tables
- III.10.14 Sending HTML email from your application
- III.10.15 Basic Caching
- III.10.16 Scheduled Procedures
- III.10.17 Enabling WYSIWYG
- III.10.18 Adding in parameters for your package
- III.10.19 Writing upgrade scripts
- III.10.20 Connect to a second database
- III.10.21 Future Topics
- III.11 Development Reference
- III.11.1 OpenACS Packages
- III.11.2 OpenACS Data Models and the Object System
- III.11.3 The Request Processor
- III.11.4 The OpenACS Database Access API
- III.11.5 Using Templates in OpenACS
- III.11.6 Groups, Context, Permissions
- III.11.7 Writing OpenACS Application Pages
- III.11.8 Parties in OpenACS
- III.11.9 OpenACS Permissions Tediously Explained
- III.11.10 Object Identity
- III.11.11 Programming with AOLserver
- III.11.12 Using Form Builder: building html forms dynamically
- III.12 Engineering Standards
- III.12.1 OpenACS Style Guide
- III.12.2 Release Version Numbering
- III.12.3 Constraint naming standard
- III.12.4 ACS File Naming and Formatting Standards
- III.12.5 PL/SQL Standards
- III.12.6 Variables
- III.12.7 Automated Testing
- III.13 CVS Guidelines
- III.13.1 Using CVS with OpenACS
- III.13.2 OpenACS CVS Concepts
- III.13.3 Contributing code back to OpenACS
- III.13.4 Additional Resources for CVS
- III.14 Documentation Standards
- III.14.1 OpenACS Documentation Guide
- III.14.2 Using PSGML mode in Emacs
- III.14.3 Using nXML mode in Emacs
- III.14.4 Detailed Design Documentation Template
- III.14.5 System/Application Requirements Template
- III.15 TCLWebtest
- III.16 Internationalization
- III.16.1 Internationalization and Localization Overview
- III.16.2 How Internationalization/Localization works in OpenACS
- III.16.4 Design Notes
- III.16.5 Translator's Guide
- III.D Using CVS with an OpenACS Site
- III.9 Development Tutorial
- IV For OpenACS Platform Developers
- IV.17 Kernel Documentation
- IV.17.1 Overview
- IV.17.2 Object Model Requirements
- IV.17.3 Object Model Design
- IV.17.4 Permissions Requirements
- IV.17.5 Permissions Design
- IV.17.6 Groups Requirements
- IV.17.7 Groups Design
- IV.17.8 Subsites Requirements
- IV.17.9 Subsites Design Document
- IV.17.10 Package Manager Requirements
- IV.17.11 Package Manager Design
- IV.17.12 Database Access API
- IV.17.13 OpenACS Internationalization Requirements
- IV.17.14 Security Requirements
- IV.17.15 Security Design
- IV.17.16 Security Notes
- IV.17.17 Request Processor Requirements
- IV.17.18 Request Processor Design
- IV.17.19 Documenting Tcl Files: Page Contracts and Libraries
- IV.17.20 Bootstrapping OpenACS
- IV.17.21 External Authentication Requirements
- IV.18 Releasing OpenACS
- IV.18.1 OpenACS Core and .LRN
- IV.18.2 How to Update the OpenACS.org repository
- IV.18.3 How to package and release an OpenACS Package
- IV.18.4 How to Update the translations
- IV.17 Kernel Documentation
- V Tcl for Web Nerds
- V.1 Tcl for Web Nerds Introduction
- V.2 Basic String Operations
- V.3 List Operations
- V.4 Pattern matching
- V.5 Array Operations
- V.6 Numbers
- V.7 Control Structure
- V.8 Scope, Upvar and Uplevel
- V.9 File Operations
- V.10 Eval
- V.11 Exec
- V.12 Tcl for Web Use
- V.13 OpenACS conventions for TCL
- V.14 Solutions
- VI SQL for Web Nerds
- VI.1 SQL Tutorial
- VI.1.1 SQL Tutorial
- VI.1.2 Answers
- VI.2 SQL for Web Nerds Introduction
- VI.3 Data modeling
- VI.3.1 The Discussion Forum -- philg's personal odyssey
- VI.3.2 Data Types (Oracle)
- VI.3.4 Tables
- VI.3.5 Constraints
- VI.4 Simple queries
- VI.5 More complex queries
- VI.6 Transactions
- VI.7 Triggers
- VI.8 Views
- VI.9 Style
- VI.10 Escaping to the procedural world
- VI.11 Trees
- VI.1 SQL Tutorial
III.11.2 OpenACS Data Models and the Object System
Developing data models in OpenACS 5.2.3rc1 is much like developing data models for OpenACS 3, save for the implementation. As usual, you need to examine how to model the information that the application must store and manipulate, and define a suitable set of SQL tables. In our Notes application, we have to be able to keep track of who entered a particular note, when they did it, and the actual text of the notes that users have entered. A simple data model might look like this:
create table notes ( note_id integer primary key, owner_id integer references users(user_id), creation_user references(user_id) not null, creation_date date not null, last_modified date not null, title varchar(255) not null, body varchar(1024) )
We've omitted constraint names for the purpose of clarity.
Thinking further ahead, we can imagine doing any of the following things with Notes as well:
-
Define access control policies on notes.
-
Attach user comments on notes.
-
Allow users to define custom fields to store on their notes.
-
Automatically generate input forms or output displays for notes.
-
Allow other applications to use notes in ways we don't know of yet.
In OpenACS, the key to enabling these types of services on your application data is to take advantage of the Object System. The first question, then, is "Just what are objects, and what do you use them for anyway?". The short answer: objects are anything represented in the application's data model that will need to be managed by any central service in OpenACS, or that may be reusable in the context of future applications. Every object in the system is represented using a row in the acs_objects
table. This table defines all the standard attributes that are stored on every object, including its system-wide unique ID, object type, and some generic auditing columns.
To make use of the object system, you as the application developer have to write your data model in a way that is slightly more complex than in the ACS 3.x days. What you get for this extra work includes:
-
The Permissions System lets you track who is allowed to do what to the rows in an application table, and gives you an easy way to enforce this from Tcl.
-
Every object has an attribute called
context_id
that provides a way to trivially specify both the default permissions for an object, and the intended "scope" of an object. Just set thecontext_id
to the controlling object and forget about it. -
And most importantly, any future object-level service - from a general-comments replacement to personalized ranking - will become available to your application "for free."
Using ACS objects is straightforward: all that's required are a few extra steps in the design of your application data model.
In order to hook our Notes application into the object system, we make some calls to use our notes
table as the basis for a new object type. Object types are analogous to classes in programming languages such as C++ and Java. In Java, a class defines a set of attributes that store data and a set of methods that run code. In OpenACS, we use one or more database tables to store the data attributes, and we define a stored procedure package to hold procedures to define the programming interface to the data model.
The object type itself is described using data in the acs_object_types
and acs_attributes
tables, which play a role similar to the data dictionary in Oracle. As in Java, object types can inherit attributes from a parent type, so the type system forms a hierarchy. Unlike Java, Oracle does not support this inheritance transparently, so we have to make sure we add our own bookkeeping code to keep everything consistent. Below you'll find the code needed to describe a new object type called notes
in your system.
Fire up your text editor and open the ROOT/packages/notes/sql/oracle/notes-create.sql
(ROOT/packages/notes/sql/postgresql/notes-create.sql
for the PG version) file created when we created the package. Then, do the following:
First, add an entry to the acs_object_types
table with the following PL/SQL call:
begin acs_object_type.create_type ( supertype => 'acs_object', object_type => 'note', pretty_name => 'Note', pretty_plural => 'Notes', table_name => 'NOTES', id_column => 'NOTE_ID' ); end; / show errors;
This PL/SQL call tells the system that we would like to use the table NOTES
as the basis for a new object type called note
. This type is a subtype of the acs_object
type, which means that we want to inherit all of the basic attributes of all ACS objects. As mentioned, it will take some work on our part to make this happen, since Oracle can't do it automatically. In general, most basic applications will define types that are simple subtypes of acs_object
.
Add entries to the acs_attributes
table to describe the data attributes of the new type. This data can eventually be used to do things like automatically generate user interfaces to manipulate the notes
table, though that functionality isn't yet available.
declare attr_id acs_attributes.attribute_id%TYPE; begin attr_id := acs_attribute.create_attribute ( object_type => 'note', attribute_name => 'TITLE', pretty_name => 'Title', pretty_plural => 'Titles', datatype => 'string' ); attr_id := acs_attribute.create_attribute ( object_type => 'note', attribute_name => 'BODY', pretty_name => 'Body', pretty_plural => 'Bodies', datatype => 'string' ); end; / show errors;
We can stop here and not bother to register the usual OpenACS 3.x attributes of creation_user
, creation_date
and last_modified
, since the object type acs_object
already defines these attributes. Again, because the new type note
is a subtype of acs_object
, it will inherit these attributes, so there is no need for us to define them.
The next thing we do is make a small modification to the data model to reflect the fact that each row in the notes
table represents something that is not only an object of type note
, but also an acs_object
. The new table definition looks like this:
create table notes ( note_id integer references acs_objects(object_id) primary key, owner_id integer references users(user_id), title varchar(255) not null, body varchar(1024) )
The usual creation_date
and modified_date
columns are absent since they already exist in acs_objects
. Also, note the constraint we have added to reference the acs_objects
table, which makes clear that since note
is a subtype of acs_object
, every row in the notes table must have a corresponding row in the acs_objects
table. This is the fundamental means by which we model inheritance; it guarantees that any services that use the acs_objects
table to find objects will transparently find any objects that are instances of any subtype of acs_objects
.
The next step is to define a PL/SQL package for your new type, and write some basic procedures to create and delete objects. Here is a package definition for our new type:
create or replace package note as function new ( note_id in notes.note_id%TYPE default null, owner_id in notes.owner_id%TYPE default null, title in notes.title%TYPE, body in notes.body%TYPE, object_type in acs_object_types.object_type%TYPE default 'note', creation_date in acs_objects.creation_date%TYPE default sysdate, creation_user in acs_objects.creation_user%TYPE default null, creation_ip in acs_objects.creation_ip%TYPE default null, context_id in acs_objects.context_id%TYPE default null ) return notes.note_id%TYPE; procedure delete ( note_id in notes.note_id%TYPE ); end note; / show errors
You might be wondering what all the extra parameters are to these calls, since we haven't mentioned them before. These parameters are needed to fill out information that will be stored about the object that's not stored directly in the table you defined. The OpenACS Object System defines these attributes on the type acs_object
since all objects should have these attributes. Internally, there are tables that store this information for you. Most of the data is pretty self-explanatory and reflects attributes that existed in the earlier OpenACS 3.x data models, with the exception of the context_id
attribute.
The context_id
attribute stores the ID of an object that represents the default security domain to which the object belongs. It is used by the permissions system in this way: if no permissions are explicitly attached to the object, then the object inherits its permissions from the context. For example, if I had told you how to use the permissions system to specify that an object OBJ was "read only", then any other object that used OBJ as its context would also be "read only" by default. We'll talk about this more later.
The PL/SQL package body contains the implementations of the procedures defined above. The only subtle thing going on here is that we must use acs_object.new
to insert a row into acs_objects
, before inserting a row into the notes
. Similarly, when we delete a row from note
, we have to be sure to delete the corresponding acs_object
row.
create or replace package body note as function new ( note_id in notes.note_id%TYPE default null, owner_id in notes.owner_id%TYPE default null, title in notes.title%TYPE, body in notes.body%TYPE, object_type in acs_object_types.object_type%TYPE default 'note', creation_date in acs_objects.creation_date%TYPE default sysdate, creation_user in acs_objects.creation_user%TYPE default null, creation_ip in acs_objects.creation_ip%TYPE default null, context_id in acs_objects.context_id%TYPE default null ) return notes.note_id%TYPE is v_note_id integer; begin v_note_id := acs_object.new ( object_id => note_id, object_type => object_type, creation_date => creation_date, creation_user => creation_user, creation_ip => creation_ip, context_id => context_id ); insert into notes (note_id, owner_id, title, body) values (v_note_id, owner_id, title, body); return v_note_id; end new; procedure delete ( note_id in notes.note_id%TYPE ) is begin delete from notes where note_id = note.delete.note_id; acs_object.del(note_id); end delete; end note; / show errors;
That's pretty much it! As long as you use the note.new
function to create notes, and the note.delete
function to delete them, you'll be assured that the relationship each note
has with its corresponding acs_object
is preserved.
The last thing to do is to make a file ROOT/packages/notes/sql/notes-drop.sql
so it's easy to drop the data model when, say, you're testing:
begin acs_object_type.drop_type ('note'); end; / show errors drop package note; drop table notes;
While it is hard to give general design advice without knowing anything about a particular application, you should follow the following rule of thumb when deciding when to hook part of your data model to the object system:
Anything in your data model that needs to be available to general OpenACS services such as user comments, permissions, and so on should be a subtype of acs_object
. In addition, if you want your data model to take advantage of attributes that exist in some object type that is a subtype of acs_object
, then you should use the object system.
For example, for most applications, you will want to use objects to represent the data in your application that is user visible and thus requires access control. But other internal tables, views, mapping tables and so on probably don't need to be objects. As before, this kind of design decision is mostly made on an application-by-application basis, but this is a good baseline from which to start.
In this section we cover some overall guidelines for designing data models that are meant to be integrated with the OpenACS object system.
There are two basic rules you should follow when designing OpenACS 5.2.3rc1 data models:
-
Never utilize fields in the
acs_objects
table in application specific ways. That is, never assign any application-specific semantics to this data. In the notes application, we use thecreation_date
andlast_modified
fields, but this is OK since we do not assign any application-specific meaning to these fields. -
In particular, never assign any application specific semantics to the
context_id
attribute of an object. This field is used for a very specific purpose by the permissions system, and using this field in any other way whatsoever is guaranteed to make your application act strangely.As we'll see later, the Notes example will point each note object's
context_id
to the package instance in which the note was created. The idea will be that in a real site, the administrator would create one package instance for every separate set of Notes (say, one per user). The instance would "own" all of the notes that it created, and the administrator would be able to use the package instance as the basis for access control, which is convenient.
The reason behind these two rules is pretty straightforward: First, the OpenACS Object system itself is meant to be a generic and reusable tool for any application to use for basic services. Second, in order for this to work, the various parts of the OpenACS Objects data model must be interpreted in the same way by all applications that use the data model. Therefore, assigning any application-specific semantics to any part of the core data model is a bad thing to do, because then the semantics of the data model are no longer independent of the application. This would make it impossible to build the generic tools that the data model is trying to support.
Another less important reason for these two rules is to not introduce any joins against the acs_objects
table in SQL queries in your application that you do not absolutely need.
In the Notes example, the result of applying these rules is that we are careful to define our own attribute for owner_id
rather than overloading creation_user
from the objects table. But, since we will probably use creation_date
and so on for their intended purposes, we don't bother to define our own attributes to store that data again. This will entail joins with acs_objects
but that's OK because it makes the overall data model cleaner. The real lesson is that deciding exactly how and when to use inherited attributes is fairly straightforward, but requires a good amount of thought at design time even for simple applications.
Hooking into the OpenACS 5.2.3rc1 object system brings the application developer numerous benefits, and doing it involves only four easy steps:
-
Describe the a new object type to the system. Most new application types will be subtypes of the built-in type
acs_object
. -
Define a table to store application object data.
-
Define a PL/SQL package to store procedures related to the new type. You have to define at least a function called
new
to create new application objects and a procedure calleddelete
to delete them. -
Define a package body that contains the implementations of the PL/SQL procedures defined above.
-
Try not to write queries in your application that join against
acs_objects
. This means you should never use the fields inacs_objects
for application-specific purposes. This is especially true for thecontext_id
field.