OpenACS Tutorial
OpenACS Tutorial
org
Day 1 ................................................................................................................................................................................................. 6
Objectives ..................................................................................................................................................................................... 6
Section 1 ........................................................................................................................................................................................... 6
Installation ........................................................................................................................................................................................ 6
What is Tcl?................................................................................................................................................................................... 8
Examples ....................................................................................................................................................................................... 8
Day 2 ............................................................................................................................................................................................... 14
Objectives: .................................................................................................................................................................................. 14
Section 2 ......................................................................................................................................................................................... 14
2
The To Do Application ................................................................................................................................................................. 14
Section 3 ......................................................................................................................................................................................... 19
Day 3 ............................................................................................................................................................................................... 21
Objectives ................................................................................................................................................................................... 21
Section 4 ......................................................................................................................................................................................... 21
Section 5 ......................................................................................................................................................................................... 43
Day 4 ............................................................................................................................................................................................... 46
Objectives: .................................................................................................................................................................................. 46
Section 6 ......................................................................................................................................................................................... 46
Section 7 ......................................................................................................................................................................................... 60
3
Permissions ................................................................................................................................................................................. 64
Conclusions ..................................................................................................................................................................................... 67
Appendix A...................................................................................................................................................................................... 68
APPENDIX B..................................................................................................................................................................................... 69
4
HOW THIS TUTORIAL IS ORGANIZED:
The tutorial is divided in 4 days (actually 3.5 days! although is known that many people finish it
sooner). Each day has two sections, in total there are 7 sections, each section will guide you through a
set of activities that will keep you learning OpenACS powerful software development features.
This tutorial was written with an OpenACS system running on an Ubuntu system as reference, so the
examples, default directories, screenshots, etc are aimed at an Ubuntu installation, but this do not limit
the reach of this tutorial to just Linux at the end of the tutorial you'll find a table with the important
directories listed both on Ubuntu and Windows, this can help you if you are using Windows as your base
system.
On each section you will find different elements, a typical section will consist of:
TRY IT!
Sometimes after describing an example your will find a: “Try It!” icon, , this
means to go ahead and try what we just did, we will usually give a url to try, this URL will be the
one of the reference system, something like: http://localhost:8000/helloworld if no url is present
then try again the last page we have been working on.
5
DAY 1
OBJECTIVES:
On this first day we want to get you up and running with an OpenACS system and have you to test how to create your very first
web pages using OpenACS.
SECTION 1
INSTALLATION
Follow these steps to install in Ubuntu, the installer is designed to work with Ubuntu 8.04 (Hardy Heron) but it can easily work on
newer releases with slight adjustments.
6
Follow these steps for Ubuntu 9.04:
3. Install Postgresql:
4. Install OpenACS:
You can find the most up to date instructions on the installer's Wiki Page:
http://openacs.org/xowiki/ubuntu
After you do this, please go to: http://localhost:8000 and fill out the form that will be on that page, this starts the installation
process of the actual service you will be using, it basically populates the database and sets everything up. After you do this, you
will need to restart your openacs service, do it this way:
Your normal Ubuntu user probably will not have write permission on the openacs directories, to correct this execute the next
commands:
WINDOWS INSTALLER:
You can download an all in one installer for Windows (XP and Vista 32 bits), this installer gets you set up with everything you
need.
If you are going to use the windows installer please keep this in mind:
7
It is going to install you 2 services, please use the one named “OpenACS”
Before you start the service, start the database server.
On Windows Vista you will need to start and stop the database server and the openacs service
with administrator privileges.
BASIC TCL
WHAT IS TCL?:
Originally from "Tool Command Language", Tcl is a highly flexible and easy to use programming language, it is the language we
will be using to work with OpenACS, so if you do not know it, now it is a great time to try and learn a little bit about it.
WHY TCL?:
The OpenACS toolkit is intended to be used with the AOLServer HTTP server, Tcl is AOLServer's built in scripting language so it is
natural to write the applications using Tcl.
MY FIRST PAGE
MY FIRST ADP:
ADP stands for AOLServer Dynamic Pages, they are pretty similar to a plain HTML page, but they include a few more tags and
extensions to able to handle dynamic information coming from the Tcl page.
The process of creating a user viewable dynamic page on OpenACS is actually divided on at least 2 files a Tcl file and one ADP file,
so for a page called "foo" you will have foo.tcl and foo.adp
The adp page is just an HTML page with the necessary markup to display dynamic data.
The Tcl page handles permissions, logic, calculations and provides the data to the ADP page.
The naming on these pages is really important, remember that tcl is a case sensitive language.
There is a third kind of page that could be involved, this third kind defines the database queries to be used, we will not
be using them on this tutorial.
EXAMPLES:
On the most basic level you can set a variable on the Tcl file and retrieve it on the ADP, this variable becomes a datasource for
the ADP page. Open up your favorite editor, we recommend Notepad++ (http://notepad-plus.sourceforge.net/uk/site.htm) on
Windows and Kate (found under the K Menu on KDE) or GEdit (found on the applications menu on Gnome) on a Linux system,
and follow these next examples.
8
HELLO WORLD:
helloworld.tcl:
helloworld.adp
Hey, @hello@
These simple two lines will generate your first page on OpenACS, you can create both files under the
/usr/share/openacs/www directory on your test site. And when you go to http://localhost:8000/helloworld (notice we are not
using any file extension) you will see something like this:
TRY IT!
http://localhost:8000/helloworld
CONNECTION INFORMATION:
On this example we will retrieve some basic connection information using the ad_conn procedure and will display it using a
simple unordered list.
connection.tcl
9
connection.adp
<ul>
<li>User Id: @user_id@</li>
<li>This URL: @url@ </li>
<li>This session: @session_id@ </li>
<li> IP Address: @IP@ </li>
</ul>
TRY IT!
http://localhost:8000/connection
If you notice, right now our last couple of pages do not look quite attractive and there is no title on the pages, they are very
simple pages and there is a really easy way to improve their look by adding a couple of lines to the adp page. Let’s improve our
last example by using the master tag at the start of the adp page.
<master>
<property name="doc(title)">Basic Connection Information </property>
<ul>
<li>User Id: @user_id@</li>
10
<li>This URL: @url@ </li>
<li>This session: @session_id@ </li>
<li> IP Address: @IP@ </li>
</ul>
You should see your page with the site template, the <master> tag tells OpenACS to include a header, footer, CSS and few more
things to your page, using this you can build sites with a unified look and feel. The property tag can set different page attributes,
in this case the title of the page.
TRY IT!
http://localhost:8000/connection
On your test site the page should look something like this:
The example will present a simple form for the user to fill out on one page and another to display the information. We will ask for
a name and an age and display it on the other page, the name, the age and if the person is an adult.
nameage.tcl
nothing here
11
nameage.adp
<master>
<form action="display-name-age">
<label for="name">Name</label>
<input type="text" name="name"/><br />
<label for="age">Age</label>
<input type="text" name="age"/><br />
<input type="submit">
</form>
display-name-age.tcl
ad_page_contract {
This page will display the name and age entered on the nameage page.
And this page is role is pretty much just to accept and validate the data before displaying it.
}{
age:integer
name
}
display-name-age.adp
<master>
<property name="title">Page for @name@</property>
<ul>
<li>Name: @name@</li>
<li>Age: @age@</li>
</ul>
12
TRY IT!
http://localhost:8000/nameage
13
DAY 2
OBJECTIVES:
Now that you know how to create pages, we want to learn on this day how to create a new package and how to work it inside
the system.
SECTION 2
MY FIRST PACKAGE
Even when using single pages and folders you can accomplish some neat stuff using OpenACS, the real power of the toolkit lies
with its packages system. To illustrate this we are going to build a simple package, a "To Do" List application and we will integrate
it as a new OpenACS package.
To let the users keep a list of items they need to accomplish before a given date.
The users must be able to see and modify only their own items.
The information we need to have on each item is:
Title
The description of the task
The due date
The item's current status
14
We need to give the users an interface to:
At a very basic level an OpenACS package is just a directory under the "packages" directory, you can probably find it on
/usr/share/openacs/packages 1, with the necessary files (Tcl/adp files) and a file with metadata about the package, but you do
not need to write it all yourself, the proper way to create the package is by going in to the "Package Manager" and selecting the
option to create a new package.
Then follow the link "Developer's Admin" and click on "Package Manager” (http://localhost:8000/acs-admin/apm ), you should
see a page listing all the packages installed on your system:
Scroll all the way down and you will find the link to "Create a new package"
1
On a Windows system: C:\aolserver\servers\openacs\packages
15
After following that link you will be presented with a page to enter the new package information, fill out the form with this
information:
Make sure to leave checked the "Write a package specification file for this package" check box.
Now after you click the "Create Package" button, the package will be created and installed on your system.
We can check that the package was created by going to the "packages" directory under your OpenACS installation,
/usr/share/openacs/packages, and looking for a directory named "todo".
Inside of it you will find the file todo.info and the following file structure:
16
Each file and directory has its own special purpose:
TODO.INFO:
This is the package specification file, inside of this file is the information that makes this directory a package inside of OpenACS,
the package name, parameters, dependencies, etc are defined here, usually you do not need to edit this file directly because it is
generated by the Package Manager when there is a change on it.
SQL:
This directory holds the data model for the package, the initial definition and changes to the data model are defined here. There
are two subdirectories here, oracle and postgresql, this is to allow the definition of database specific code, we will be using the
postgresql directory only on this tutorial.
TCL:
The Tcl directory holds the library files containing the definition of procedures related to your package.
WWW:
This are the public pages of your package, here you will define the pages that the user will interact with, a special service of
OpenACS called the request processor maps each request done to an instance of your package to this pages, so you can have
your package mounted at different URLs all working with the same code.
You will find this same basic structure on every OpenACS package.
Now that our package is created we will mount an instance of it on our "Site Map". Mount means that the pages at its www
directory can be accessed by the user.
Head to the administration page, http://localhost:8000/acs-admin/, under "Subsite Administration" go to your "Main Site",
http://localhost:8000/admin/ once there look under the "Advanced Features" section for the "Site Map" option.
Usually the URL for this page would be something like: http://localhost:8000/admin/site-map/
17
On this page you are able to see the different "site nodes" that exists on your site, packages, subsites, it is all here. Scroll to the
bottom and look for a little form that will let you mount your package, you can even give it any name you want at the moment of
mounting, but we will leave it blank this time.
After clicking "Mount Package" the new instance of your package will appear on the site map list, you can even browse to it! but,
it does not have any content yet.
You can create a simple "index.adp" or "index.html" file under the www directory of your package to test that it is working.
18
SECTION 3
TO DO DATA MODEL:
We will need to create at least one new table on the data base to store the information of our “To Do” list.
We need to store:
Run the next create table statement on your data base to create the "todo_item" table:
19
By saving it with that name you make sure that if you ever install the "todo" package in any OpenACS system, it will create the
todo_item table during the installation process; right now we will not use this file again, but will come in handy if you need to
install the package again on another system.
If you need help running the script, you can check the Appendix B at the end of this tutorial.
You can also specify a file with instructions to be executed when uninstalling the package, you save this
to:"/usr/share/openacs/packages/todo/sql/postgresql/todo-drop.sql" for now a simple drop table statement will work.
insert into todo_item values(acs_object_id_seq.nextval, 'My first item', 'My first item
description', 'p', 568);
insert into todo_item values(acs_object_id_seq.nextval, 'My second item', 'My second item
description', 'p', 568);
insert into todo_item values(acs_object_id_seq.nextval, 'Another item', ' item description', 'p',
568);
TRY IT!
On psql run both the create table statement and the test values. See appendix B on how to connect.
20
DAY 3
OBJECTIVES:
On your third with OpenACS we want you to be able to finish up the package we started yesterday, have all the user interface set
up and be able to use your "To Do" list application.
SECTION 4
Our next step will be to create the user interface of our package; we need to create the web pages that the user will be
interacting with. Please remember to save all of this pages you want the user to be able to access and the www directory of your
package, this is very important for the package to work properly.
You can do a simple HTML form and handle the user inputs yourself, but there is no fun in that. We will do it the OpenACS way,
by using the built in procedure ad_form that allows you to specify the form fields, the actions to be executed on different states
of the form, input validation, etc. You can read all about ad_form here: ad_form on the OpenACS API
Let’s do the ADP first, you will see the page is really simple:
todo-ae.adp
21
<master>
<property name="doc(title)">@page_title@</property>
<formtemplate id="todo_item_form"></formtemplate>
todo-ae.tcl
Let’s begin with the ad_page_contract for this page and declaring a few variables that we will use later.
ad_page_contract {
This page allows the users to add new items to their to do list or edit existing items.
}{
item_id:optional
}
If you notice we have declared item_id as optional parameter on this page, this allows to easily know if we are on edit mode or
adding new information, if item_id is present then we are editing an existing item, if not, we are adding a new one, ad_form is
smart enough to know this. We have used ad_conn to get information about the current user.
FORM WIDGETS:
Let’s continue by building our forms widgets, take a look at the next code snippet:
22
}
}
On the first line we are starting to use ad_form, the "name" parameter gives a name to our form, this way we can reference it on
the ADP or later on the same Tcl file. We also "exported" some variables to the ad_form, this takes the variables from our Tcl file
and includes them in the form as hidden fields.
After the "-form" switch we can start defining our form's widgets, we start with the "key" widget, this is usually a unique
identifier for the item we are adding or editing.
item_id:key
Our Next line defines a "text area" widget for our task's description.
The next one is a date widget, what this actually does is to insert 2 "selects" and one text field so the user can select the month
and day and input a year, this saves you the trouble of handling 3 different widgets for just one piece of information. We specify
also the format we wish to use with the date.
Our last widget is a "select" widget, this one contains an "options" list with all the possible values and labels this select will
present to the user, each option is a list of two elements {<label> <value> }
At this point if you go to this page in the browser you will see something like this:
23
If you notice the required label next to each label, this means that ad_form is doing some basic validation for you, if any of the
required fields has no value, then the form processing will stop and an error message will appear next to each field, like this:
To make a field optional, you just need to change a little bit the widgets definition, let’s do it for the Description field, because
sometimes the title says it all.
TRY IT!
http://localhost:8000/todo/todo-ae
The next section of our form is going to define what we will do with new data, when we are inserting a new item to our to do list.
This switch's name is "new_data" this block contains code that will be executed only when we are inserting a new item, usually
we use it with database statements to store the information.
24
-new_data {
db_dml insert_item "
insert into todo_item
(item_id, title, description, status, due_date, owner_id)
values
(:item_id, :title, :description, :status, to_date(:due_date,'YYYY MM DD HH24 MI SS'),
:user_id)
"
}
Note here, for the "due date" value we used the "to_date" function, the date widget creates a list with the year, month, day,
hour, minute and seconds fields, so we can't use it directly.
Try it now, and you will be able to insert a new item in the database, you will need to do a query on the database prompt to see
the results, for example:
25
To test your page go to: http://localhost:8000/todo/todo-ae
TRY IT!
At this point you have a page that effectively stores a new item on the data base, you can still do much more, maybe you would
like to check the next section to learn how to display your information, or continue reading this section to learn how to edit the
an existing item.
To edit an existing item we need to do 2 things, first, get all the information about the existing item and fill the values of the
form's widgets, and then update the database when the form is submitted.
We get the existing information by using the “select_query” switch, on the body of this block we do a query that returns
a single row with all the information of user editable fields, this values will be transferred to the form widgets. This
switch can only be used if the form contains a "key" element. This is the select query for our form:
-select_query {
select title,
description,
to_char(due_date, 'YYYY MM DD') as due_date,
status
from todo_item
where item_id = :item_id
and owner_id = :user_id
}
Get the item_id from the database (like we did a couple of sections back) and append it to the form page url, now you
should be able to see the fields filled with data when you enter:
26
2. UPDATING THE ITEM
Now that we have the data on the form we can edit it, but we also need to able to store the edited information, for this
we use another switch, the “edit_data” switch, it works pretty much in the same way as new_data does, the only
difference is that it runs only when we are updating an item.
-edit_data {
db_dml update_item "
update todo_item
set title = :title,
description =:description,
status = :status,
due_date = to_date(:due_date,'YYYY MM DD'),
where item_id = :item_id and owner_id = :user_id "
}
Remember to add the where clause to make sure you are updating only the item you want to update, in this case we
added a couple of other checks to make sure we are updating only items that belong to us.
AFTER SUBMIT:
Sometimes, after processing correctly the form you may wish to do some more calculations or simply redirect the user to another
page, we can easily do this in the “after_submit” block. Here we will use it to direct the user to this package's index page.
-after_submit {
ad_returnredirect index
ad_script_abort
}
27
FORM MODES:
At last in this section we will add one more trick to our form, a display mode, this way you can present the item's information in a
non-editable way to the user.
To do this you just need to add one more switch to the ad_form call, like this:
We added the form_mode variable as part of the ad_page_contract, with a default value of "edit", it can also be “display” to
display the information in a non-editable way.
todo-ae.tcl
The complete Tcl file for todo-ae.tcl looks like this:
ad_page_contract {
This page allows the users to add new items to their to do list or edit existing items.
}{
item_id:optional
{due_date ""}
{form_mode "edit" }
}
28
description,
to_char(due_date, 'YYYY MM DD') as due_date,
status
from todo_item
where item_id = :item_id
and owner_id = :user_id
} -new_data {
db_dml insert_item "
insert into todo_item
(item_id, title, description, status, due_date, owner_id, package_id)
values
(:item_id, :title, :description, :status, to_date(:due_date,'YYYY MM DD'), :user_id)”
} -edit_data {
db_dml update_item "
update todo_item
set title = :title,
description =:description,
status = :status,
due_date = to_date(:due_date,'YYYY MM DD'),
where item_id = :item_id and owner_id = :user_id "
} -after_submit {
ad_returnredirect index
ad_script_abort
}
INDEX PAGE:
We will create the index page as a list of all the “To do” items we have created.
To create the list we will use the List Builder, a powerful tool from the OpenACS templating system, it allows us to easily create
lists with a great deal of functionalities. The main procedure of the list builder is template::list::create we can do many things
with this procedure, but right now we will limit ourselves to the basics.
29
The way to use the list builders is similar to the way we used ad_form in the previous section, it is just one call to a procedure
and different switches affect the outcome and behavior of the list, also, associated to the list there is a multirow, we will create
this by using the db_multirow function from the database API.
The ADP page is very simple, so Let’s get that out of the way:
index.adp
<master>
<property name="doc(title)">@title@</property>
<listtemplate name="todo_list"></listtemplate>
The ad_page_contract:
Right now we do not have anything on our ad_page_contract, but as we build functionality into the list we will add one or two
more things.
ad_page_contract {
This page will display a list of to do items belonging to the current user.
}{
}
Defining variables:
We will define a couple of variables we will use later, this are just some context variables for the query and the adp page.
The first line calls the template::list::create procedure, names our list, the name is necessary to be able to call it on the ADP page,
we also define here the name of the multirow that will be the source of the list elements.
30
Defining the elements:
We define the elements of the list inside the "elements" switch, each element follows the same basic format:
element_name {
element_option option_value
element_option option_value
}
There are many options and you do not always need to use them, you can read all about them here:
template::list::element::create on the OpenACS API.
A simple:
title {
would work, because the list builder knows you want to output the value of the title field on each row returned on the multirow
so you do not need any additional options, but we can do it better than that.
label "Task"
To make the content of the element, in this case the task's title, link to another page use the link_url_col option
link_html {title "You can create a title text for the link here" }
Our complete element for the task's title looks like this:
title {
label "Task"
link_url_col item_url
31
link_html {title "Click to view this item details" }
}
The rest of the elements can be defined in a similar way, this would be the complete list builder call for this list:
32
But we are not done yet with the list, we need to define its data source, the multirow, for this we will use the db_multirow
procedure.
The db_multirow proc creates a multirow object, this multirow object will contain the results of the database query we will pass
it.
33
Our db_multirow is defined like this:
We have extended our multirow with the item_url, delete_url, cancel_url, completed_url and status_text variables to make the
list more useful, we will need to define this in the optional Tcl code block.
The item url will be pretty simple, remember the little "mode" fix we did to the add and edit page just before starting with the
list? well, we did it thinking of this, now all we need to do is to tell the add and edit page to show us the information on "display"
mode.
The export_vars procedure takes care of creating the query string for the URL, it only needs a list of variables it will include on the
URL.
The status we store on the database is just one character, so it is not very helpful to display that, we will create a new variable to
hold a better description for us. The tcl's switch statement comes handy for that.
switch $status {
p {set status_text "Pending"}
c {set status_text "Completed"}
x {set status_text "Canceled"}
default {set status_text "Unknown" }
}
The delete, completed and cancel links are done pretty much the same way, they link to another page that does the actual
update and then we return to the list. We will create these pages later, but right now we can just create the links to them.
34
set return_url [util_get_current_url]
set delete_url "todo-delete?[export_vars -url {item_id return_url}]"
if { $status != "c" } {
set new_status completed
set completed_url "todo-update-item?[export_vars -url {item_id new_status return_url}]"
}
if { $status != "x" } {
set new_status canceled
set cancel_url "todo-update-item?[export_vars -url {item_id new_status return_url}]"
}
If everything has worked so far, you should see a list that looks like this:
TRY IT!
at: http://localhost:8000/todo/
At this point you have a pretty functional list with your "To Do" items on it, but we can improve it a little bit by adding the ability
to sort each column.
35
Adding Sorting:
To add the ability to sort the different columns we will need to make just a few changes, this are:
We add a new optional variable, this is the variable that will hold on what column we are doing the sorting, so now our
ad_page_contract looks like this:
ad_page_contract {
This page will display a list of to do items belonging to the current user.
}{
orderby:optional
}
Each element on the orderby block follows the same format: <element name> { <order by clause> } the list builder takes
care of knowing when to do ascending or descending order.
-orderby {
title {orderby title}
due_date_pretty {orderby due_date}
status_text {orderby status}
creation_date_pretty {orderby creation_date}
}
You can read more about the orderby block here: orderby on the OpenACS API.
We do this by checking that the orderby variable of the ad_page_contract it is not empty, if it is not empty then
it means we are sorting a column, we will get the clause into a variable that we will later pass to our query, in
the case the orderby variable is empty we will set a default order.
if {[exists_and_not_null orderby]} {
set orderby_clause "ORDER BY [template::list::orderby_clause -name todo_list]"
36
} else {
set orderby_clause "ORDER BY due_date asc"
}
We will simply add the orderby_clause variable to our query, so our db_multirow now looks like this:
After all of this is done you will notice that each column header is now a link, by clicking on those links you will sort the list by that
column, clicking again the same link will resort the list on a different order.
To do this we only need to add another block to our list builder statement, we need the "actions" block. The format for each
action is pretty simple, but with many actions it may get hard to read. The format is:
37
-actions {
<Action label> <Action URL> <Action title text>
<Action label> <Action URL> <Action title text>
}
-actions {
"Add New Task" "todo-ae" "Click here to add a new item to the list"
}
The action button will appear at the top of the list and takes you to the "todo-ae" page, when position the cursor on the button
the text "click here to add a new item to the list" will appear momentarily.
At this point you have a basic, but functional, to do list application, built using a couple of the tools OpenACS provides. Next is the
final index.tcl file with all of the functionality we built in the last few sections.
index.tcl
This is the complete index.tcl file for our package.
ad_page_contract {
This page will display a list of to do items belonging to the current user.
}{
orderby:optional
}
38
set page_title "My To Do List"
set user_id [ad_conn user_id]
39
status_text {orderby status}
creation_date_pretty {orderby creation_date}
} -actions {
"Add New Task" "todo-ae" "Click here to add a new item to the list"
}
if {[exists_and_not_null orderby]} {
set orderby_clause "ORDER BY [template::list::orderby_clause -name todo_list]"
} else {
set orderby_clause "ORDER BY due_date asc"
}
"{
switch $status {
p {set status_text "Pending"}
c {set status_text "Completed"}
x {set status_text "Canceled"}
default {set status_text "Unknown" }
}
40
if { $status != "c" } {
set new_status completed
set completed_url "todo-update-item?[export_vars -url {item_id new_status return_url}]"
}
if { $status != "x" } {
set new_status canceled
set cancel_url "todo-update-item?[export_vars -url {item_id new_status return_url}]"
}
}
TRY IT!
http://localhost:8000/todo/
DELETING ITEMS:
We will create a very simple page for deleting the items, it will take an item id, delete it and return the user to the previous page.
We only need the Tcl file for this page.
todo-delete.tcl
ad_page_contract {
This page will delete an item from the todo list package and then return the user
to the specified return URL.
}{
item_id
return_url
}
41
UPDATING THE STATUS:
The page for updating the items' status works in pretty much the same way, just the dml operation is different this time.
ad_page_contract {
This page will update an item's status from the todo list package and then return the user
to the specified return URL.
}{
item_id
new_status
return_url
}
ad_returnredirect $return_url
42
SECTION 5
OpenACS is not limited to user visible pages, in addition each package can define their own procedures. Remember the /tcl
directory on our package? We will use that now.
Create a new file called todo-procs.tcl in /usr/share/openacs/packages/todo/tcl/ and paste the following code there.
todo.tcl
ad_library {
Procs for the To Do list package.
}
This procedure receives a status code and returns the corresponding label.
@param status is the status code received.
@return Label corresponding to the status code or Unknown if the status code is not valid
} {
switch $status {
p{
set status_text "Pending"
}
c{
set status_text "Completed"
}
x{
43
set status_text "Canceled"
}
default {
set status_text "Unknown"
}
}
return $status_text
}
}{
set status_text [string tolower $status_text]
switch $status_text {
"pending" {
set status_code p
}
"completed" {
set status_code c
}
"canceled" {
set status_code x
}
default {
set status_code p
}
}
return $status_code
}
The first few lines define this tcl file as a new library and provide documentation for its purpose by using the ad_library
procedure. Next we define a new namespace for our procedures, this way we make sure any procedure we define here will not
have conflicts with another package's procedure with the same name, but different namespace.
Next we defined two different procedures to encapsulate some code we did earlier, we have set their scope as public so any
package can use them.
44
But we cannot still use the package, we will need to tell package manager that there is a new procedure file and that it needs to
load it. Go to the package manager http://localhost:8000/acs-admin/apm and look for our package on the list, and click on
"reload changed"
Now the procedures have been loaded into the system and we can use them, it is a good idea to mark the file to be watched if
we are going to be working on it.
Now you can replace the code on the index.tcl page and todo-update-status.tcl for a call to the corresponding procedures, like
this. Find the correct place and replace it.
on index.tcl:
on todo-status-update.tcl:
TRY IT!
45
DAY 4
OBJECTIVES:
On your fourth day working with OpenACS we want you to learn about the ACS Objects system and how your packages and items
can be a part of it so your applications can take full advantage of the powerful services and modules that OpenACS presents.
We will learn integrating OpenACS Object s into your "To Do" list application.
SECTION 6
ACS OBJECTS
OpenACS gives us the option to use its objects system to integrate our application to the system and be able to take advantage of
many of the different services that OpenACS provides.
The motivation behind the existence and use of ACS objects is the fact that while developing OpenACS applications and modules
you will find that many of them share some common characteristics, and needs, such as:
Using the ACS Objects it is simple but it requires you to take some extra steps while defining your data model, this is what you
need to do.
46
2. Define a table for your application
• This table is for all the application specific information about your object
• This table's primary key column must reference the object_id in the acs_objects table that is the central table
that manages the ACS objects
3. Define application specific procedures
• This can be either tcl procedures or stored procedures on the database
4. Make your application "object aware"
• With just a few changes we can make our "To Do" application to be aware of the existence of our objects
Let’s start now to modify our "todo" application to use ACS Objects and integrate it better with OpenACS.
USING OBJECTS:
One of the first things we’ll do is to drop our current table, execute this command on psql:
todo-create.sql
This file is the one that will be loaded by the system when we install our package on another OpenACS installation, in this case
since the package is already installed, we will define and then load it manually to the database.
The first section of the files defines our new table, if you notice we have eliminated many of the previous fields we were using,
that is because the objects system will keep track of the package id, creation user, creation date, etc. Also notice that our item_id
now points to the acs_objects table.
The second part is a little bit more interesting, here we will create and execute a function that will register our new object type
and will define it as using our table "todo_item" as the table that holds its information.
47
create table todo_item (
item_id integer,
title varchar(500),
description text,
status char(1),
due_date date default now(),
constraint todo_item_pk primary key (item_id),
constraint todo_item_fk foreign key (item_id) references acs_objects(object_id)
);
-- Here is the function that will register a new object type for our package, in order the
parameters are:
-- Name of the new object_type
-- "Pretty" name of the object type, this is an human readable name for our new type
-- Pretty plural, our pretty name in plural form
-- Supertype, this object parent type, usually it is acs_object for a new kind of object
-- Table name, the name that holds the data model for this object
-- ID column, the column of our data model table primary key
-- Package Name, our package's name
-- The last 3 parameters are not relevant at this time
return 0;
end;' language 'plpgsql';
48
select inline_0 ();
\i packages/todo/sql/postgresql/todo-package.sql
The last line simply calls a new sql file where we will define a couple of functions to deal with our new object and the acs_objects
table:
todo-package.sql
We will define two Postgresql functions to handle our objects, one to create a new object and one to delete it. Those
functions must exist in order to comply with the OpenACS standards and expect ACS Object system behavior.
todo_obj_item__new: This function takes as parameters all the fields that we need to handle our to do list items, it
uses some of them to create a new object and just leaves the application specific ones to be added to the todo_item table we
defined earlier. Noticed that we must tell the function that creates the new object what type of object are we creating.
todo_obj_item__delete: This function takes just one argument, the id of the item we want to delete, it first removes it
from our applications data model and then calls a function to remove the object from the system, do not try to simply delete the
object from acs_objects since this function exists to ensure that the objects are deleted in a safe way.
v_id integer;
v_type varchar;
BEGIN
49
v_type := ''todo_item'';
v_id := acs_object__new(
p_item_id,
v_type,
now(),
p_creation_user,
p_creation_ip,
p_context_id::Integer,
true
);
return v_id;
END;
' LANGUAGE 'plpgsql';
TRY IT!
See appendix B for instructions on how to access your database.
50
CHANGES TO THE PACKAGES PAGES:
We have modified our data model to use objects, but we need to make sure our application knows this and uses them, in order
to do this we are going to make some small changes in our user visible pages.
We will start the changes on the "todo-ae" page, the changes we need to make are to ensure that:
When retrieving information, either for editing or displaying the item's details, we need to change the "select_query" block on
the ad_form definition, we are simple going to rewrite the query to be like this:
select todo.title,
todo.description,
to_char(todo.due_date, 'YYYY MM DD') as due_date,
todo.status
from todo_item todo,
acs_objects obj
where todo.item_id = :item_id
and obj.object_id = todo.item_id
and obj.creation_user = :user_id
and obj.context_id = :package_id
As you can see the main change here was getting some of the information out of the acs_objects table, one of the fields included
on the where clause, context_id tells in what package instance was the item created and creation_user field tells us who created
this item, we use them to be sure we are displaying information that belongs to the correct instance and the appropriate user.
Remember when we used the site map to mount our first package? What we did there is to create a new instance of the package
“todo”, you can go there and mount as many instances you want, for example you could mount an instance named “todo-work”
and another “todo-school” to keep track of your work and school activities on a separate instance, OpenACS will handle the logic
behind creating the URL, permissions, users, etc.
51
WORKING WITH MULTIPLE INSTANCES:
To create multiple instances of the same package follow these steps:
2. Following the same process we did when mounting our package for the first time, select “To Do List” from the
dropdown box menu and give this new instance a new name, we called our new instance “todo-work”
3. Now click “Mount Package”, the page will reload and you will have a new instance of our “To Do List” package mounted
on, http://localhost:8000/todo-work
52
4. You can repeat this process and mount as many instances as you like.
5. If you click on the “Permissions” link for any instance, you can see and manage who has what privilege over this
particular instance.
53
6. Click on “Don’t inherit Permission’s from Main Site” to remove the automatic permission’s this instance has obtained
from the main site. In this case it will remove the public’s permission to read anything on this instance.
54
Changes Adding New Data:
The main changes here will be replacing our insert query for a call to the procedure that creates a new object of type todo_item.
All of these changes take place inside the "new_data" block of the ad_form definition.
-new_data {
set context_id [ad_conn package_id]
set new_object_id [ db_exec_plsql do_insert {
select todo_item__new (
:item_id,
:title,
:description,
:status,
:user_id,
to_date(:due_date, 'YYYY MM DD HH24 MI SS'),
:ip_address,
:context_id
);
}]
}
As you can see we defined a "context_id" variable, this is just for sakes of making the function call clearer on what parameter we
are passing, our insert statement was replaced by a function call using db_exec_plsql (which is special for calling database
functions).
-edit_data {
# update the information on our table
db_dml todo_item_update "
update todo_item
set title= :title,
description = :description,
status = :status,
due_date = to_date(:due_date, 'YYYY MM DD HH24 MI SS')
where item_id = :item_id"
55
# update the last modified information on the object
db_exec_plsql to_do_list_obj_item_object_update {
select acs_object__update_last_modified(:item_id,:user_id,:ip_address)
}
}
To edit our item's information we just issued an updated statement on the database, for the object we used a call to the
acs_object__update_last_modified function that will record when the last change was made, on what IP address and by which
user.
56
The complete todo-ae.tcl file
ad_page_contract {
This page allows the users to add new items to their to do list or edit existing items.
}{
item_id:optional
{form_mode "edit" }
}
57
set new_object_id [ db_exec_plsql do_insert {
select todo_item__new (
:item_id,
:title,
:description,
:status,
:user_id,
to_date(:due_date, 'YYYY MM DD HH24 MI SS'),
:ip_address,
:context_id
);
}]
} -edit_data {
} -after_submit {
ad_returnredirect index
ad_script_abort
}
The ADP file does not need any changes at this point.
58
INDEX PAGE WITH OBJECTS:
The index page that just presents a list of our todo items and allows us to add more, also have to be modified a little to make it
work with our new data model.
select todo.item_id,
todo.title,
todo.due_date,
to_char(todo.due_date, 'Month DD YYYY ') as due_date_pretty,
obj.creation_date,
to_char(obj.creation_date, 'Month DD YYYY ') as creation_date_pretty,
todo.status
from todo_item todo, acs_objects obj
where obj.context_id = :package_id
and obj.creation_user = :user_id
and obj.object_id = todo.item_id
$orderby_clause
Notice how we pulled the creation date out of the object_id and made sure the context of the objects we are displaying is inside
our current package.
We will need to do a couple of adjustments to the orderby statements, nothing major, we will just make sure that we are
ordering by the correct fields.
-orderby {
title {orderby todo.title}
due_date_pretty {orderby todo.due_date}
status_text {orderby todo.status}
creation_date_pretty {orderby obj.creation_date}
}
And those are all the changes we need on the user visible pages to make them work with objects system, but right now we just
made sure we can retain the same functionality.
59
TRY IT!
http://localhost:8000/todo/
What can we gain from using the ACS Objects? We will explore that in the next section.
SECTION 7
COMMENTS:
What we intend to do is to add the ability to store comments on a task, we could simply create our own data model and store our
comments, but the real benefit of using OpenACS is taking advantage of all of the great modules and applications that already
have been developed and tested with it. For our comments functionality we are going to use the "General Comments" service,
that allows us to tie any ACS Object with comments and we'll do all of this in just a few lines of code.
But first, let’s check if the "General Comments" package is already installed, to do this go to: http://localhost:8000/acs-
admin/install/ and under "Installed Packages" look for General Comments.
60
If you do have it installed, continue to the next section “Adding Comments to our Items”, if not, the next steps will show you
how to install it.
3. Now click on the "Install or Upgrade Checked Applications" button near the bottom of the page.
4. A confirmation page will come up click on the "Install Above Package" to begin the installation process.
5. Wait for the packages to be installed and on the confirmation page click the link to restart your server.
61
6. Depending on your set up you may need to restart your service manually.
On Ubuntu run this command on a terminal:
Sudo /etc/init.d/openacs restart
On Windows:
Go to the Start Menu
Look for the “win32-OpenACS” programs menu
Click on “Stop OpenACS” and then
Click on “Start OpenACS”
We will display the comments when we are viewing just one single item on "display" mode, the comments will be hidden on
"Edit" mode, we need to add link to add comments and display the content of the comments. Add the following code of block
after the ad_form definition on the file "todo-ae.tcl"
First we are checking that this is only executed on "display" mode, so we will have the comments only when reviewing
the item.
Next we are building or "Add Comment" link, notice:
o We use the [general_comments_package_url] proc to get the URL to the general comments package,
hardcoded URLs are not good idea on a system as flexible as OpenACS.
o The use of export_vars to pass a few variables that will be added to the link, those variables are the item_id (as
object_id) and return_url so we can return to this page after the comment is created.
Finally we are getting all the comments on this item, already to be displayed by using the
general_comments_get_comments procedure.
o We will display this on our adp.
We already got the information we want on the tcl file, now we need to display it on the adp.
62
Add this code to the "todo-ae.adp" file
Here we are again checking that we are on display mode, if we are, then we build and display the "Add a comment" link and we
will display all of the comments related to this object.
Notice how we are getting the comments_html variable content, @comments_html;noquote@, the use of the noquote modifier
is to prevent OpenACS from cleaning up and displaying the html markup instead of letting it pass to the browser to display as we
intended. If you do not use it you will get something like:
TRY IT!
Try it now and your results should be similar to the next screenshot.
63
PERMISSIONS:
This is probably one of the major benefits you may get out of using ACS Objects, easy integration with OpenACS
permissions system. We are going to set up a little test case to see the permission system in action
First, add a new user to your system, you can do this by going to: http://localhost:8000/acs-admin/users/user-add and filling out
the new users form, after hitting submit a new user will be created.
We will modify our code so only the user creating each item is able to read and modify them.
On the todo-ae.tcl file we will modify the "new_data" section on the ad_form, add this code after the object has been created,
but before closing the new_data block.
#The first command on this last block of code will remove all the permissions the object has
inherited from its "ancestors" (the package, the site, etc), the next command grants the "admin"
permission to the current user and finally, we allow the current user to be able to add
comments to this object.
Now add this permissions verification at the top of the page, just after the ad_page_contract.
This will require the user to have the specified permissions before attempting to do anything, if the form is in display mode then
the "read" permission is required, if the form is in edit mode then the "write" permission is required. The permissions system
opens a world of possibilities.
64
Try it by trying to read or edit the objects with different users, you will see that when you are trying to read something to which
you do not have permissions the system will detect it and stop you with an error message:
TRY IT!
todo-delete.tcl
ad_page_contract {
This page will delete, an item from the todo list package and then return the user
to the specified return URL.
}{
item_id
return_url
}
ad_returnredirect $return_url
Notice how we have replaced the delete statement for a call to the todo_item_delete stored procedure, this way we ensure that
both the item and the object are deleted correctly. We are also checking that the user has the correct permissions to delete this
item.
65
todo-update-status.tcl
ad_page_contract {
This page will update an item's status from the todo list package and then return the user
to the specified return url.
}{
item_id
new_status
return_url
}
ad_returnredirect $return_url
This page has not changed much, we are just making sure the user has the required permissions to carry out the operation. We
waited to update these pages so we could take advantage of the user permissions checking.
And that is all for objects for now, they are a rich tool and their integration with the other OpenACS services allows you to add a
great deal of functionality in an easy and organized way.
66
CONCLUSIONS
Still we need at OpenACS quick tutorial for important areas such as xql, libraries, advanced examples of
ad_form & list builder, usage of the content repository, more advanced ACS Objects examples, etc, so
hopefully more people will be interested to develop new quick tutorials for OpenACS and its features.
2009
67
APPENDIX A
OPENACS ON WINDOWS:
Thanks to the flexibility of OpenACS and the software it is built upon it is possible to run an OpenACS system on different
environments, including Microsoft Windows © at the moment of this writing the Windows installer works with Windows XP and
Vista, both on the 32 bits version.
Next is a table detailing where to find the different directories we discussed on the tutorial, this paths are valid if you used the
windows installer.
68
APPENDIX B
There are many ways to connect to your OpenACS database on both Windows and Linux, we will deal here exclusively with how
to connect to the database using the built-in psql command line utility always included on a Postgresql installation.
CONNECTING ON WINDOWS:
69
CONNECTING ON UBUNTU:
70