0.00%
Search · Index

Weblog Page

Showing 81 - 90 of 230 Postings (summary)

Scheduled Procedures

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

Put this proc in a file /packages/myfirstpackage/tcl/scheduled-init.tcl. Files in /tcl with the -init.tcl ending are sourced on server startup. This one executes my_proc every 60 seconds:

ad_schedule_proc 60 myfirstpackage::my_proc

This executes once a day, at midnight:

ad_schedule_proc  -schedule_proc ns_schedule_daily  [list 0 0]  myfirstpackage::my_proc

See ad_schedule_proc for more information.

Using Templates in OpenACS

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

By Pete Su

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

The OpenACS Template System (ATS) is designed to allow developers to cleanly separate application logic from display logic. The intent is to have all of the logic related to manipulating the database and other application state data in one place, and all the logic related to displaying the state of the application in another place. This gives developer's quicker customization and easier upgrades, and also allows developers and graphic designers to work more independently.

In ATS, you write two files for every user-visible page in the system. One is a plain .tcl file and the other is a special .adp file. The .tcl file runs a script that sets up a set of name/value bindings that we call data sources. These data sources are generally the results of Tcl and/or database queries or some combination thereof. The template system automatically makes them available to the .adp file, or the display part of the template, which is written in a combination of HTML, special template related tags, and data source substitutions.

In the overall context of our example OpenACS Notes application, this document will show you how to set up a simple templated page that displays a form to the user for entering new notes into the system. In later sections of the DG, we'll discuss how to develop the pages that actually add notes to the database, how to provide a separate instance of the Notes application to every user and how to design appropriate access control policies for the system.

In order for the Notes application to be useful, we have to allow users to enter data into the database. Typically, this takes two pages: one that displays a form for data entry, and another page that runs the code to update the database and tells the user whether the operation failed. In this document, we will use the template system to build the first of these pages. This isn't a very interesting use of the system since we won't be displaying much data, but we'll cover more on that end later.

The .tcl file for the form entry template is pretty simple. Here, the only thing we need from the database is a new ID for the note object to be inserted. Open up a file called note-add.tcl in the ROOT/packages/notes/www directory, and put the following code in it:

ad_page_contract {

    Form to add a note in OpenACS Notes.

    @author Jane Coder
    @creation-date 11 Oct 2000

} -properties {
    note_id:onevalue
    submit_label:onevalue
    target:onevalue
    page_title:onevalue
} -query {
}

set user_id [ad_verify_and_get_user_id]

db_1row user_name {
    select first_names || ' ' || last_name as user_name
    from users
    where forum_id = :user_id
}

set page_title "Add a note for $user_name"
set submit_label "Add"
set target "note-add-2"
set note_id [db_nextval acs_object_id_seq]

ad_return_template "note-add"

Some things to note about this code:

  • The procedure ad_page_contract is always the first thing a .tcl file calls, if it's under the www/ directory (i.e. not a Tcl library file). It does validation of input values from the HTTP request (i.e. form variables) and in this case, the -properties clause is used to set up the data sources that we will ship over to the .adp part of the page. In this case, we only use the simplest possible kind of data source, called a onevalue, which hold just a single string value. Later on, we'll see how to use more powerful kinds of data sources for representing multiple rows from an SQL query. You also include overall documentation for the page in the contract, and OpenACS has automatic tools that extract this documentation and make it browsable.

  • After being declared in the ad_page_contract, each property is just a simple Tcl variable. The template system passes the final value of the variable to the .adp template when the .tcl file is processed.

  • The call ad_return_template tells the template system what .adp template page to fetch to display the properties that have been processed. By default, the template system will look for a file by the same name as the .tcl file that just ran, but with an .adp extension.

Next we write the corresponding .adp page. This page outputs HTML for the form, and also contains placeholders whose values are substituted in from the properties set up by the .tcl file. Create a file called note-add.adp in your editor, and insert this text:

<master src="master">
<property name="title">@page_title@</property>
<property name="context_bar">@context_bar@</property>

<form action=@target@>
<p>Title:
<input type="text" name="title" value="">
</p>
<p>Body:
<input type="text" name="title" value="">
</p>
<p>
<center>
<input type=submit value="@submit_label@">
</center>
</p>
</form>

