- I OpenACS For Everyone
- I.1 High level information: What is OpenACS?
- I.1.1 Overview
- I.1.2 OpenACS Release Notes
- I.2 OpenACS: robust web development framework
- I.2.1 Introduction
- I.2.2 Basic infrastructure
- I.2.3 Advanced infrastructure
- I.2.4 Domain level tools
- I.1 High level information: What is OpenACS?
- II Administrator's Guide
- II.2 Installation Overview
- II.2.1 Basic Steps
- II.2.2 Prerequisite Software
- II.3 Complete Installation
- II.3.1 Install a Unix-like system and supporting software
- II.3.2 Install Oracle 10g XE on debian
- II.3.2.1 Install Oracle 8.1.7
- II.3.3 Install PostgreSQL
- II.3.4 Install AOLserver 4
- II.3.5 Quick Install of OpenACS
- II.3.5.1 Complex Install OpenACS 5.3
- II.3.6 OpenACS Installation Guide for Windows2000
- II.3.7 OpenACS Installation Guide for Mac OS X
- II.4 Configuring a new OpenACS Site
- II.4.1 Installing OpenACS packages
- II.4.2 Mounting OpenACS packages
- II.4.3 Configuring an OpenACS package
- II.4.4 Setting Permissions on an OpenACS package
- II.4.5 How Do I?
- II.4.6 Configure OpenACS look and feel with templates
- II.5 Upgrading
- II.5.1 Overview
- II.5.2 Upgrading 4.5 or higher to 4.6.3
- II.5.3 Upgrading OpenACS 4.6.3 to 5.0
- II.5.4 Upgrading an OpenACS 5.0.0 or greater installation
- II.5.5 Upgrading the OpenACS files
- II.5.6 Upgrading Platform components
- II.6 Production Environments
- II.6.1 Starting and Stopping an OpenACS instance.
- II.6.2 AOLserver keepalive with inittab
- II.6.3 Running multiple services on one machine
- II.6.4 High Availability/High Performance Configurations
- II.6.5 Staged Deployment for Production Networks
- II.6.6 Installing SSL Support for an OpenACS service
- II.6.7 Set up Log Analysis Reports
- II.6.8 External uptime validation
- II.6.9 Diagnosing Performance Problems
- II.7 Database Management
- II.7.1 Running a PostgreSQL database on another server
- II.7.2 Deleting a tablespace
- II.7.3 Vacuum Postgres nightly
- II.8 Backup and Recovery
- II.8.1 Backup Strategy
- II.8.2 Manual backup and recovery
- II.8.3 Automated Backup
- II.8.4 Using CVS for backup-recovery
- II.A Install Red Hat 8/9
- II.B Install additional supporting software
- II.B.1 Unpack the OpenACS tarball
- II.B.2 Initialize CVS (OPTIONAL)
- II.B.3 Add PSGML commands to emacs init file (OPTIONAL)
- II.B.4 Install Daemontools (OPTIONAL)
- II.B.5 Install qmail (OPTIONAL)
- II.B.6 Install Analog web file analyzer
- II.B.7 Install nspam
- II.B.8 Install Full Text Search
- II.B.9 Install Full Text Search using Tsearch2
- II.B.10 Install Full Text Search using OpenFTS (deprecated see tsearch2)
- II.B.11 Install nsopenssl
- II.B.12 Install tclwebtest.
- II.B.13 Install PHP for use in AOLserver
- II.B.14 Install Squirrelmail for use as a webmail system for OpenACS
- II.B.15 Install PAM Radius for use as external authentication
- II.B.16 Install LDAP for use as external authentication
- II.B.17 Install AOLserver 3.3oacs1
- II.C Credits
- II.C.1 Where did this document come from?
- II.C.2 Linux Install Guides
- II.C.3 Security Information
- II.C.4 Resources
- II.2 Installation Overview
- III For OpenACS Package Developers
- III.9 Development Tutorial
- III.9.1 Creating an Application Package
- III.9.2 Setting Up Database Objects
- III.9.3 Creating Web Pages
- III.9.4 Debugging and Automated Testing
- III.10 Advanced Topics
- III.10.1 Write the Requirements and Design Specs
- III.10.2 Add the new package to CVS
- III.10.3 OpenACS Edit This Page Templates
- III.10.4 Adding Comments
- III.10.5 Admin Pages
- III.10.6 Categories
- III.10.7 Profile your code
- III.10.8 Prepare the package for distribution.
- III.10.9 Distributing upgrades of your package
- III.10.10 Notifications
- III.10.11 Hierarchical data
- III.10.12 Using .vuh files for pretty urls
- III.10.13 Laying out a page with CSS instead of tables
- III.10.14 Sending HTML email from your application
- III.10.15 Basic Caching
- III.10.16 Scheduled Procedures
- III.10.17 Enabling WYSIWYG
- III.10.18 Adding in parameters for your package
- III.10.19 Writing upgrade scripts
- III.10.20 Connect to a second database
- III.10.21 Future Topics
- III.11 Development Reference
- III.11.1 OpenACS Packages
- III.11.2 OpenACS Data Models and the Object System
- III.11.3 The Request Processor
- III.11.4 The OpenACS Database Access API
- III.11.5 Using Templates in OpenACS
- III.11.6 Groups, Context, Permissions
- III.11.7 Writing OpenACS Application Pages
- III.11.8 Parties in OpenACS
- III.11.9 OpenACS Permissions Tediously Explained
- III.11.10 Object Identity
- III.11.11 Programming with AOLserver
- III.11.12 Using Form Builder: building html forms dynamically
- III.12 Engineering Standards
- III.12.1 OpenACS Style Guide
- III.12.2 Release Version Numbering
- III.12.3 Constraint naming standard
- III.12.4 ACS File Naming and Formatting Standards
- III.12.5 PL/SQL Standards
- III.12.6 Variables
- III.12.7 Automated Testing
- III.13 CVS Guidelines
- III.13.1 Using CVS with OpenACS
- III.13.2 OpenACS CVS Concepts
- III.13.3 Contributing code back to OpenACS
- III.13.4 Additional Resources for CVS
- III.14 Documentation Standards
- III.14.1 OpenACS Documentation Guide
- III.14.2 Using PSGML mode in Emacs
- III.14.3 Using nXML mode in Emacs
- III.14.4 Detailed Design Documentation Template
- III.14.5 System/Application Requirements Template
- III.15 TCLWebtest
- III.16 Internationalization
- III.16.1 Internationalization and Localization Overview
- III.16.2 How Internationalization/Localization works in OpenACS
- III.16.4 Design Notes
- III.16.5 Translator's Guide
- III.D Using CVS with an OpenACS Site
- III.9 Development Tutorial
- IV For OpenACS Platform Developers
- IV.17 Kernel Documentation
- IV.17.1 Overview
- IV.17.2 Object Model Requirements
- IV.17.3 Object Model Design
- IV.17.4 Permissions Requirements
- IV.17.5 Permissions Design
- IV.17.6 Groups Requirements
- IV.17.7 Groups Design
- IV.17.8 Subsites Requirements
- IV.17.9 Subsites Design Document
- IV.17.10 Package Manager Requirements
- IV.17.11 Package Manager Design
- IV.17.12 Database Access API
- IV.17.13 OpenACS Internationalization Requirements
- IV.17.14 Security Requirements
- IV.17.15 Security Design
- IV.17.16 Security Notes
- IV.17.17 Request Processor Requirements
- IV.17.18 Request Processor Design
- IV.17.19 Documenting Tcl Files: Page Contracts and Libraries
- IV.17.20 Bootstrapping OpenACS
- IV.17.21 External Authentication Requirements
- IV.18 Releasing OpenACS
- IV.18.1 OpenACS Core and .LRN
- IV.18.2 How to Update the OpenACS.org repository
- IV.18.3 How to package and release an OpenACS Package
- IV.18.4 How to Update the translations
- IV.17 Kernel Documentation
- V Tcl for Web Nerds
- V.1 Tcl for Web Nerds Introduction
- V.2 Basic String Operations
- V.3 List Operations
- V.4 Pattern matching
- V.5 Array Operations
- V.6 Numbers
- V.7 Control Structure
- V.8 Scope, Upvar and Uplevel
- V.9 File Operations
- V.10 Eval
- V.11 Exec
- V.12 Tcl for Web Use
- V.13 OpenACS conventions for TCL
- V.14 Solutions
- VI SQL for Web Nerds
- VI.1 SQL Tutorial
- VI.1.1 SQL Tutorial
- VI.1.2 Answers
- VI.2 SQL for Web Nerds Introduction
- VI.3 Data modeling
- VI.3.1 The Discussion Forum -- philg's personal odyssey
- VI.3.2 Data Types (Oracle)
- VI.3.4 Tables
- VI.3.5 Constraints
- VI.4 Simple queries
- VI.5 More complex queries
- VI.6 Transactions
- VI.7 Triggers
- VI.8 Views
- VI.9 Style
- VI.10 Escaping to the procedural world
- VI.11 Trees
- VI.1 SQL Tutorial
V.3 List Operations
A Tcl list holds a sequence of elements, each of which can be a number, a string, or another list. Let's look at the commands for constructing a list:At this point, the variable
% # create an empty list using the list command
% set user_preferences [list]
% # verify that we've created a 0-item list
% llength $user_preferences
0
% lappend user_preferences "hiking"
hiking
% lappend user_preferences "biking"
hiking biking
% lappend user_preferences "whale watching"
hiking biking {whale watching}
% llength $user_preferences
3
user_preferences
is a three-element list. We can pull individual items out with lindex
:
% lindex $user_preferences 0
hiking
% lindex $user_preferences 1
biking
% lindex $user_preferences 2
whale watching
% lindex $user_preferences 3
% lindex $user_preferences 5
Note, that lindex list 0 gives the first element of the list! (Indexing is 0-based and lindex
will return the empty string rather than an error if you supply an out-of-range index.)
When producing a page for a user, we'd be more likely to be interested in searching the list. The command lsearch
returns the index of the list element matching a query argument or -1 if unsuccessful:
if { [lsearch -exact $user_preferences "hiking"] != -1 } {
# look for new articles related to hiking
}
Concat
Suppose that User A marries User B. You want to combine their preferences into a household_preferences
variable using the concat
command:
% # use the multiple-argument form of list to create an N-element
% # list with one procedure call
% set spouse_preferences [list "programming" "computer games" "slashdot"]
programming {computer games} slashdot
% set household_preferences [concat $user_preferences $spouse_preferences]
hiking biking {whale watching} programming {computer games} slashdot
% llength $household_preferences
6
Split and Join
Suppose we have a file called addressees.txt
with information about people, one person to a line. Suppose each of these lines contains, among other information, an email address which we assume we can recognize by the presence of an at-sign (@
). The following program extracts all the email addresses and joins them together, separated by commas and spaces, to form a string called spam_address
that we can use as the Bcc:
field of an email message, to spam them all:
Some things to observe here:
# open the file for reading
set addressees_stream [open "~/addressees.txt" r]
# read entire file into a variable
set contents_of_file [read $addressees_stream]
close $addressees_stream
# split the contents on newlines
set list_of_lines [split $contents_of_file "\n"]
# loop through the lines
foreach line $list_of_lines {
if { [regexp {([^ ]*@[^ ]*)} $line one_address] } {
lappend all_addresses $one_address
}
}
# use the join command to mush the list together
set bcc_line_for_mailer [join $all_addresses ", "]
- We've used the
foreach
operator (see the chapter on control structure) to iterate over the list formed by splitting the file at newline characters. - We use pattern matching to extract the email address from each line. The pattern here specifies "stuff that doesn't contain a space, followed by at-sign, followed by stuff that doesn't contain a space." (See the explanation of
regexp
in the chapter on pattern matching.) - The iteration keeps
lappend
ing toall_addresses
, but this variable is never initialized.lappend
treats an unbound list variable the same as an empty list.
Reference: List operations
- list arg1 arg2 ...
Construct and return a list the arguments. Akin to(list arg1 arg2...)
in Scheme.set foo [list 1 2 [list 3 4 5]] ==> 1 2 {3 4 5}
orset foo {1 2 {3 4 5}}==> 1 2 {3 4 5}
lset varName ?index...? newValue
Gives you the opportunity to insert elements at a given position and work with positions within a list in general.
.
lindex list i
Returns the ith element from list; starts at index 0.llength $foo ==> 1
llength list
Returns the number of elements in list.llength $foo ==> 3
lrange list i j
Returns the ith through jth elements from list.lrange $foo 1 2 ==> 2 {3 4 5}
lappend listVar arg arg...
Append elements to the value of listVar and reset listVar to the new list. Please note that listVar is the name of a variable and not the value, i.e., you should not put a $ in front of it (the same way thatset
works.lappend foo [list 6 7] ==> 1 2 {3 4 5} {6 7} set foo ==> 1 2 {3 4 5} {6 7}
linsert list index arg arg...
Insert elements into list before the element at position index. Returns a new list.linsert $foo 0 0 ==> 0 1 2 {3 4 5} {6 7}
lreplace list i j arg arg...
Replace elements i through j of list with the args. Returns a new list and leaves the original list unmodified.lreplace $foo 3 4 3 4 5 6 7 ==> 0 1 2 3 4 5 6 7 set foo ==> 1 2 {3 4 5} {6 7}
lsearch mode list value
Return the index of the element in list that matches the value according to the mode, which is -exact, -glob, or -regexp. -glob is the default. Return -1 if not found.set community_colleges [list "caltech" "cmu" "rpi"]
lsearch -exact $community_colleges "caltech" ==> 0 lsearch -exact $community_colleges "harvard" ==> -1lsort switches list
Sort elements of the list according to the switches: -ascii, -integer, -real, -increasing, -decreasing, -command command. Returns a new list.set my_friends [list "herschel" "schlomo" "mendel"]
set my_sorted_friends [lsort -decreasing $my_friends] ==> schlomo mendel herschelconcat arg arg...
Join multiple lists together into one list.set my_wifes_friends [list "biff" "christine" "clarissa"]
concat $my_wifes_friends $my_friends ==> biff christine clarissa herschel schlomo mendeljoin list joinString
Merge the elements of list to produce a string, where the original list elements are separated by joinString. Returns the resulting string. Note: if you want to merge without separating the elements, just put "" .set foo_string [join $foo ":"] ==> 0:1:2:3:4:5:6:7
split string splitChars
Split a string to produce a list, using (and discarding) the characters in splitChars as the places where to split. Returns the resulting list.set my_ip_address 18.1.2.3 ==> 18.1.2.3 set ip_elements [split $my_ip_address "."] ==> four element list,
with values 18 1 2 3
Exercises
1. Write the iota
function, which takes a numeric argument n and returns a list of numbers of length n which are the numbers from 0 to n-1.
# iota :: num -> [num]
proc iota {n} {...}
Examples:
iota 3
=> 0 1 2
iota 20
=> 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
2. Write the function incrlist
, which takes one argument, a list of numbers, and returns a list of equal length as a result, in which each element is the successor to the corresponding element of the argument list.
# incrlist :: [num] -> [num]
proc incrlist {L} {...}
incrlist {34 987 1 567 -23 8}
=> 35 988 2 568 -22 9
incrlist [iota 12]
=> 1 2 3 4 5 6 7 8 9 10 11 12
Hint: Arithmetic is not done directly by the Tcl interpreter. It is done by calling the C library using the expr
command on arthmetic expressions.
Does your function work for the empty list?
3. Write the function strlenlist
, which takes one argument, a list of strings, and returns a list of equal length as a result, in which each element is the string length of the corresponding element of the argument list.
# strlenlist :: [str] -> [num]
proc strlenlist {L} {...}
strlenlist {34 987 1 567 -23 8}
=> 2 3 1 3 3 1
strlenlist {foo bar antidisestablishmentarianism}
=> 3 3 28
Does your function work for the empty list?
4. Write sumlist
, which takes one argument, a list of numbers, and returns, as result, a single number: the sum of the numbers in the argument list.
# sumlist :: [num] -> num
proc sumlist {L} {...}
sumlist [iota 3]
=> 3
sumlist {34 987 1 567 -23 8}
=> 1574
5. Write multlist
, which takes one argument, a list of numbers, and returns, as result, a single number: the product of the numbers in the argument list.
# multlist :: [num] -> num
proc multlist {L} {...}
multlist [iota 3]
=> 0
multlist [incrlist [iota 3]]
=> 3
multlist {34 987 1 567 -23 8}
=> 793928272
6. Write catlist
, which takes one argument, a list of strings, and returns, as result, a single string: the concatenation of the strings in the argument list.
# catlist :: [str] -> str
proc catlist {L} {...}
catlist [iota 3]
=> 012
catlist [incrlist [iota 3]]
=> 123
list {foo bar antidisestablishmentarianism}
=> foobarantidisestablishmentarianism
Data abstraction with lists
The Tcl shell's output is giving you an ugly insight into the internal representation of lists as strings, with elements being separated by spaces and grouped with braces. There is no reason to rely on how Tcl represents lists or even think about it. Practice data abstraction by using Tcl lists as your underlying storage mechanism but define constructor and accessor procedures that the rest of your source code invokes. You won't find a huge amount of this being done in Web development because the really important data structures tend to be RDBMS tables, but here's an example of how it might work, taken from http://photo.net/philg/careers/four-random-people.tcl. We're building a list of lists. Each sublist contains all the information on a single historical figure. Method A is quick and dirty:
set einstein [list "A. Einstein" "Patent Office Clerk" "Formulated Theory of Relativity."]
set mill [list "John Stuart Mill" "English Youth" "Was able to read Greek and Latin at age 3."]
# let's build the big list of lists
set average_folks [list $einstein $mill ...]
# let's pull out Einstein's title
set einsteins_title [lindex $einstein 1]
Method B uses data abstraction:
proc define_person {name title accomplishment} {
return [list $name $title $accomplishment]
}
proc person_name {person} {
return [lindex $person 0]
}
proc person_title {person} {
return [lindex $person 1]
}
proc person_accomplishment {person} {
return [lindex $person 2]
}
% set einstein [define_person "A. Einstein" "Patent Office Clerk" "Formulated Theory of Relativity."]
{A. Einstein} {Patent Office Clerk} {Formulated Theory of Relativity.}
% set einsteins_title [person_title $einstein]
Patent Office Clerk
Data abstraction will make building and maintaining a large system much easier. As noted above, however, the stateless nature of HTTP means that any information you want kept around from page to page must be kept by the RDBMS. SQL already forces you to refer to data by table name and column name rather than positionally.
---
based on Tcl for Web Nerds