0.00%
Search · Index

Weblog Page

Filtered by date 2008-02-17, 31 - 40 of 111 Postings (all, summary)

Add the new package to CVS

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

Before you do any more work, make sure that your work is protected by putting it all into cvs. The cvs add command is not recursive, so you'll have to traverse the directory tree manually and add as you go. (More on CVS)

[$OPENACS_SERVICE_NAME xml]$ cd ..
[$OPENACS_SERVICE_NAME doc]$ cd ..
[$OPENACS_SERVICE_NAME www]$ cd ..
[$OPENACS_SERVICE_NAME myfirstpackage]$ cd ..
[$OPENACS_SERVICE_NAME packages]$ cvs add myfirstpackage/
Directory /cvsroot/$OPENACS_SERVICE_NAME/packages/myfirstpackage added to the repository
[$OPENACS_SERVICE_NAME packages]$ cd myfirstpackage/
[$OPENACS_SERVICE_NAME myfirstpackage]$ cvs add www
Directory /cvsroot/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www added to the repository
[$OPENACS_SERVICE_NAME myfirstpackage]$ cd www
[$OPENACS_SERVICE_NAME www]$ cvs add doc
Directory /cvsroot/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/doc added to the repository
[$OPENACS_SERVICE_NAME www]$ cd doc
[$OPENACS_SERVICE_NAME doc]$ cvs add *
cvs add: cannot add special file `CVS'; skipping
cvs add: scheduling file `admin-guide.html' for addition
cvs add: scheduling file `bi01.html' for addition
cvs add: scheduling file `data-model.dia' for addition
cvs add: scheduling file `data-model.png' for addition
cvs add: scheduling file `design-config.html' for addition
cvs add: scheduling file `design-data-model.html' for addition
cvs add: scheduling file `design-future.html' for addition
cvs add: scheduling file `design-ui.html' for addition
cvs add: scheduling file `filename.html' for addition
cvs add: scheduling file `index.html' for addition
cvs add: scheduling file `page-map.dia' for addition
cvs add: scheduling file `page-map.png' for addition
cvs add: scheduling file `requirements-cases.html' for addition
cvs add: scheduling file `requirements-introduction.html' for addition
cvs add: scheduling file `requirements-overview.html' for addition
cvs add: scheduling file `requirements.html' for addition
cvs add: scheduling file `sample-data.html' for addition
cvs add: scheduling file `sample.png' for addition
cvs add: scheduling file `user-guide.html' for addition
cvs add: scheduling file `user-interface.dia' for addition
cvs add: scheduling file `user-interface.png' for addition
Directory /cvsroot/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/doc/xml added to the repository
cvs add: use 'cvs commit' to add these files permanently
[$OPENACS_SERVICE_NAME doc]$ cd xml
[$OPENACS_SERVICE_NAME xml]$ cvs add Makefile index.xml
cvs add: scheduling file `Makefile' for addition
cvs add: scheduling file `index.xml' for addition
cvs add: use 'cvs commit' to add these files permanently
[$OPENACS_SERVICE_NAME xml]$ cd ../../..
[$OPENACS_SERVICE_NAME myfirstpackage]$ cvs commit -m "new package"
cvs commit: Examining .
cvs commit: Examining www
cvs commit: Examining www/doc
cvs commit: Examining www/doc/xml
RCS file: /cvsroot/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/doc/admin-guide.html,v
done
Checking in www/doc/admin-guide.html;
/cvsroot/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/doc/admin-guide.html,v  <--  admin-guide.html
initial revision: 1.1
done
(many lines omitted)
[$OPENACS_SERVICE_NAME myfirstpackage]$

Figure10.1.Upgrading a local CVS repository

Upgrading a local CVS repository

Using Form Builder: building html forms dynamically

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

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

OpenACS has a form manager called ad_form. Ad_form has an adaptable UI. Error handling includes inline error reporting, and is customizable. However, ad_form can be tricky to use. In addition to this document, the ad_form api documentation is helpful.