The main point to note here is: when you want to substitute a value into a page, you put the name of the data source between two "@" characters. Another point to note is the use of a master template: Master templates allow you do centralize display code that is used throughout an application in a single file. In this case, we intend to have a master template that does the standard page headers and footers for us - create the master.adp file, which looks like this:

<%= [ad_header $title] %>
<h2>@title@</h2>
<%= [eval ad_context_bar $context_bar] %>
<hr>
<slave>
<br clear="all">
<%= [ad_footer] %>

The main subtlety in this code is the inline Tcl code for running procs to build the header, footer, context bar, etc. Also, note the property substitutions that happen here, the values of which are set up in the <property> tags in the slave page.

After putting all these files into ROOT/packages/notes/www, you should be able to go to /notes/ URL for your server and see the input form.

Templates separate application logic from display logic by requiring the developer to write pages in two stages, one file for database queries and application logic, and another for display. In OpenACS, the logic part of the page is just a .tcl that sets up data sources that are used by the display part of the page. The display part of the page is an .adp file with some special tags and notations for dealing with display logic and inserting properties into the text of the page. Later on we'll get into templates more deeply, and show how to use database queries as data sources.

Writing OpenACS Application Pages

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

By Rafael H. Schloming and Pete Su

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

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 is ROOT/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.

Install nspam

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

Permissions Design

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

By John Prevost and Rafael H. Schloming

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

The goal of the Permissions system is to provide generic means to both programmers and site administrators to designate operations (methods) as requiring permissions, and then to check, grant, or revoke permissions via a consistent interface. For example, we might decide that the transaction that bans a user from a sub-site is an operation a site administrator is able to assign to a particular user. Or perhaps an application developer might decide that viewing a certain set of pages within the application is an operation to be individually granted or revoked from a user. It's expected that the Permissions system will be seeing a lot of use - almost every page will make at least one permissions API call, and some will make several.

For programmers, the Permissions API provides a means to work with access control in a consistent manner. If a programmer's OpenACS package defines new methods for itself, the Permissions API must provide simple calls to determine whether the current user is authorized to perform the given method. In addition, using the Permissions API, queries should easily select only those package objects on which a user has certain permissions.

For site administrators and other authorized users, the Permissions UI provides a means to aggregate the primitive operations (methods) made available by the programmer into logical privileges (like read, write, and admin) that can be granted and revoked.

In earlier versions of the OpenACS, permissions and access control was handled on a module-by-module basis, often even on a page-by-page basis. For example, a typical module might allow any registered user to access its pages read-only, but only allow members of a certain group to make changes. The way this group was determined also varied greatly between modules. Some modules used "roles", while others did not. Other modules did all access control based simply on coded rules regarding who can act on a given database row based on the information in that row.

Problems resulting from this piecemeal approach to permissions and access control were many, the two major ones being inconsistency, and repeated/redundant code. Thus the drive in OpenACS 4 to provide a unified, consistent permissions system that both programmers and administrators can readily use.

None available as of 10/2000.

The core of the permissions data model is quite simple. Unfortunately, the hierarchical nature of default permissions entails quite a number of tree queries which could slow the system down. Since every page will have at least one permissions check, a number of views and auxiliary tables (de-normalizations of the data model) have been created to speed up access queries. As a consequence, speed of updates are decreased and requirements for additional storage space increase.

As described in section V., the core of the permissions data model is simple, though a number of views and auxiliary tables exist to ensure adequate performance. The core model consists of five tables:

acs_methods

The set of all defined methods.

acs_privileges

The set of all defined privileges.

acs_privilege_method_rules

A relation describing the set of methods directly associated with each privilege.

acs_privilege_hierarchy

A relation describing which privileges directly "contain" other privileges.

acs_permissions

A table with one (party, object, privilege) row for every privilege directly granted on any object in the system - this is a denormalization of acs_privilege_method_rules and acs_privilege_hierarchy

There are also a number of views to make it easier to ask specific questions about permissions. For example, a number of the above tables describe "direct" or explicit permissions. Inheritance and default values can, however, introduce permissions which are not directly specified. (For example, read access on a forum allows read access on all the messages in the forum.)

