JSON
Created by Michael Aram, last modified by Michael Aram 20 Mar 2019, at 05:17 PM
Caveat: Page under construction
There are several libraries and ways available to parse and/or generate JSON in OpenACS.
Created by Michael Aram, last modified by Michael Aram 20 Mar 2019, at 05:17 PM
Caveat: Page under construction
There are several libraries and ways available to parse and/or generate JSON in OpenACS.
Created by Michael Aram, last modified by Michael Aram 20 Mar 2019, at 05:16 PM
Caveat: Page under construction
apt-get install libyajl-dev pkg-config
cd /var/tmp
export YAJLTCL_VERSION=1.7.0-rc1
wget --no-check-certificate -O yajl-tcl-${YAJLTCL_VERSION}.tar.gz https://github.com/flightaware/yajl-tcl/archive/v${YAJLTCL_VERSION}.tar.gz
cd /usr/local/src
tar -xzf /var/tmp/yajl-tcl-${YAJLTCL_VERSION}.tar.gz
cd yajl-tcl-${YAJLTCL_VERSION}
autoconf
./configure --enable-threads --with-tcl=/usr/local/ns/lib
make
make install
Created by Rocael Hernández Rizzardini, last modified by Gustaf Neumann 13 Feb 2019, at 11:48 PM
Useful aliases for cvs.openacs.org:
* These aliases are controlled by OCT and .LRN honchos groups, the packages that belong to these aliases are the ones that are maintained by both groups. They are not to be changed unless approved by OCT and / or .LRN technical committee.
Below the packages details for each alias:
##################################################################
# OpenACS Core
##################################################################
acs-core -a openacs-4/packages/acs-admin
openacs-4/packages/acs-api-browser
openacs-4/packages/acs-authentication
openacs-4/packages/acs-automated-testing
openacs-4/packages/acs-bootstrap-installer
openacs-4/packages/acs-content-repository
openacs-4/packages/acs-core-docs
openacs-4/packages/acs-developer-support
openacs-4/packages/acs-kernel
openacs-4/packages/acs-lang
openacs-4/packages/acs-mail-lite
openacs-4/packages/acs-messaging
openacs-4/packages/acs-reference
openacs-4/packages/acs-service-contract
openacs-4/packages/acs-subsite
openacs-4/packages/acs-tcl
openacs-4/packages/acs-templating
openacs-4/packages/ref-countries
openacs-4/packages/ref-language
openacs-4/packages/ref-timezones
openacs-4/packages/acs-translations
openacs-4/packages/intermedia-driver
openacs-4/packages/openacs-default-theme
openacs-4/packages/notifications
openacs-4/packages/search
openacs-4/packages/tsearch2-driver
openacs-4/ChangeLog
openacs-4/license.txt
openacs-4/readme.txt
openacs-4/bin
openacs-4/content-repository-content-files
openacs-4/database-backup
openacs-4/etc
openacs-4/log
openacs-4/tcl
openacs-4/www
##################################################################
# .LRN aliases
##################################################################
dotlrn-all -a acs-datetime acs-events
attachments bulk-mail calendar faq file-storage
forums general-comments news lars-blogger
weblogger-portlet rss-support trackback oacs-dav
xml-rpc categories dotlrn dotlrn-syllabus new-portal
profile-provider user-profile bm-portlet dotlrn-bm
calendar-portlet dotlrn-calendar dotlrn-portlet
dotlrn-dotlrn faq-portlet dotlrn-faq forums-portlet
dotlrn-forums fs-portlet dotlrn-fs news-portlet
dotlrn-news static-portlet dotlrn-static
dotlrn-homework dotlrn-weblogger evaluation
evaluation-portlet dotlrn-evaluation assessment
assessment-portlet dotlrn-assessment theme-selva
theme-zen dotlrn-random-photo random-photo-portlet
views
dotlrn-extras -a lors lorsm lorsm-portlet dotlrn-lorsm
photo-album photo-album-portlet dotlrn-photo-album
survey-portlet dotlrn-survey survey chat
dotlrn-chat chat-portlet xotcl-core imsld
imsld-portlet dotlrn-imsld
##################################################################
# Project Manager aliases
##################################################################
project-manager-all -a calendar acs-events acs-datetime
acs-mail-lite categories logger
project-manager general-comments
organizations
##################################################################
# xoTCL Stuff + required
##################################################################
xotcl-all -a xotcl-core xotcl-request-monitor xowiki categories
acs-events acs-datetime file-storage oacs-dav
rss-support general-comments
Created by Torben Brosten, last modified by Gustaf Neumann 13 Feb 2019, at 11:43 PM
Summary: | ISO 3166 country data. |
Description: | Countries supplies the ISO 3166 Country data for acs-reference. |
Documentation: | Package Documentation |
Maturity: | Immature |
This package depends on: | acs-kernel acs-reference |
Packages that depend on ref-countries: | accounts-ledger acs-lang ams contacts ecommerce postal-address ref-currency value-based-shipping |
Package parameters: | None |
There is no package with the name "ref-countries" known to bug-tracker.
# Tcl Procs | 2 |
# Tcl Lines | 110 |
# Tcl Blank Lines | 20 |
# Tcl Comment Lines | 11 |
# Automated Tests | 1 |
# Stored Procedures | PG: 0 ORA: 0 |
# SQL Lines | PG: 44 (blank 8 comments 11) ORA: 49 (blank 7 comments 11) |
# ADP pages | 0 |
# ADP lines | 0 |
# Include pages (ref-countries/lib/) | 0 |
# Documentation pages | 1 (Package Documentation) |
# Documentation lines | 16 |
Browse Source | API-browser |
Github Repository: | https://github.com/openacs/openacs-core |
Created by Robert Taylor, last modified by Gustaf Neumann 13 Feb 2019, at 11:41 PM
Summary: | ISO 639-1 language reference data for acs-reference. |
Description: | This is the ISO 639-1 language reference data (with languages names in English). |
Maturity: | Mature |
This package depends on: | acs-kernel acs-reference |
Packages that depend on ref-language: | accounts-ledger acs-lang ecommerce ref-currency |
Package parameters: | None |
There is no package with the name "ref-language" known to bug-tracker.
# Tcl Procs | 6 |
# Tcl Lines | 267 |
# Tcl Blank Lines | 47 |
# Tcl Comment Lines | 14 |
# Automated Tests | 1 |
# Stored Procedures | PG: 0 ORA: 0 |
# SQL Lines | PG: 49 (blank 10 comments 9) ORA: 54 (blank 10 comments 9) |
# ADP pages | 0 |
# ADP lines | 0 |
# Include pages (ref-language/lib/) | 0 |
# Documentation pages | 0 |
# Documentation lines | 0 |
Browse Source | API-browser |
Github Repository: | https://github.com/openacs/openacs-core |
Created by Gustaf Neumann, last modified by Gustaf Neumann 16 Dec 2018, at 09:45 PM
The OpenACS translation server is currently not supported. Volunteers are welcome to help to revive this project which helps to collect message keys in various languages from users and to feed this into the OpenACS core distributions.
On interest, please send a mail to oct@openacs.org.
Created by Nick Carroll, last modified by Gustaf Neumann 01 Dec 2018, at 10:31 AM
Summary: | Simplified reliable email transmission with bounce management. |
Description: | This package provides a service for sending and receiving messages, messages are queued in the database to ensure reliable sending and make sending a message 'transactional'. Replacement for acs-mail. |
Documentation: | Package Documentation |
Maturity: | Mature and Standard |
This package depends on: | acs-kernel acs-service-contract acs-tcl |
Packages that depend on acs-mail-lite: | acs-admin acs-authentication acs-messaging acs-subsite bulk-mail contacts dotlrn forums mail-tracking notifications postcard project-manager simulation |
Package parameters: |
|
Open Bugs: | 4 |
All Tracked Issues: | 30 |
Latest Bug Opened: | 2019-06-19 Mail server down — no retry? |
Latest Bug Fixed: | 2021-08-27 redirect does not work in acs-mail-lite 5.10.0d12. |
Top Bug Submitters: | Jade Rubick (4) Benjamin Brink (3) Martin Magerl (2) Dirk Gomez (1) Brian Fenton (1) |
Top Bug Fixers: | Malte Sussdorff (7) Gustaf Neumann (6) Jade Rubick (3) Dave Bauer (2) Caroline Meeks (1) Rocael Hernández Rizzardini (1) Torben Brosten (1) |
# Tcl Procs | 67 |
# Tcl Lines | 5800 |
# Tcl Blank Lines | 765 |
# Tcl Comment Lines | 948 |
# Automated Tests | 2 |
# Stored Procedures | PG: 0 ORA: 0 |
# SQL Lines | PG: 433 (blank 48 comments 216) ORA: 423 (blank 39 comments 213) |
# ADP pages | 1 |
# ADP lines | 14 |
# Include pages (acs-mail-lite/lib/) | 0 |
# Documentation pages | 55 (Package Documentation) |
# Documentation lines | 2572 |
Browse Source | API-browser |
Github Repository: | https://github.com/openacs/openacs-core |
Commands for ds/shell to test, whether you mail setup is correct:
set to_addr [party::get -party_id [ad_conn user_id] -element email] set from_addr [parameter::get -parameter HostAdministrator -package_id $::acs::kernel_id] acs_mail_lite::send_immediately -to_addr $to_addr -from_addr $from_addr -body "hi" -subject "testing with send_immediately"
Created by OpenACS community, last modified by Gustaf Neumann 06 Jul 2018, at 10:41 AM
This page is mostly outdated and we keep it for a reference. For non-packaged installs on *nix systems, consider naviserver-openacs which supports systemd and upstart scripts.
The simplest way to start and stop and OpenACS site is to run the startup shell script provided in the OpenACS distribution, /var/lib/aolserver/$OPENACS_SERVICE_NAME/etc/daemontools/run. This runs as a regular task, and logs to the logfile. To stop the site, kill the script.
A more stable way to run OpenACS is with a "keepalive" mechanism of some sort, so that whenever the server halts or is stopped for a reset, it restarts automatically. This is recommended for development and production servers.
The Reference Platform uses Daemontools to control AOLserver. An alternative method, is using inittab.
Daemontools must already be installed. If not, install it.
Each service controlled by daemontools must have a directory in /service. That directory must have a file called run. It works like this:
The init program starts every time the computer is booted.
A line in init's configuration file, /etc/inittab, tells init to run, and to restart if necessary, svscanboot.
svscanboot checks the directory /service every few seconds.
If it sees a subdirectory there, it looks for a file in the subdirectory called run. If it finds a run file, it creates a supervise process
supervise executes the run script. Whenever the run script stops, supervise executes it again. It also creates additional control files in the same directory.
Hence, the AOLserver instance for your development server is started by the file /service/$OPENACS_SERVICE_NAME/run. But we use a symlink to make it easier to add and remove stuff from the /service, so the actual location is /var/lib/aolserver/$OPENACS_SERVICE_NAMEetc/daemontools/run.
Daemontools creates additional files and directories to track status and log. A daemontools directory is included in the OpenACS tarball at /var/lib/aolserver/$OPENACS_SERVICE_NAME/etc/daemontools. To use it, first kill any existing AOLserver instances. As root, link the daemontools directory into the /service directory. Daemontools' svscan process checks this directory every five seconds, and will quickly execute run.
[$OPENACS_SERVICE_NAME etc]$ killall nsd nsd: no process killed [$OPENACS_SERVICE_NAME etc]$ emacs /var/lib/aolserver/$OPENACS_SERVICE_NAME/etc/daemontools/run [$OPENACS_SERVICE_NAME etc]$ exit [root root]# ln -s /var/lib/aolserver/$OPENACS_SERVICE_NAME/etc/daemontools/run /service/$OPENACS_SERVICE_NAME/run
Verify that AOLserver is running.
[root root]# ps -auxw | grep nsd $OPENACS_SERVICE_NAME 5562 14.2 6.2 22436 15952 ? S 11:55 0:04 /usr/local/aolserver/bin/nsd -it /var/lib/aolserver/$OPENACS_SERVICE_NAME/etc/config.tcl -u serve root 5582 0.0 0.2 3276 628 pts/0 S 11:55 0:00 grep nsd [root root]#
The user $OPENACS_SERVICE_NAME can now control the service $OPENACS_SERVICE_NAME with these commands:
svc -d /service/$OPENACS_SERVICE_NAME - Bring the server down
svc -u /service/$OPENACS_SERVICE_NAME - Start the server up and leave it in keepalive mode.
svc -o /service/$OPENACS_SERVICE_NAME - Start the server up once. Do not restart it if it stops.
svc -t /service/$OPENACS_SERVICE_NAME - Stop and immediately restart the server.
svc -k /service/$OPENACS_SERVICE_NAME - Sends the server a KILL signal. This is like KILL -9. AOLserver exits immediately. If svc -t fails to fully kill AOLserver, use this option. This does not take the server out of keepalive mode, so it should still bounce back up immediately.
Install a script to automate the stopping and starting of AOLserver services via daemontools. You can then restart a service via restart-aolserver $OPENACS_SERVICE_NAME
[root root]# cp /var/lib/aolserver/$OPENACS_SERVICE_NAME/packages/acs-core-docs/www/files/restart-aolserver-daemontools.txt /usr/local/bin/restart-aolserver [root root]# chmod 755 /usr/local/bin/restart-aolserver [root root]#
At this point, these commands will work only for the root user. Grant permission for the web group to use svc commands on the $OPENACS_SERVICE_NAME server.
[root root]# /usr/local/bin/svgroup web /service/$OPENACS_SERVICE_NAME [root root]#
Verify that the controls work. You may want to tail -f /var/lib/aolserver/$OPENACS_SERVICE_NAME/log/$OPENACS_SERVICE_NAME-error.log in another window, so you can see what happens when you type these commands.
Most of this information comes from Tom Jackson's AOLserver+Daemontools Mini-HOWTO.
Table 6.1. How it Works
Program | Invoked by this program ... | ... using this file | Where to find errors | Log goes to | Use these commands to control it |
---|---|---|---|---|---|
svscanboot | init | /etc/inittab | ps -auxw | grep readproctitle | n/a | |
aolserver | supervise (a child of svscanboot) | /service/$OPENACS_SERVICE_NAME/run | /var/lib/aolserver/$OPENACS_SERVICE_NAME/log/error.log | /var/lib/aolserver/$OPENACS_SERVICE_NAME/log/$OPENACS_SERVICE_NAME.log | svc -k /service/$OPENACS_SERVICE_NAME |
postgresql | Redhat init scripts during boot | /etc/init.d/postgresql | /usr/local/pgsql/data/server.log | service postgresql start (Red Hat), /etc/init.d/postgresql start (Debian) |
This is an alternative method for keeping the AOLserver process running. The recommended method is to run AOLserver supervised.
This step should be completed as root. This can break every service on your machine, so proceed with caution.
There are 2 general steps to getting this working.
Install a script called restart-aolserver. This script doesn't actually restart AOLserver - it just kills it.
Ask the OS to restart our service whenever it's not running. We do this by adding a line to /etc/inittab.
Calling restart-aolserver kills our service. The OS notices that our service is not running, so it automatically restarts it. Thus, calling restart-aolserver effectively restarts our service.
This script needs to be SUID-root, which means that the script will run as root. This is necessary to ensure that the AOLserver processes are killed regardless of who owns them. However the script should be executable by the web group to ensure that the users updating the web page can use the script, but that general system users cannot run the script. You also need to have Perl installed and also a symbolic link to it in /usr/local/bin.
[joeuser ~]$ su - Password: *********** [root ~]# cp /var/lib/aolserver/service0/packages/acs-core-docs/www/files/restart-aolserver.txt /usr/local/bin/restart-aolserver [root ~]# chown root.web /usr/local/bin/restart-aolserver [root ~]# chmod 4750 /usr/local/bin/restart-aolserver [root ~]# ln -s /usr/bin/perl /usr/local/bin/perl [root ~]# exit
Test the restart-aolserver script. We'll first kill all running servers to clean the slate. Then, we'll start one server and use restart-aolserver to kill it. If it works, then there should be no more servers running. You should see the following lines.
[joeuser ~]$ killall nsd nsd: no process killed [joeuser ~]$ /usr/local/aolserver/bin/nsd-postgres -t ~/var/lib/aolserver/service0/nsd.tcl [joeuser ~]$ restart-aolserver service0 Killing 23727 [joeuser ~]$ killall nsd nsd: no process killed
The number 23727 indicates the process id(s) (PIDs) of the processes being killed. It is important that no processes are killed by the second call to killall. If there are processes being killed, it means that the script is not working.
Assuming that the restart-aolserver script worked, login as root and open /etc/inittab for editing.
[joeuser ~]$ su - Password: ************ [root ~]# emacs -nw /etc/inittab
Copy this line into the bottom of the file as a template, making sure that the first field nss1 is unique.
nss1:345:respawn:/usr/local/aolserver/bin/nsd-postgres -i -u nobody -g web -t /home/joeuser/var/lib/aolserver/service0/nsd.tcl
Important: Make sure there is a newline at the end of the file. If there is not a newline at the end of the file, the system may suffer catastrophic failures.
Still as root, enter the following command to re-initialize /etc/inittab.
[root ~]# killall nsd nsd: no process killed [root ~]# /sbin/init q
See if it worked by running the restart-aolserver script again.
[root ~]# restart-aolserver service0 Killing 23750
If processes were killed, congratulations, your server is now automated for startup and shutdown.
https://openacs.org/doc/uptime.html
Add a pause to the aolserver "run" script, for example:
sleep 15
Alternately, John Sequeira suggests adding this code to the run script:
pid=`pidof -s postmaster` while !([ $pid ] && /etc/init.d/postgresql status > /dev/null 2>&1) do pid=`pidof -s postmaster` echo "checking...not yet" sleep 1 done
run AOLserver on port 80 (or <1024) To run AOLserver on a port below 1024 (normally, for a webserver use port 80), edit the run script. /var/lib/aolserver/service0/etc/daemontools/run script according to the documentation found there (namely: Add the -b yourip:yourport switch)
To set up real-time monitoring of the AOLserver error log, from the shell, type
less /var/lib/aolserver/$OPENACS_SERVICE_NAME/log/openacs-dev-error.log
F to show new log entries in real time (like tail -f)
C-c to stop and F to start it up again.
G goes to the end.
? searches backward
/ searches forward.
Cannot view sample AOLserver welcome page There may be a problem with the server configuration. Start by viewing the AOLserver log, which is in /usr/local/aolserver/log/server.log. You should also try to find lines of the form:
[01/Jun/2000:12:11:20][5914.2051][-nssock-] Notice: nssock: listening on http://localhost.localdomain:8000 (127.0.0.1:8000) [01/Jun/2000:12:11:20][5914.2051][-nssock-] Notice: accepting connections
If you can find these lines, try entering the URL the server is listening on into your browser. If you cannot find these lines, there must be an error somewhere in the file. Search for lines beginning with the word Error instead of Notice.
The sample-config.tcl file grabs your address and hostname from your OS settings.
set hostname [ns_info hostname] set address [ns_info address]
If you get an error that nssock can't get the requested address, you can set these manually. If you type 0.0.0.0, AOLserver will try to listen on all available addresses. Note: ns_info address doesn't appear to be supported in current versions of AOLserver.
set hostname [ns_info hostname] #set address [ns_info address] set address 0.0.0.0
NsTclInitObjs: sizeof(int) < sizeof(long) This is a 64-bit compile problem that keeps Aolserver from starting. An error detection check in file nsd/tclobj.c is causing the error, and needs to be commented out. Wrap the following code with the C style /* */ comments (as shown), then rebuild. See https://openacs.org/forums/message-view?message_id=309887 for more info.
/* if (sizeof(int) < sizeof(long)) { Tcl_Panic("NsTclInitObjs: sizeof(int) < sizeof(long)"); } */
Cannot view the OpenACS login or OpenACS Installation: Welcome page. View your error log (/var/lib/aolserver/$OPENACS_SERVICE_NAME/log/$OPENACS_SERVICE_NAME-error.log) to make sure the service is starting without any problems. The most common errors here are trying to start a port 80 server while not root, failing to connect because of a firewall, and aolserver failing to start due to permissions errors or missing files. If you need to make changes, don't forget to kill any running servers with killall nsd.
httpport - If you want your server on a different port, enter it here. The Reference Platform port is 8000, which is suitable for development use. Port 80 is the standard http port - it's the port used by your browser when you enter http://yourserver.test. So you should use port 80 for your production site.
httpsport - This is the port for https requests. The Reference Platform https port is 8443. If http port is set to 80, httpsport should be 143 to match the standard.
address - The IP address of the server. If you are hosting multiple IPs on one computer, this is the address specific to the web site. Each virtual server will ignore any requests directed at other addresses.
server - This is the keyword that, by convention, identifies the service. It is also used as part of the path for the service root, as the name of the user for running the service, as the name of the database, and in various dependent places. The Reference Platform uses $OPENACS_SERVICE_NAME.
db_name - In almost all cases, this can be kept as a reference to $server. If for some reason, the tablespace you are using is different than your servername, then you can set it here. You should have a good reason for doing this.
servername - This is just a *pretty* name for your server.
user_account - The account that will both own OpenACS files and connect to the database (for Postgresql).
debug - Set to true for a very verbose error log, including many lines for every page view, success or failure.
AOLserver is very configurable. These are some of the more commonly changed settings. For more options, read the AOLserver docs.
Created by Gustaf Neumann, last modified by Antonio Pisano 26 Jun 2018, at 06:01 PM
The database abstraction layer of xotcl-core interfaces between the OpenACS object model used in the relational Database and XOTcl objects. The database abstraction layer works for PostgreSQL and Oracle in exact the same way.
The functionality of the database abstraction layer provides separate classes for operation on generic ACS objects and on objects stored in the OpenACS content repository. The generic lookup queries and instantiate methods are provided through the following classes
Generic operations on ACS Object IDs:
Generic operations on ACS Object Types:
Generic operations on items of the content repository:
Methods on classes for ACS Object Types
Methods on content repository classes
Methods on instances of ordinary classes
Methods on instances of content repository classes
The following links provide a detailed description of these classes in the version as they are installed on openacs.org: ::xo::db::Class and ::xo::db::CrClass
The remainder of this page provides a short tutorial into the basic functionality of the interface. A more detailed description can be found in the xotcl-core tutorial
############################################################ # # 1) Create new ACS Objects, destroy it in memory, # load it from the database, delete it in the database. # .. Create a plain new ACS object just for demo purposes. .. The ACS object is created with a new object id. >> set o [::xo::db::Object new_persistent_object] = ::7845 .. Show the contents of object ::7845 by serializing it: >> ::7845 serialize = ::xo::db::Object create ::7845 -noinit \ -set object_title {Object 7845} \ -set object_id 7845 .. In the next steps, we (a) get the object_id of the newly .. created ACS object, (b) destroy the XOTcl object (the ACS .. object is still in the database, (c) we recreate the .. XOTcl object from the database, and (d) delete it in the .. database. .. Step (a) >> set o_id [::7845 object_id] = 7845 # # Delete object from memory: <object> destroy # Check, if an XOTcl object exists: ::xotcl::Object isobject <obj> # >> ::xotcl::Object isobject ::7845 = 1 .. Step (b) >> ::7845 destroy >> ::xotcl::Object isobject ::7845 = 0 # # Load an object from the database: ::xo::db::Class get_instance_from_db -id <id> # .. Step (c) >> set o [::xo::db::Class get_instance_from_db -id 7845] = ::7845 >> ::xotcl::Object isobject ::7845 = 1 .. Now, we have recreated the same object as before: >> ::7845 serialize = ::xo::db::Object create ::7845 -noinit \ -set object_title {Object 7845} \ -set object_id 7845 # # Check, if an ACS object exists in the database: ::xo::db::Class exists_in_db -id <id> # Delete object from memory and database: <object> delete # >> ::xo::db::Class exists_in_db -id 7845 = 1 .. Step (d) >> ::7845 delete .. Now, we have deleted the ACS Object and the XOTcl object: >> ::xo::db::Class exists_in_db -id 7845 = 0 >> ::xotcl::Object isobject ::7845 = 0 ############################################################ # # 2) Create new ACS Object Types, ACS Attributes and # SQL Tables from XOTcl Classes with slot definitions. # .. We want to create a new ACS Object type and .. an XOTcl class named ::demo::Person. .. Does the ACS Object type ::demo::Person exist in the database? >> ::xo::db::Class object_type_exists_in_db -object_type ::demo::Person = 0 .. The ACS Object Type should not exist (statement should return 0) .. We create a new XOTcl Class '::demo::Person'. .. By defining this class, the database layer takes care .. of creating the ACS Object Type and the necessary table via SQL. .. The persistent attributes (stored in the database) are defined .. as slots of type ::xo::db::Attribute. >> ::xo::db::Class create ::demo::Person \ -superclass ::xo::db::Object \ -slots { ::xo::db::Attribute create name -column_name pname ::xo::db::Attribute create age -default 0 -datatype integer ::xo::db::Attribute create projects -default {} -multivalued true } = ::demo::Person .. If the ACS Object Type and the ACS Attributes would be .. already defined in the database, the class definition above .. would be a no-op operation. .. Now, the ACS Object Type exists in the database >> ::xo::db::Class object_type_exists_in_db -object_type ::demo::Person = 1 .. The XOTcl class definition created automatically the .. following table for storing instances: CREATE TABLE demo_person ( age integer DEFAULT '0' , pname text , projects text DEFAULT '' , person_id integer REFERENCES acs_objects(object_id) ON DELETE CASCADE CONSTRAINT demo_person_person_id_pk PRIMARY KEY ) .. SQL attribute slot names: >> ::demo::Person array names db_slot = name age projects person_id .. The XOTcl class definition created as well a 'save' and .. an 'insert' method: .. Created 'save' method: ::demo::Person instproc save {} { db_transaction { next my instvar object_id name age projects db_dml dbqd..update_demo_person {update demo_person set pname = :name,age = :age,projects = :projects where person_id = :object_id } } } .. Created 'insert' method: ::demo::Person instproc insert {} { set __table_name [[self class] table_name] set __id [[self class] id_column] my set $__id [my set object_id] my log "ID insert in $__table_name, id = $__id = [my set $__id]" next foreach {__slot_name __slot} [[self class] array get db_slot] { my instvar $__slot_name if {[info exists $__slot_name]} { lappend __vars $__slot_name lappend __atts [$__slot column_name] } } db_dml dbqd..insert_$__table_name "insert into $__table_name ([join $__atts ,]) values (:[join $__vars ,:])" } .. The 'insert' method is typically called via .. '<classname> new', which takes care about db_transaction (see below) # # Create a new instance of ::demo::Person with name 'Gustaf' # # The method 'new_persistent_object' of a database class (instance of ::xo::db::Class) # creates an ACS Object with a fresh id in the database and # creates as well an XOTcl object in memory >> set p [::demo::Person new_persistent_object -name Gustaf -age 105] = ::7846 ::7846 created .. check, if object ::7846 exists in memory >> ::xotcl::Object isobject ::7846 = 1 .. check, if object ::7846 exists in the database >> ::xo::db::Class exists_in_db -id 7846 = 1 .. Every persistent XOTcl object has an object_id. >> ::7846 exists object_id = 1 .. Show the contents of object ::7846 (using serialize) >> ::7846 serialize = ::demo::Person create ::7846 -noinit \ -set object_title {Person 7846} \ -set name Gustaf \ -set age 105 \ -set projects {} \ -set person_id 7846 \ -set object_id 7846 .. modify some attributes of the XOTcl object >> ::7846 incr age = 106 .. show modified contents of ::7846 ::demo::Person create ::7846 -noinit \ -set object_title {Person 7846} \ -set name Gustaf \ -set age 106 \ -set projects {} \ -set person_id 7846 \ -set object_id 7846 .. save the modified object data in the database >> ::7846 save >> set person_id [$p person_id] = 7846 .. deleting xotcl object ::7846 in memory >> $p destroy .. check, if object ::7846 exists in memory >> ::xotcl::Object isobject ::7846 = 0 .. check, if object ::7846 exists in the database >> ::xo::db::Class exists_in_db -id 7846 = 1 .. fetch person again from database: >> set p [::xo::db::Class get_instance_from_db -id 7846] = ::7846 .. check, if object ::7846 exists in memory >> ::xotcl::Object isobject ::7846 = 1 ::demo::Person create ::7846 -noinit \ -set object_title {Person 7846} \ -set name Gustaf \ -set age 106 \ -set projects {} \ -set object_id 7846 \ -set person_id 7846 .. The object ::7846 was fetched from the database using the .. automatically created query: SELECT demo_person.pname AS name,demo_person.age, demo_person.projects,demo_person.person_id, acs_objects.title AS object_title,acs_objects.object_id FROM demo_person,acs_objects WHERE person_id = 7846 and object_id = person_id .. In order to delete an object from the database, .. the method 'delete' can be used. 'delete' deletes the .. ACS object in the database as well the XOTcl object in memory. >> set p_id [::7846 object_id] = 7846 >> ::xo::db::Class exists_in_db -id 7846 = 1 >> ::7846 delete >> ::xo::db::Class exists_in_db -id 7846 = 0 .. Instead of using 'new_persistent_object' to create .. new acs_objects, one can use the method 'new' in .. combination with 'save_new'. While the method 'new' creates .. an XOTcl object in memory, the method 'save_new' can be used .. to create a new ACS object (with a new object_id) an to .. save this object persistently in the database. >> set p [::demo::Person new -name Gustaf -age 105] = ::xotcl::__#A >> ::xotcl::__#A exists object_id = 0 >> ::xotcl::__#A save_new = 7847 >> ::xotcl::__#A exists object_id = 1 .. Now, we create a subclass of ::demo::Person called ::demo::Employee .. which has a few more attributes. Again, we define an XOTcl class .. ::demo::Employee which creates the ACS Object Type, the ACS .. attributes and the table, if necessary. >> ::xo::db::Class create ::demo::Employee \ -superclass ::demo::Person \ -table_name demo_employee \ -id_column employee_id \ -slots { ::xo::db::Attribute create salary -datatype integer ::xo::db::Attribute create dept_nr -datatype integer -default "0" } = ::demo::Employee .. The XOTcl class definition created automatically the .. following table for storing instances: CREATE TABLE demo_employee ( dept_nr integer DEFAULT '0' , salary integer , employee_id integer REFERENCES demo_person(person_id) ON DELETE CASCADE CONSTRAINT demo_employee_employee_id_pk PRIMARY KEY ) .. Create a first new employee: >> set p1 [::demo::Employee new_persistent_object -name Neophytos -age 25] = ::7848 .. show contents of ::7848 ::demo::Employee create ::7848 -noinit \ -set employee_id 7848 \ -set object_title {Employee 7848} \ -set dept_nr 0 \ -set name Neophytos \ -set age 25 \ -set projects {} \ -set person_id 7848 \ -set object_id 7848 .. Create a second new employee: >> set p2 [::demo::Employee new_persistent_object -name Lore -age 35 -salary 100000] = ::7849 .. show contents of ::7849 ::demo::Employee create ::7849 -noinit \ -set employee_id 7849 \ -set object_title {Employee 7849} \ -set salary 100000 \ -set dept_nr 0 \ -set name Lore \ -set age 35 \ -set projects {} \ -set person_id 7849 \ -set object_id 7849 .. Create a third new employee: >> set p3 [::demo::Employee new_persistent_object -name Nora -age 7 -salary 100] = ::7850 .. show contents of ::7850 ::demo::Employee create ::7850 -noinit \ -set employee_id 7850 \ -set object_title {Employee 7850} \ -set salary 100 \ -set dept_nr 0 \ -set name Nora \ -set age 7 \ -set projects {} \ -set person_id 7850 \ -set object_id 7850 # # Delete object from memory: <object> destroy # >> ::xotcl::Object isobject ::7848 = 1 >> set p1_id [$p1 object_id] = 7848 >> ::xo::db::Class exists_in_db -id 7848 = 1 >> $p1 destroy >> ::xotcl::Object isobject ::7848 = 0 >> ::xo::db::Class exists_in_db -id 7848 = 1 # # Delete object from memory and database: <object> delete # >> ::xotcl::Object isobject ::7849 = 1 >> set p2_id [$p2 object_id] = 7849 >> ::xo::db::Class exists_in_db -id 7849 = 1 >> $p2 delete >> ::xotcl::Object isobject ::7849 = 0 >> ::xo::db::Class exists_in_db -id 7849 = 0 .. Fetch employee with id 7848 again from the database: >> set p [::xo::db::Class get_instance_from_db -id 7848] = ::7848 .. show contents of ::7848 ::demo::Employee create ::7848 -noinit \ -set object_title {Employee 7848} \ -set salary {} \ -set employee_id 7848 \ -set dept_nr 0 \ -set name Neophytos \ -set age 25 \ -set projects {} \ -set object_id 7848 \ -set person_id 7848 .. The object ::7848 was fetched from the database using the .. automatically created query: SELECT demo_employee.employee_id,demo_employee.salary, demo_employee.dept_nr, demo_person.pname AS name,demo_person.age, demo_person.projects,demo_person.person_id, acs_objects.title AS object_title,acs_objects.object_id FROM demo_employee,demo_person,acs_objects WHERE employee_id = 7848 and person_id = employee_id and object_id = employee_id >> ::7848 destroy .. Query the instances from the database .. Instances are queried by default based on the following, .. automatically generated SQL Query: >> ::demo::Employee instance_select_query = SELECT demo_employee.employee_id,demo_employee.salary, demo_employee.dept_nr, demo_person.pname AS name,demo_person.age, demo_person.projects, demo_person.person_id, acs_objects.title AS object_title,acs_objects.object_id FROM demo_employee,demo_person,acs_objects WHERE person_id = employee_id and object_id = employee_id .. Query the object using this query into a set of objects. >> set s [::demo::Employee get_instances_from_db] = ::xotcl::__#H .. The result is an OrderedComposite. Children can be accessed .. via the 'children' method: >> llength [$s children] = 2 .. serialize all children ::xotcl::Object create ::xotcl::__#I -noinit \ -set __parent ::xotcl::__#H \ -set object_title {Employee 7848} \ -set dept_nr 0 \ -set salary {} \ -set employee_id 7848 \ -set age 25 \ -set name Neophytos \ -set projects {} \ -set object_id 7848 \ -set person_id 7848 ::xotcl::Object create ::xotcl::__#J -noinit \ -set __parent ::xotcl::__#H \ -set object_title {Employee 7850} \ -set dept_nr 0 \ -set salary 100 \ -set employee_id 7850 \ -set age 7 \ -set name Nora \ -set projects {} \ -set object_id 7850 \ -set person_id 7850 .. The instance_select_query can be configured in many ways. .. Here we use the '-count true' option to return just the number .. of solutions: >> ::demo::Employee instance_select_query -count true = SELECT count(*) FROM demo_employee,demo_person,acs_objects WHERE person_id = employee_id and object_id = employee_id >> db_string cnt [::demo::Employee instance_select_query -count true] = 2 .. The same queries for ::demo::Person instead of ::demo::Employee. .. Note, that we get the person data of employees as well, since .. employees are as well persons (i.e. a specialization of person): >> ::demo::Person instance_select_query = SELECT demo_person.pname AS name,demo_person.age, demo_person.projects,demo_person.person_id, acs_objects.title AS object_title,acs_objects.object_id FROM demo_person,acs_objects WHERE object_id = person_id >> set s [::demo::Person get_instances_from_db] = ::xotcl::__#M >> llength [$s children] = 3 ::xotcl::Object create ::xotcl::__#N -noinit \ -set __parent ::xotcl::__#M \ -set object_title {Person 7847} \ -set age 105 \ -set name Gustaf \ -set projects {} \ -set object_id 7847 \ -set person_id 7847 ::xotcl::Object create ::xotcl::__#O -noinit \ -set __parent ::xotcl::__#M \ -set object_title {Employee 7848} \ -set age 25 \ -set name Neophytos \ -set projects {} \ -set object_id 7848 \ -set person_id 7848 ::xotcl::Object create ::xotcl::__#P -noinit \ -set __parent ::xotcl::__#M \ -set object_title {Employee 7850} \ -set age 7 \ -set name Nora \ -set projects {} \ -set object_id 7850 \ -set person_id 7850 TODO: explain more options for instance_select_query and get_instances_from_db ############################################################ # # 3) Create XOTcl classes from existing ACS Object Types # and ACS Attributes based on the definitions in the # database >> set c [::xo::db::Class get_class_from_db -object_type party] = ::xo::db::party .. XOTcl class ::xo::db::party created (superclass ::xo::db::Object) .. SQL attributes: >> ::xo::db::party array names db_slot = email party_id url >> set c [::xo::db::Class get_class_from_db -object_type person] = ::xo::db::person .. XOTcl class ::xo::db::person created (superclass ::xo::db::party) .. SQL attributes: >> ::xo::db::person array names db_slot = last_name first_names person_id >> set c [::xo::db::Class get_class_from_db -object_type user] = ::xo::db::user .. XOTcl class ::xo::db::user created (superclass ::xo::db::person) .. SQL attributes: >> ::xo::db::user array names db_slot = user_id >> set c [::xo::db::Class get_class_from_db -object_type group] = ::xo::db::group .. XOTcl class ::xo::db::group created (superclass ::xo::db::party) .. SQL attributes: >> ::xo::db::group array names db_slot = group_name group_id .. Create XOTcl object for user_id = 0 .. please log in >> set u [::xo::db::Class get_instance_from_db -id $myuser] = ::0 .. ::0 created, class ::xo::db::user .. class hierarchy for ::0 ::xo::db::user ::xo::db::person ::xo::db::party ::xo::db::Object ::xotcl::Object .. show contents of ::0 ::xo::db::user create ::0 -noinit \ -set object_title Unregistered Visitor \ -set party_id 0 \ -set email {} \ -set last_name Visitor \ -set object_id 0 \ -set url {} \ -set person_id 0 \ -set first_names Unregistered \ -set user_id 0 .. The object ::0 was fetched from the database using the .. automatically created query: SELECT users.user_id,persons.last_name,persons.first_names, persons.person_id,parties.email, parties.party_id,parties.url, acs_objects.title AS object_title,acs_objects.object_id FROM users,persons,parties,acs_objects WHERE user_id = 0 and person_id = user_id and party_id = user_id and object_id = user_id .. Now we modify the user. To be on the safe side, we .. a) save firstly the original URL of the current user, then .. b) we set it to a new value and save the user information .. to the database .. c) delete the user object in memory .. d) load it again from the database to see the modified data .. e) reset it to the original value and save it again to restore .. the original state .. Step a: >> set url [::0 url] .. Step b: >> ::0 url https://openacs.org/ = https://openacs.org/ >> ::0 save .. Step c: >> ::0 destroy .. Step d: >> set u [::xo::db::Class get_instance_from_db -id $myuser] = ::0 .. show contents of ::0 ::xo::db::user create ::0 -noinit \ -set object_title Unregistered Visitor \ -set party_id 0 \ -set email {} \ -set last_name Visitor \ -set object_id 0 \ -set url https://openacs.org" \ -set person_id 0 \ -set first_names Unregistered \ -set user_id 0 .. Step e: >> ::0 url $url >> ::0 save >> ::0 destroy >> set u [::xo::db::Class get_instance_from_db -id $myuser] = ::0 .. show contents of ::0 ::xo::db::user create ::0 -noinit \ -set object_title Unregistered Visitor \ -set party_id 0 \ -set email {} \ -set last_name Visitor \ -set object_id 0 \ -set url {} \ -set person_id 0 \ -set first_names Unregistered \ -set user_id 0 ############################################################ # # 4) Use the generic Content Repository interface (using # just cr_items and cr_revisions) # .. check, if cr_item with name 'testing' exists >> set item_id [::xo::db::CrClass lookup -name testing] = 7841 .. yes, it exists; delete it without instantiating >> ::xo::db::CrItem delete -item_id 7841 .. create item >> set i [::xo::db::CrItem new -name "testing" -title "Title of Hello World" -text "Hello World" ] = ::xotcl::__#a >> $i save_new = 7852 .. show contents of item ::xo::db::CrItem create ::xotcl::__#a -noinit \ -set creation_date {2007-09-03 23:28:26.727511+02} \ -set creation_user 0 \ -set object_id 7852 \ -set description {} \ -set text {Hello World} \ -set nls_language en_US \ -set mime_type text/plain \ -set name testing \ -set publish_status ready \ -set title {Title of Hello World} \ -set revision_id 7851 \ -set item_id 7852 \ -set last_modified {2007-09-03 23:28:26.727511+02} \ -set parent_id -100 .. update content >> ::xotcl::__#a append text {... more text.. more text.. more text.} = Hello World... more text.. more text.. more text. >> ::xotcl::__#a append title { - v2} = Title of Hello World - v2 .. show modified contents of item ::xo::db::CrItem create ::xotcl::__#a -noinit \ -set creation_date {2007-09-03 23:28:26.727511+02} \ -set creation_user 0 \ -set object_id 7852 \ -set description {} \ -set text {Hello World... more text.. more text.. more text.} \ -set nls_language en_US \ -set mime_type text/plain \ -set name testing \ -set publish_status ready \ -set title {Title of Hello World - v2} \ -set revision_id 7851 \ -set item_id 7852 \ -set last_modified {2007-09-03 23:28:26.727511+02} \ -set parent_id -100 >> ::xotcl::__#a save = 7852 .. method 'save' creates a new revision; see changed revision_id below ::xo::db::CrItem create ::xotcl::__#a -noinit \ -set creation_date {2007-09-03 23:28:26.727511+02} \ -set creation_user 0 \ -set object_id 7852 \ -set description {} \ -set text {Hello World... more text.. more text.. more text.} \ -set nls_language en_US \ -set mime_type text/plain \ -set name testing \ -set publish_status ready \ -set title {Title of Hello World - v2} \ -set revision_id 7853 \ -set item_id 7852 \ -set last_modified {2007-09-03 23:28:26.727511+02} \ -set parent_id -100 >> set item_id 7852 = 7852 >> ::xotcl::__#a destroy >> set i [::xo::db::CrClass get_instance_from_db -item_id 7852] = ::7852 ::xo::db::CrItem create ::7852 -noinit \ -set object_type content_revision \ -set creation_date {2007-09-03 23:28:26.727511+02} \ -set object_id 7853 \ -set creation_user 0 \ -set description {} \ -set text {Hello World... more text.. more text.. more text.} \ -set nls_language en_US \ -set package_id 492 \ -set name testing \ -set mime_type text/plain \ -set publish_status ready \ -set title {Title of Hello World - v2} \ -set item_id 7852 \ -set revision_id 7853 \ -set last_modified {2007-09-03 23:28:26.750059+02} \ -set parent_id -100 ############################################################ # # 5) Create new application classes by sub-typing the # Content Repository, adding additional attributes # .. We create a subclass of ::xo::db::CrItem called ::demo::Page .. which has a few more attributes. Actually, this class is very .. similar to ::xowiki::Page. Again, we define an XOTcl class .. ::demo::Page which creates the ACS Object Type, the ACS .. attributes and the table, if necessary. >> # content class very similar to ::xowiki::Page ::xo::db::CrClass create Page \ -superclass ::xo::db::CrItem \ -pretty_name "demo Page" \ -mime_type text/html \ -slots { if {[::xo::db::has_ltree]} { ::xo::db::CrAttribute create page_order \ -sqltype ltree -validator page_order \ -default "" } ::xo::db::CrAttribute create creator \ -column_name creator_string } = ::demo::Page .. The sql_attribute_names for content items contain .. the attributes of all supertypes: >> ::demo::Page array names db_slot = creator page_order page_id object_id description text object_title nls_language mime_type name title item_id revision_id >> ::demo::Page info slots = ::demo::Page::slot::creator ::demo::Page::slot::page_order ::demo::Page::slot::mime_type ::demo::Page::slot::page_id >> ::xo::slotobjects ::demo::Page = ::demo::Page::slot::creator ::demo::Page::slot::page_order ::demo::Page::slot::mime_type ::demo::Page::slot::page_id ::xo::db::CrItem::slot::package_id ::xo::db::CrItem::slot::nls_language ::xo::db::CrItem::slot::description ::xo::db::CrItem::slot::name ::xo::db::CrItem::slot::text ::xo::db::CrItem::slot::publish_status ::xo::db::CrItem::slot::parent_id ::xo::db::CrItem::slot::title ::xo::db::CrItem::slot::revision_id ::xo::db::CrItem::slot::item_id ::xo::db::Object::slot::object_title ::xo::db::Object::slot::object_id .. create page >> set i [Page new \ -name "page0" \ -title "Joke of the Month" \ -creator "GN" \ -text "Three cannibals meet in a NYC subway station..." ] = ::xotcl::__#j >> $i save_new = 7855 .. show contents of page object ::xotcl::__#j (including creator and page_order, .. when ltree is available) ::demo::Page create ::xotcl::__#j -noinit \ -set page_order {} \ -set creator GN \ -set page_id {} \ -set creation_date {2007-09-03 23:28:29.149591+02} \ -set object_id 7855 \ -set creation_user 0 \ -set description {} \ -set text {Three cannibals meet in a NYC subway station...} \ -set nls_language en_US \ -set mime_type text/html \ -set name page0 \ -set publish_status ready \ -set title {Joke of the Month} \ -set revision_id 7854 \ -set item_id 7855 \ -set last_modified {2007-09-03 23:28:29.149591+02} \ -set parent_id -100 >> set item_id [::xotcl::__#j item_id] = 7855 >> ::xotcl::__#j destroy >> ::demo::Page get_instance_from_db -item_id 7855 = ::7855 ::demo::Page create ::7855 -noinit \ -set page_order {} \ -set object_type ::demo::Page \ -set page_id 7854 \ -set creation_date {2007-09-03 23:28:29.149591+02} \ -set object_id 7854 \ -set creation_user 0 \ -set description {} \ -set text {Three cannibals meet in a NYC subway station...} \ -set nls_language en_US \ -set package_id 492 \ -set name page0 \ -set mime_type text/html \ -set title {Joke of the Month} \ -set publish_status ready \ -set revision_id 7854 \ -set item_id 7855 \ -set last_modified {2007-09-03 23:28:29.149591+02} \ -set parent_id -100 \ -set creator_string GN
Created by Rocael Hernández Rizzardini, last modified by Gustaf Neumann 14 Feb 2018, at 09:07 AM
These general rules are applicable to Tcl & SQL.
Also, the 80 character limit is important because the OpenACS API browser (at /api-doc/) presents documentation defined in ad_proc's doc_string declaration in html format; whereas ad_proc's body of code is presented wrapped in PRE tags. Longer code lines result in API documentation pages (and their paragraphs) stretching outside of typical browser window width.
If you are using Emacs editor, you usually are on the safe side with regards to Tcl, as the tcl-mode indents just as we want it. ADP pages are formatted correctly when editor is in html-mode.
There is an OpenACS mode for emacs OpenACS mode for Emacs which has features to help meet formatting standards.
One can add the following stanza to the end of a .tcl file, when using Emacs, to avoid, that spaces are changed again into tabs.
# # Local variables: # mode: tcl # tcl-indent-level: 4 # indent-tabs-mode: nil # End:
Allowing user input to include images, see patch in this discussion thread: https://openacs.org/forums/message-view?message_id=182057