Some elements have more than one choice, or can submit more than one value.

  1. Creating the form element.Populate a list of lists with values for the option list.

    set foo_options [db_list_of_lists foo_option_list "
        select foo,
               foo_id
          from foos
    "]
    

    The variable foo_options should resemble {{first foo} 1234} {{second foo} 1235}

    Within ad_form, set up the element to use this list:

    {foo:text(select)
             {label "Which Foo"}
             {options $foo_options}
         }

    This will result in a single name/value pair coming back in the submitted form. Handle this within the same ad_form structure, in the -new_data and -edit_data. In the example, it is available as $foo

See also the W3C spec for "The SELECT, OPTGROUP, and OPTION elements".

A situation you may run into often is where you want to pull in form items from a sub-category when the first category is selected. Ad_form makes this fairly easy to do. In the definition of your form element, include an html section

    {pm_task_id:integer(select),optional
         {label "Subject"}
         {options {$task_options}}
        {html {onChange "document.form_name.__refreshing_p.value='1';submit()"}}
        {value $pm_task_id}
    }
    

What this will do is set the value for pm_task_id and all the other form elements, and resubmit the form. If you then include a block that extends the form, you'll have the opportunity to add in subcategories:

    if {[exists_and_not_null pm_task_id]} {
    db_1row get_task_values { }
    ad_form -extend -name form_name -form { ... }
    

Note that you will get strange results when you try to set the values for the form. You'll need to set them explicitly in an -on_refresh section of your ad_form. In that section, you'll get the values from the database, and set the values as so:

    db_1row get_task_values { }
    template::element set_value form_name estimated_hours_work $estimated_hours_work
    

A good way to troubleshoot when you're using ad_form is to add the following code at the top of the .tcl page (thanks Jerry Asher):

ns_log notice it's my page!
set mypage [ns_getform]
if {[string equal "" $mypage]} {
    ns_log notice no form was submitted on my page
} else {
    ns_log notice the following form was submitted on my page
    ns_set print $mypage
}
    

Here are some tips for dealing with some of the form widgets:

Current widget

Here are some common errors and what to do when you encounter them:

This generally happens when there is an error in your query.

Install PAM Radius for use as external authentication

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 step by step guide is derived from the installation instructions which you can find at yourdomain.com/doc/acs-authentication/ext-auth-pam-install.html. It is build upon PAM 0.77 (tested) and does not work on RedHat Linux Enterprise 3 (using PAM 0.75). It makes use of the ns_pam module written by Mat Kovach. The instructions given in here do work with PAM LDAP accordingly and differences will be shown at the end of the file.

  1. Install ns_pam.Download and install ns_pam

    [root aolserver]# cd /usr/local/src/aolserver/
              [root aolserver]# wget http://braindamage.alal.com/software/ns_pam-0.1.tar.gz
              [root aolserver]# tar xvfz ns_pam-0.1.tar.gz
              [root aolserver]# cd ns_pam-0.1
              [root ns_pam-0.1]# make install INST=/usr/local/aolserver
              [root ns_pam-0.1]#
    cd /usr/local/src/aolserver/
    wget http://braindamage.alal.com/software/ns_pam-0.1.tar.gz
    tar xvfz ns_pam-0.1.tar.gz
    cd ns_pam-0.1
    make install INST=/usr/local/aolserver
    
    
  2. Configure ns_pam.Configure AOLserver for ns_pam

    To enable ns_pam in AOLServer you will first have to edit your config.tcl file and enable the loading of the ns_pam module and configure the aolservers pam configuration file.

    • Change config.tcl. Remove the # in front of ns_param nspam ${bindir}/nspam.so to enable the loading of the ns_pam module.

    • Change config.tcl. Replace pam_domain in the section ns/server/${server}/module/nspam with aolserver

    • Create /etc/pam.d/aolserver.

                    [root ns_pam]#cp /var/lib/aolserver/service0/packages/acs-core-docs/www/files/pam-aolserver.txt /etc/pam.d/aolserver
      
  3. Configure PAM Radius.Configure and install PAM Radius

    You have to make sure that pam_radius v.1.3.16 or higher is installed, otherwise you will have to install it.

    [root ns_pam]# cd /usr/local/src/
              [root src]# wget ftp://ftp.freeradius.org/pub/radius/pam_radius-1.3.16.tar
              [root src]# tar xvf pam_radius-1.3.16
              [root src]# cd pam_radius
              [root pam_radius]# make
              [root pam_radius]# cp pam_radius_auth.so /lib/security/
              [root pam_radius]#
    cd /usr/local/src
    wget ftp://ftp.freeradius.org/pub/radius/pam_radius-1.3.16.tar
    tar xvf pam_radius-1.3.16
    cd pam_radius
    make
    cp pam_radius_auth.so /lib/security/
    
    

    Next you have to add the configuration lines to your Radius configuration file (/etc/rddb/server). For AOLserver to be able to access this information you have to change the access rights to this file as well.

    [root pam_radius]# echo "radius.yourdomain.com:1645 your_radius_password >>/etc/rddb/server
              [root src]# chown service0:web /etc/rddb/server
    

Running a PostgreSQL database on another server

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

To run a database on a different machine than the webserver requires changes to the database configuration file and access control file, and to the OpenACS service's configuration file.

  • Edit the database configuration file, which in a Reference install is located at /usr/local/pgsql/data/postgresql.conf and change

    #tcpip_socket = false

    to

    tcpip_socket = true
  • Change the access control file for the database to permit specific remote clients to access. Access can be controlled ... (add notes from forum post)

  • Change the OpenACS service's configuration file to point to the remote database. Edit /var/lib/aolserver/$OPENACS_SERVICE_NAME/etc/config.tcl and change

    
          

    to

     

Diagnosing Performance Problems

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

  • Did performance problems happen overnight, or did they sneak up on you? Any clue what caused the performance problems (e.g. loading 20K users into .LRN)

  • Is the file system out of space? Is the machine swapping to disk constantly?

  • Isolating and solving database problems.

    • Without daily internal maintenance, most databases slowly degrade in performance. For PostGreSQL, see the section called “Vacuum Postgres nightly”. For Oracle, use exec dbms_stats.gather_schema_stats('SCHEMA_NAME') (Andrew Piskorski's Oracle notes).

    • You can track the exact amount of time each database query on a page takes:

      1. Go to Main Site : Site-Wide Administration : Install Software

      2. Click on "Install New Application" in "Install from OpenACS Repository"

      3. Choose "ACS Developer Support">

      4. After install is complete, restart the server.

      5. Browse to Developer Support, which is automatically mounted at /ds.

      6. Turn on Database statistics

      7. Browse directly to a slow page and click "Request Information" at the bottom of the page.

      8. This should return a list of database queries on the page, including the exact query (so it can be cut-paste into psql or oracle) and the time each query took.

        Figure6.8.Query Analysis example

        Query Analysis example

    • Identify a runaway Oracle query: first, use ps aux or top to get the UNIX process ID of a runaway Oracle process.

      Log in to SQL*Plus as the admin:

      [$OPENACS_SERVICE_NAME ~]$ svrmgrl
      
      Oracle Server Manager Release 3.1.7.0.0 - Production
      
      Copyright (c) 1997, 1999, Oracle Corporation.  All Rights Reserved.
      
      Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
      With the Partitioning option
      JServer Release 8.1.7.3.0 - Production
      
      SVRMGR> connect internal	
      Password:

      See all of the running queries, and match the UNIX PID:

      select p.spid  -- The UNIX PID
             ,s.sid  ,s.serial#
             ,p.username  as os_user
             ,s.username  ,s.status
             ,p.terminal  ,p.program
        from v$session s  ,v$process p
       where p.addr = s.paddr
       order by s.username ,p.spid ,s.sid ,s.serial# ;

      See the SQL behind the oracle processes:

      select s.username
             ,s.sid  ,s.serial#
             ,sql.sql_text
        from v$session s, v$sqltext sql
       where sql.address    = s.sql_address
         and sql.hash_value = s.sql_hash_value
       --and upper(s.username) like 'USERNAME%'
       order by s.username ,s.sid ,s.serial# ,sql.piece ;

      To kill a troubled process:

      alter system kill session 'SID,SERIAL#';  --substitute values for SID and SERIAL#

      (See Andrew Piskorski's Oracle notes)

    • Identify a runaway Postgres query. First, logging must be enabled in the database. This imposes a performance penalty and should not be done in normal operation.

      Edit the file postgresql.conf - its location depends on the PostGreSQL installation - and change

      #stats_command_string = false

      to

      stats_command_string = true

      Next, connect to postgres (psql service0 ) and select * from pg_stat_activity;. Typical output should look like:

        datid   |   datname   | procpid | usesysid | usename |  current_query
      ----------+-------------+---------+----------+---------+-----------------
       64344418 | openacs.org |   14122 |      101 | nsadmin | <IDLE>
       64344418 | openacs.org |   14123 |      101 | nsadmin |
                                                               delete
                                                               from acs_mail_lite_queue
                                                               where message_id = '2478608';
       64344418 | openacs.org |   14124 |      101 | nsadmin | <IDLE>
       64344418 | openacs.org |   14137 |      101 | nsadmin | <IDLE>
       64344418 | openacs.org |   14139 |      101 | nsadmin | <IDLE>
       64344418 | openacs.org |   14309 |      101 | nsadmin | <IDLE>
       64344418 | openacs.org |   14311 |      101 | nsadmin | <IDLE>
       64344418 | openacs.org |   14549 |      101 | nsadmin | <IDLE>
      (8 rows)
      openacs.org=>

The first task is to create an appropriate environment for finding out what is going on inside Oracle. Oracle provides Statspack, a package to monitor and save the state of the v$ performance views. These reports help finding severe problems by exposing summary data about the Oracle wait interface, executed queries. You'll find the installation instructions in $ORACLE_HOME/rdbms/admin/spdoc.txt. Follow the instructions carefully and take periodic snapshots, this way you'll be able to look at historical performance data.

Also turn on the timed_statistics in your init.ora file, so that Statspack reports (and all other Oracle reports) are timed, which makes them a lot more meaningful. The overhead of timing data is about 1% per Oracle Support information.

To be able to get a overview of how Oracle executes a particular query, install "autotrace". I usually follow the instructions here http://asktom.oracle.com/~tkyte/article1/autotrace.html.

The Oracle Cost Based optimizer is a piece of software that tries to find the "optimal" execution plan for a given SQL statement. For that it estimates the costs of running a SQL query in a particular way (by default up to 80.000 permutations are being tested in a Oracle 8i). To get an adequate cost estimate, the CBO needs to have adequate statistics. For that Oracle supplies the dbms_stats package.

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

Next Page