The following views provide flattened versions of inherited information:

acs_privilege_method_map

Map of privileges to the methods they contain either directly or because of another privilege which is included (at any depth).

acs_object_grantee_priv_map

Relation on (object, party, privilege) for privileges from acs_privileges) granted directly on the object, or on the context of the object (at any depth).

acs_object_party_privilege_map

Relation on (object, party, privilege) for privileges directly from acs_object_grantee_priv_map or also because a party is a member of a group (at any depth).

acs_object_party_method_map

Relation with every (object, party, method) tuple implied by the above trees.

In general, only acs_object_party_method_map should be used for queries from other modules. The other views are intermediate steps in building that query.

The data model also includes two simple PL/SQL procedures (acs_permission.grant_permission and acs_permission.revoke_permission) for granting and revoking a specific privilege for a specific user on a specific object.

To sum up, the PL/SQL procedures are meant to be used to grant or revoke permissions. The five base tables represent the basic data model of the system, with a set of views provided to convert them into a format suitable for joining to answer specific questions. The exact means by which this transformation takes place should not be depended on, since they may change for efficiency reasons.

The transformations done create a set of default permissions, in which:

  • parties get the privileges of any groups they are directly or indirectly a member of

  • privileges get associated with the methods of any other privileges they have taken methods from (at any level) (see acs_privilege_hierarchy)

  • objects get access control from direct grants, or inherit permissions from their context (unless the "don't inherit" flag is set)

There are three essential areas in which all transactions in the permissions system fall:

  • Modification of methods and privileges

  • Modification of permissions

  • Queries on permissions

"Modification of methods and privileges." This refers to actions that happen mainly at package installation time - a package will create a number of methods for its own use, then associate them with the system's standard privileges, or new privileges which the package has created. The association step might also happen later, if the site-wide administrator chooses to change permissions policy.

These steps involve directly manipulating the acs_methods, acs_privileges, and acs_privilege_method_rules tables. A web page for manipulating these features should be limited to site-wide administrators.

"Modification of permissions" - involves fairly common operations. Users are typically able to administer permissions for objects they themselves create. The two basic operations here are "grant" and "revoke". Granting permissions is done via acs_permissions.grant_permission, and revocation via acs_permissions.revoke_permission. These directly manipulate the acs_permissions table.

Web pages for making these changes are available to all users, so they should not be in an admin area. In order to grant and revoke permissions on an object, the user must have the administer_privileges method permission on that object.

"Queries on permissions" - by far the most common operation is querying the permissions database. Several kinds of questions are commonly asked: First, and most commonly, "Can this party perform this method on this object?" Two Tcl functions are provided to answer this - one which returns a boolean, the other of which results in an error page. These tcl functions directly access the acs_object_party_method_map.

The second most commonly asked question occurs when a list of objects is being displayed, often in order to provide appropriate UI functionality: "For this party, what methods are available on these objects?" Here, the SQL query needs to filter based on whether the party/user can perform some operation on the object. This is done via a join or sub-select against acs_object_party_method_map, or by calling the Tcl functions for appropriate methods.

Finally, when administering the permissions for an object, a web page needs to know all permissions directly granted on that object. This is done by querying against acs_permissions.

The API to the permissions system consists of a few well-known tables, plus a pair of PL/SQL procedures and a pair of Tcl functions.

Tables

acs_methods, acs_privileges, and acs_privilege_method_rules manage the set of permissions in the system. At installation time, a package will add to these three tables to introduce new permissions into the system.

The main table for queries is acs_object_party_method_map, which contains (object, party, method) triples for all allowed operations in the system.

Also of interest for queries is acs_permissions, which lists directly granted privileges. Neither acs_object_party_method_map (which is a view) nor acs_permissions should be updated directly.

PL/SQL Procedures

acs_permissions.grant_permission introduces new permissions for an object. It should be given an (object, party, privilege) triple, and will always succeed. If the permission is already in the system, no change occurs. The interface for this procedure is:

procedure grant_permission (
  object_id    acs_permissions.object_id%TYPE,
  grantee_id   acs_permissions.grantee_id%TYPE,
  privilege    acs_permissions.privilege%TYPE
);

