- 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.7 Writing OpenACS Application Pages
In this document, we'll examine the user interface pages of the Notes application in more detail, covering two separate aspects of page development in OpenACS. First, we'll talk about the code needed to make your pages aware of which application instance they are running in. Second, we'll talk about using the form builder to develop form-based user interfaces in OpenACS. While these seem like unrelated topics, they both come up in the example page that we are going to look at, so it makes sense to address them at the same time.
As you will recall from the packages tutorial, the Request Processor (RP) and Package Manager (APM) allow site administrators to define an arbitrary mapping from URLs in the site to objects representing content. These objects may represent single files, or entire applications. The APM uses the site map to map application instances to particular URLs within a site. We call creating such a mapping mounting the application instance at a particular URL. The tutorial also showed how a given URL is translated into a physical file to serve using the site map. We'll repeat this description here, assuming that you have mounted an instance of Notes at the URL /notes
as we did in the packages-example:
-
AOLserver receives your request for the URL
/notes/somepage
. -
This URL is passed to the request processor.
-
The RP looks up the URL in the site map, and sees that the object mounted at that location is an instance of the
notes
application. -
The RP asks the package manager where in the file system the Notes package lives. In the standard case, this would be
ROOT/packages/notes
. -
The RP translates the URL to serve a page relative to the page root of the application, which is
ROOT/packages/notes/www/
. Therefore, the page that is finally served isROOT/packages/notes/www/hello.html
, which is what we wanted.
What is missing from this description is a critical fact for application developers: In addition to working out what file to serve, the RP also stores information about which package instance the file belongs to into the AOLserver connection environment. The following ad_conn
interfaces can be used to extract this information:
[ad_conn package_url]
-
If the URL refers to a package instance, this is the URL to the root of the tree where the package is mounted.
[ad_conn package_id]
-
If the URL refers to a package instance, this is the ID of that package instance.
[ad_conn package_key]
-
If the URL refers to a package instance, this is the unique key name of the package.
[ad_conn extra_url]
-
If we found the URL in the site map, this is the tail of the URL following the part that matched a site map entry.
In the Notes example, we are particularly interested in the package_id
field. If you study the data model and code, you'll see why. As we said before in the data modeling tutorial, the Notes application points the context_id
of each Note object that it creates to the package instance that created it. That is, the context_id
corresponds exactly to the package_id
that comes in from the RP. This is convenient because it allows the administrator and the owner of the package to easily define access control policies for all the notes in a particular instance just my setting permissions on the package instance itself.
The code for adding and editing notes, in notes/www/add-edit.tcl
, shows how this works. At the top of the page, we extract the package_id
and use it to do permission checks:
set package_id [ad_conn package_id] if {[info exists note_id]} { permission::require_permission -object_id $note_id -privilege write set context_bar [ad_context_bar "Edit Note"] } else { permission::require_permission -object_id $note_id -privilege create set context_bar [ad_context_bar "New Note"] }
This code figures out whether we are editing an existing note or creating a new one. It then ensures that we have the right privileges for each action.
Later, when we actually create a note, the SQL that we run ensures that the context_id
is set the right way:
db_dml new_note { declare id integer; begin id := note.new( owner_id => :user_id, title => :title, body => :body, creation_user => :user_id, creation_ip => :peeraddr, context_id => :package_id ); end; }
The rest of this page makes calls to the form builder part of the template system. This API allows you to write forms-based pages without generating a lot of duplicated HTML in your pages. It also encapsulates most of the common logic that we use in dealing with forms, which we'll discuss next.
The forms API is pretty simple: You use calls in the template::form
namespace in your Tcl script to create form elements. The final template page then picks this stuff up and lays the form out for the user. The form is set up to route submit buttons and whatnot back to the same Tcl script that set up the form, so your Tcl script will also contain the logic needed to process these requests.
So, given this outline, here is a breakdown of how the forms code works in the add-edit.tcl
page. First, we create a form object called new_note
:
template::form create new_note
All the forms related code in this page will refer back to this object. In addition, the adp
part of this page does nothing but display the form object:
<master> @context_bar@ <hr> <center> <formtemplate id="new_note"></formtemplate> </center>
The next thing that the Tcl page does is populate the form with form elements. This code comes first:
if {[template::form is_request new_note] && [info exists note_id]} { template::element create new_note note_id -widget hidden -datatype number -value $note_id db_1row note_select { select title, body from notes where note_id = :note_id } }
The if_request
call returns true if we are asking the page to render the form for the first time. That is, we are rendering the form to ask the user for input. The tcl
part of a form page can be called in 3 different states: the initial request, the initial submission, and the validated submission. These states reflect the typical logic of a forms based page in OpenACS:
-
First render the input form.
-
Next, control passes to a validation page that checks and confirms the inputs.
-
Finally, control passes to the page that performs the update in the database.
The rest of the if
condition figures out if we are creating a new note or editing an existing note. If note_id
is passed to us from the calling page, we assume that we are editing an existing note. In this case, we do a database query to grab the data for the note so we can populate the form with it.
The next two calls create form elements where the user can insert or edit the title and body of the Note. The interface to template::element
is pretty straightforward.
Finally, the code at the bottom of the page performs the actual database updates when the form is submitted and validated:
if [template::form is_valid new_note] { set user_id [ad_conn user_id] set peeraddr [ad_conn peeraddr] if [info exists note_id] { db_dml note_update { update notes set title = :title, body = :body where note_id = :note_id } } else { db_dml new_note { declare id integer; begin id := note.new( owner_id => :user_id, title => :title, body => :body, creation_user => :user_id, creation_ip => :peeraddr, context_id => :package_id ); end; } } ad_returnredirect "." }
In this simple example, we don't do any custom validation. The nice thing about using this API is that the forms library handles all of the HTML rendering, input validation and database transaction logic on your behalf. This means that you can write pages without duplicating all of that code in every set of pages that uses forms.
To watch all of this work, use the installer to update the Notes package with the new code that you grabbed out of CVS or the package repository, mount an instance of Notes somewhere in your server and then try out the user interface pages. It should become clear that in a real site, you would be able to, say, create a custom instance of Notes for every registered user, mount that instance at the user's home page, and set up the permissions so that the instance is only visible to that user. The end result is a site where users can come and write notes to themselves.
This is a good example of the leverage available in the OpenACS 5.2.3rc1 system. The code that we have written for Notes is not at all more complex than a similar application without access control or site map awareness. By adding a small amount of code, we have taken a small, simple, and special purpose application to something that has the potential to be a very useful, general-purpose tool, complete with multi-user features, access control, and centralized administration.
In OpenACS 5.2.3rc1, application pages and scripts can be aware of the package instance, or subsite in which they are executing. This is a powerful general purpose mechanism that can be used to structure web services in very flexible ways.
We saw how to use this mechanism in the Notes application and how it makes it possible to easily turn Notes into an application that appears to provide each user in a system with their own private notes database.
We also saw how to use the templating system's forms API in a simple way, to create forms based pages with minimal duplication of code.