acs_permissions.revoke_permission removes a permission entry given a triple. It always succeeds--if a permission does not exist, nothing changes. The interface for this procedure is:

procedure revoke_permission (
  object_id    acs_permissions.object_id%TYPE,
  grantee_id   acs_permissions.grantee_id%TYPE,
  privilege    acs_permissions.privilege%TYPE
);

These procedures are defined in permissions-create.sql

Tcl Procedures

Two tcl procedures provide a simple call for the query, "Can this user perform this method on this object?" One returns true or false, the other presents an error page.

To receive a true or false value, Tcl code should call:

ad_permission_p $object_id $object_type $method -user_id $user_id

If the user_id argument is left out, then the currently logged in user is checked. To create an error page, Tcl code should call:

ad_require_permission $object_id $object_type $method

These procedures are defined in acs-permissions-procs.tcl.

All users of the permissions system are the same at the user-interface level. If you have the administer_privileges method permission on an object, then you may edit privileges for that object with the UI.

The UI currently provides a list of all granted permissions on the object. If the user wishes to revoke privileges, she may select a set of grants, choose revoke, confirm their deletion, and be returned to the same page after those privileges have been revoked.

Granting permissions currently (as of 10/2000) works by providing a list of all possible permissions and a list of all parties in the system. (For large sites, some future search mechanism will be necessary.) After choosing privileges to grant, the user is returned to the "edit privileges for one object" screen.

If it makes sense, the system will also display a checkbox which the user may select to toggle whether permissions are inherited from the object's context.

There are a number of potential future enhancements for the permissions UI, outlined below.

There are no configuration options for the permissions system.

The most important future changes to the Permissions system are likely to be in the UI:

  • There should be a page displaying a list of all objects for which the current user is allowed to administer privileges.

  • Users should be able to view the permissions on any object, or perhaps on objects which they have the "read_permissions" method. This would allow them to see what grants are affecting their objects through inheritance.

System creator

Rafael H. Schloming

System owner

Rafael H. Schloming

Documentation author

John Prevost

Document Revision # Action Taken, Notes When? By Whom?
0.1 Creation 9/11/2000 John Prevost
0.2 Edited for ACS 4 Beta release 10/04/2000 Kai Wu

Install Squirrelmail for use as a webmail system for OpenACS

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

By Malte Sussdorff

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

This section is work in progress. It will detail how you can install Squirrelmail as a webmail frontend for OpenACS, thereby neglecting the need to have a seperate webmail package within OpenACS

[$OPENACS_SERVICE_NAME $OPENACS_SERVICE_NAME]# cd www
[$OPENACS_SERVICE_NAME www]# wget http://cesnet.dl.sourceforge.net/sourceforge/squirrelmail/squirrelmail-1.4.4.tar.gz
[$OPENACS_SERVICE_NAME www]# tar xfz squirrelmail-1.4.4.tar.gz
[$OPENACS_SERVICE_NAME www]# mv squirrelmail-1.4.4 mail
[$OPENACS_SERVICE_NAME www]# cd mail/config
[$OPENACS_SERVICE_NAME www]# ./conf.pl

Now you are about to configure Squirrelmail. The configuration heavily depends on your setup, so no instructions are given here.

Profile your code

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

by Jade Rubick

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

There are several facilities for profiling your code in OpenACS. The first thing to do is to install the developer-support package and play around with it. But there is also support in the API for profiling your code: profiling your code using ds_profile

Upgrading an OpenACS 5.0.0 or greater installation

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

  • Upgrading a stock site.If you have no custom code, and your site is not in a CVS repository, upgrade with these steps:

    1. Go to /acs-admin/install/ and click "Upgrade Your System" in "Install from OpenACS Repository"

    2. Select all of the packages you want to upgrade and proceed

    3. After upgrade is complete, restart the server as indicated.

    4. If you are using locales other than en_US, go to acs-lang/admin and "Import all Messages" to load the new translated messages. Your local translations, if any, will take precedence over imported translations.

  • Upgrading a Custom or CVS site.If you have custom code, and your site is in a CVS repository, upgrade with these steps:

    1. Upgrade the file system for all packages in use.the section called “Upgrading the OpenACS files”

    2. Go to /acs-admin/install/ and click "Upgrade Your System" in "Install from local file system"

    3. Select all of the packages you want to upgrade and proceed

    4. After upgrade is complete, restart the server as indicated.

    5. If you are using locales other than en_US, go to acs-lang/admin and "Import all Messages" to load the new translated messages. Your local translations, if any, will take precedence over imported translations.

Object Identity

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

By Rafael H. Schloming

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

One of the major design features of OpenACS 5.2.3rc1 is the explicit representation of object identity. The reason I say "explicit representation" is because the concept of object identity has been around forever. It is inherent to our problem domain. Consider the example of 3.x style scoping. The 3.x data models use the triple (user_id, group_id, scope) to identify an object. In the 5.2.3rc1 data model this object is explicitly represented by a single party_id.

Another good example of this is can be found in the user groups data model. The 3.x user groups data model contains another example of an implied identity. Every mapping between a user and a group could have an arbitrary number of attached values (user_group_member_fields, etc.). In this case it is the pair (group_id, user_id) that implicitly refers to an object (the person's membership in a group). In the 5.2.3rc1 data model this object identity is made explicit by adding an integer primary key to the table that maps users to groups.

Coming from a purely relational world, this might seem slightly weird at first. The pair (group_id, user_id) is sufficient to uniquely identify the object in question, so why have the redundant integer primary key? If you take a closer look, it actually isn't quite so redundant. If you want to be able to use the object model's permissioning features, and generic attribute features on a table, you need an integer primary key for that table. This is because you can't really write a data model in oracle that uses more than one way to represent identity.

So, this apparently redundant primary key has saved us the trouble of duplicating the entire generic storage system for the special case of the user_group_map, and has saved us from implementing ad-hoc security instead of just using acs-permissions. This design choice is further validated by the fact that services like journals that weren't previously thought to be generic can in fact be generically applied to membership objects, thereby allowing us to eliminated membership state auditing columns that weren't even capable of fully tracking the history of membership state.

The design choice of explicitly representing object identity with an integer primary key that is derived from a globally unique sequence is the key to eliminating redundant code and replacing it with generic object level services.

Using .vuh files for pretty urls

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

.Vuh files are special cases of .tcl files, used for rewriting incoming urls. We can use a vuh file to prettify the uri for our notes. Instead of note-edit?item_id=495, we can use note/495. To do this, we will need a new .vuh file for redirection and we will need to change the referring links in note-list. First, add the vuh:

[$OPENACS_SERVICE_NAME $OPENACS_SERVICE_NAME]$ cd /var/lib/aolserver/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www
[$OPENACS_SERVICE_NAME www]$ emacs note.vuh

Paste this into the file:

# Transform requests of type: a/b
# into this internal request: A?c=b
# for example, note/495 > note-edit?item_id=496
# a: base name of this .vuh file
# b: from the request
# A: hard-coded
# C: hard-coded

set query [ad_conn url]

set request [string range $query [expr [string last / $query] + 1] end]

rp_form_put item_id $request

set internal_path "/packages/[ad_conn package_key]/www/note-edit"

rp_internal_redirect $internal_path

We parse the incoming request and treat everything after the final / as the item id. Note that this simple redirection will lose any additional query parameters passed in. Many OpenACS objects maintain a pretty-name, which is a unique, human-readable string, usually derived from title, which makes an even better 'pretty url' than a numeric id; this requires that your display page be able to look up an item based on pretty id.

We use rp_form_put to store the item id in the internal register that the next page is expecting, and then redirects the request in process internally (ie, without a browser refresh).

Next, modify note-list so that its link is of the new form.:

[$OPENACS_SERVICE_NAME www]$ emacs ../lib/note-edit.tcl
db_multirow  -extend {
	edit_url
	delete_url
    } notes notes_select {
	select ci.item_id,
	       n.title
        from   cr_items ci,
               mfp_notesx n
        where  n.revision_id = ci.live_revision
    } {
	set edit_url [export_vars -base "note/$item_id"]
	set delete_url [export_vars -base "note-delete" {item_id}]
    }

You may also need to change some of the links in your package. Commonly, you would use ad_conn package_url to build the URL. Otherwise, some of your links may be relative to the virtual directory (note/) instead of the actual directory that the note is being served from.

Next Page