View · Index
No registered users in community rubick
in last 10 minutes

Weblog Page

Showing 1 - 10 of 66 Postings (summary)

Availability of packages

Created by Gustaf Neumann, last modified by Gustaf Neumann 24 Oct 2019, at 10:38 AM

OpenACS Package: see https://openacs.org/xowiki/packages

Features

Created by Gustaf Neumann, last modified by Gustaf Neumann 24 Oct 2019, at 10:37 AM

 

Resources and References

Created by Gustaf Neumann, last modified by Gustaf Neumann 24 Oct 2019, at 10:31 AM

How to get help and find the information you need.

  • First of all, try to track down documentation for what you're looking for. The first place to look is in the official OpenACS documentation. Familiarize yourself with this documentation, especially the API browser, which shows you all the commands you can run. Look at the package documentation (and note that sadly, some of the packages don't have documentation). Notice that there is documentation not just for getting installed, but also for developing an OpenACS package.
  • Second, try searching on Google. Try your search, but include the word OpenACS in the query. This finds answers a surprisingly high amount of the time.
  • Third, look through the forums. Use the search functionality to try and find what you're looking for.
  • Fourth, look through the websites listed below. These are websites set up by OpenACS hackers as documentation and how-to guides:
  • Fifth, if your question is a quick one, then try asking on the OpenACS IRC channel.
  • Sixth, try asking your question on the OpenACS forums. Make sure you clearly describe what your problem is, what version of OpenACS you're using, what you did before and after the problem, and what the error message is. Show relevant portions of your error log. If you want people to be helpful and take their time on your question, make sure you take the time to ask the question in a way that someone can give you a meaningful answer.
  • See below for more helpful resources

Introductions to OpenACS

Helping out with OpenACS

  • How to get started as a worker bee for OpenACS. This describes how you can contribute if you have time and want to learn more about OpenACS by helping out with simple tasks to improve the toolkit

History of OpenACS

 
 

OpenACS Web Development

Created by Gustaf Neumann, last modified by Gustaf Neumann 24 Oct 2019, at 10:29 AM

 

Open source, transparent development

Created by Gustaf Neumann, last modified by Gustaf Neumann 06 Oct 2019, at 12:47 PM

 

Upgrading ACS to OpenACS

Created by Gustaf Neumann, last modified by Gustaf Neumann 27 Sep 2019, at 09:35 AM

This page describes how I am upgrading an ACS 3.4.10 Oracle installation to a OpenACS 4.6.2 Postgres installation.

Here are some other webpages to look at:

http://jongriffin.com/static/openacs/acs-conversion/upgrade
http://openacs.org/faq/one-faq?faq_id=43841
http://openacs.org/doc/openacs-3/html/oracle-to-pg-porting.html
http://grumet.net/random/mikes-migration-guide/
Ola's posting on upgrading OpenACS 3 to 4
Using two databases with OpenACS
Sloan's upgrade scripts -- woohoo! Even better, Sloan's scripts that upgraded their 3.4 installation to OpenACS 4.0! (See this link also: thread announcing file upload. This is how I would do things if I could go back and do it again

 

Note on sequence caching

When you're doing any sort of migration, apparently getting the next sequence values is one of the more expensive operations you'll do. To speed things up considerably, you can cache your sequence values. Both Oracle and Postgres let you do this. (caching sequences in postgres)

Upgrading users

What I'm doing is set up a table that contains the old and new user_ids (so I can map the old values to the new values). It's nothing complicated, just a table with the old value and the new value. I call it upgrade-user-map. Then there is a little utility function which you can use to get the new user_id when you're importing the data. set new_user_id [upgrade_user_id $old_user_id] will do it.

/sql/postgresql/upgrade-user-map-create.sql

--
-- packages/upgrade-user-map-create/sql/postgresql/upgrade-user-map-create.sql
--
-- @author jader@bread.com
-- @creation-date 2003-03-21
-- @cvs-id $Id: upgrade-user-map-create.sql,v 1.1 2003/03/21 22:49:01 oacs Exp $
--

create table upgrade_user_map (
    old_id                 integer
                           constraint upgrade_user_map_old_id_nn
                           not null,
    new_id                 integer
                           constraint upgrade_user_map_new_id_fk
                           references users(user_id)
);

/sql/postgresql/upgrade-user-map-drop.sql

drop table upgrade_user_map;

/tcl/upgrade-user-map-procs.tcl

# /packages/upgrade-user-map/tcl/upgrade-user-map-procs.tcl

ad_library {

    Procs for upgrade of users, and for upgrade for ACS 3.4 intranet

    @author Jade Rubick (jader@bread.com)
    @creation-date 2003-03-31
    @cvs-id $Id$
}

ad_proc -public upgrade_user_id {
    old_user_id
} {
    returns the new user id when given the old
} {
    set user_id [db_string get_new_user_id {
        SELECT
        u.user_id
        FROM 
        users u,
        upgrade_user_map um
        WHERE
        u.user_id = um.new_id and
        um.old_id = :old_user_id
    } -default "0"]

    return $user_id
}

Port common functions

Another thing to do to make the transition easier is to port some common functions over from ACS 3.4. They can be marked as deprecated, so that as you work on your packages in the future, you can gradually move to a more pure OpenACS model. I put these inside the upgrade_user_map package, /tcl/upgrade-user-map-procs.tcl file. Then all packages that I port to OpenACS will have a dependency on the upgrade-user-map package. This will make it easy to keep track of which packages depend on these functions, and I can explicitly go through later and remove than dependency. Note that these functions depend on acs-tcl, which I believe is going to be removed from OpenACS eventually. More work later!

Here are a list of functions I ported into OpenACS:

  • im_header
  • im_footer
  • im_return_template

Ported functions

im_header
ad_proc -public -deprecated im_header {
    {-page_title ""}
    {-page_focus ""}
    {-context_bar ""}
    {-extra_stuff_for_document_head ""}
} {
    Shows page headers for a legacy page ported from an ACS 3.4 installation
} {
return "
[ad_header -focus $page_focus $page_title $extra_stuff_for_document_head]
<font size=4><b>$page_title</b></font>
<br><font size=2>$context_bar</font>
<hr>
<link rel=\"stylesheet\" href=\"/style.css\" type=\"text/css\">
"
}
im_footer
ad_proc -public -deprecated im_footer {
} {
    Shows the footer for the legacy page ported from an ACS 3.4 installation
} {
    return " <hr><font size=-2>
Integrated Bakery Resources ©2003
<a href=/register/logout>log out</a>
</font>"
}
im_return_template
ad_proc -public -deprecated im_return_template {
} {
    Returns a legacy page ported from an ACS 3.4 installation
} {
    uplevel { 
        return "
        [im_header -page_title [value_if_exists page_title] -page_focus [value_if_exists page_focus] -context_bar [value_if_exists context_bar] -extra_stuff_for_document_head [value_if_exists extra_stuff_for_document_head]]
        [value_if_exists page_body]
        [value_if_exists page_content]
        [im_footer]
        "
    }
}

Checklist of things to check for in each ported file

  • Use new data model
  • Add in permissions

Exporting data from ACS 3.4

The approach I use is easy, but maybe not as elegant as you might like. I export each table to a file, and then import that file into Postgres. Another way you could do this is to define an extra database pool. I'm not sure if this would work with a different database, however.

I am creating an export script for every table I need from the old Intranet, and an import script for every table in the new Intranet. This will be easiest for my own custom packages -- much more difficult for things like the projects and user_groups tables in ACS 3.4.10. I haven't quite solved that problem yet.

The export scripts looks something like this:

# /packages/packagename/www/upgrade/export.tcl

ad_page_contract {
    page which exports the packagename table

    @author jader@bread.com
    @cvs-id $Id$
    @creation-date 3/14/03

} {
}

ns_write "<html><table>"

set file_stream [open /tmp/packagename.export w]

db_foreach get_brand "
...
" {
     puts $file_stream "@%@%@<oneitem>$oneitemvalue</oneitem>..."

     ns_write "<tr><td>$oneitemvalue</td>..."
}

close $file_stream

ns_write "</table></html>"


----------


One caveat: this works as long as the amount of data shown on your page does not overwhelm your browser. This is not a very robust solution. You don't have to display everything out to the browser after you've debugged it. Take out the portion with the ns_write inside the db_foreach statement. Also, make sure you use a browser that doesn't timeout within 60 seconds, like the version of Safari I'm currently using.

Another thing I did with the export scripts is at the bottom of each script, I redirect to the next export script. Add this before the </html>:

<meta HTTP-EQUIV=\"REFRESH\" CONTENT=\"3;URL=http://path/to/next/upgrade/export\">

Importing into OpenACS

The import script (unfinished) looks like this so far:
# /packages/packagename/www/upgrade/import.tcl

ad_page_contract {
    page which imports the packagename table

    @author jader@bread.com
    @cvs-id $Id$
    @creation-date 3/14/03

} {
}


set file_stream [open /tmp/packagename.export r]

set the_whole_file [read -nonewline $file_stream]

close $file_stream

set list_of_lines [split $the_whole_file "@%@%@"]

ns_write "<html><table border=1 cellspacing=0>"

db_dml delete_all "delete from packagename"

# determine highest value for sequence
set highest_seq_value -1

foreach line $list_of_lines {

    if {[exists_and_not_null line]} {
        regexp {<oneitem>(.*)</oneitem>} $line match oneitem

        ns_write "
        <tr>
        <td>$oneitem

        if {$index_value > $highest_seq_value} {
            set highest_seq_value $index_value
        }

        # get the user_id from a user_map table
        set user_id [db_string get_user_id xxxxxxx]

        db_exec_plsql new_packagetablename "
        select packagename__new(...)"
    }
}

Other references

From Mike Bonnet and Andrew Grummet's migration page, I stole this script for Oracle, which shows you the references to any given table. This really helps in deciding which item to port when:
select (select table_name || '.' || column_name from user_cons_columns where constraint_name = uc.constraint_name) 
              || ' references ' ||
             (select table_name || '.' || column_name from user_cons_columns where constraint_name = uc.r_constraint_name) as references
        from user_constraints uc 
       where constraint_type = 'R' 
        and table_name = upper('&table_name')

select (select table_name || '.' || column_name from user_cons_columns where constraint_name = uc.r_constraint_name) 
              || ' is referenced by ' ||
             (select table_name || '.' || column_name from user_cons_columns where constraint_name = uc.constraint_name) as referenced_by
        from user_constraints uc 
       where constraint_type = 'R' 
        and r_constraint_name in (select constraint_name from user_constraints where table_name = upper('&table_name'));

An earlier version of this document is at: http://openacs.org/forums/message-view?message_id=88229.

 
 

Jerry Asher's Virtual Hosting Article

Created by Gustaf Neumann, last modified by Gustaf Neumann 26 Sep 2019, at 09:00 AM

HOWTO - Virtual Hosting in AOLserver original URL

 

Virtual Hosting in AOLserver

how to host multiple ACS/OpenACS installations with one static IP


What's in here:

I describe how to use NaviServer /AOLserver to serve content for many different sites with each site sharing a port. This is can be useful
  • for hosting multiple ACS or OpenACS sites when you have only one static IP.

     

  • for ISP's or Web Hosting companies whose allocated range of IP addresses is small and who want each site to be available from port 80.

     

  • when your primary web service is composed of several cooperating web server technologies and yet you still want to present one unified address to your visitors. For example, your web service may be comprised of an AOLserver ACS, an Apache CGI module, and even a Tomcat servlet engine.

     

  • when you worry that your user's firewalls will keep them from accessing just *any* port and so you want all your sites to be available from just one port.

 

Table of Contents

Example
What kind of virtual hosting do you need?
DNS and email
Implementing NSVHR
The basic architecture
Get and Install AOLserver
Applying the patches
Configuration
Load the modules
Registering methods
Nsvhr Configuration
Nsunix Configuration
Nssock Configuration
Support
An alternative: Implementing VAT
Multiple ACS installations
New Features for AOLserver 3.3ad13
The future
Appendix I: nsunix or nssock
Appendix II: aolserver or apache or squid
Appendix III: Upgrading to AOLserver 3.3ad13
Appendix IV: Security
securing the nscp port with ssh
Related Links

Example

You're not yet funded but you have a great plan. Host a series of websites about the FBI and the Sopranos. To corner the market, you'll also host websites discussing how much the fbi and the sopranos suck. With thousands of uh, hits per day on your site, what VC wouldn't make you an offer you couldn't refuse?

So let's host four websites.

  1. rico.fbi.gov: a large dynamic website hosting an FBI chat room and other FBI applications.
  2. Sopranos.org: a large dynamic website hosting a Sopranos fanclub. Ecommerce too: sell comemorative hits (fedex dead fishes, and rubber galoshes to your friends.)
  3. fbisucks.org: a small site for now, mainly for members of the aluminum foil beanie crowd that know the fbi and cia are beaming thoughts into their head.
  4. sopranossuck.org: a small site that focuses on touched by an angel.

What kind of virtual hosting do you need?

If you want to virtually host from two to 64K static sites, Apache is certainly a better choice. You'll find lots of sources and tools to help you do that. If you want to virtually host from two to 64K dynamic sites that have cgi capabilities mixed with mysql (for example), Apache is probably a better choice, because once again, there are lots of tools and people out there who can aid you.

If you want to virtually host from two to 64K sites that have dynamic capabilities and that benefit from a fast scripting language and wonderful database pooling, than AOLserver is the server for you.

VHOSTing with AOLserver

If all your sites can share the same Tcl libraries, then you can look for "simple", single process forms of virtual hosting most commonly referred to as vhost. With this form, all of your sites will be served within the same AOLserver process.

If you search the aolserver.com list archives, or the forums at OpenACS, or at NaviServer, or just google for VHOST and AOLserver, you'll probably find what you need.

I've implemented Tom Jackson's VAT vhosting module available from ZMBH. You can find some details below.

NSVHR hosting with AOLserver

If you have sites that require custom Tcl Libraries that you don't want to share or can't share, then NSVHR based hosting is your solution. With NSVHR based hosting, each new site gets its own AOLserver process to serve the site. This can be used to help secure virtual hosts, and to separate reliability issues of one site from impacting another site. This solution also lets you stop and start various sites independently.

If you want to virtually host from two to a few dozen (my guess only) sites that each have their own tcl library, where each library might conflict (in tcl namespace) with another sites tcl library (say you are virtually hosting four customized ACS sites, one running ACS 4, one running ACS 3.4.6, and two running ACS 3.4.10).

It's also a good solution for running several sites based on differing ACS versions. Do you have one customer that relies on legacy data that can't be upgraded (yet) while another customer wants the latest version of the ACS? No problem, they can each run in a different Tcl context with NSVHR based virtual hosting.

Want to run sites identical in most respects except for the database user they connect as? NSVHR based virtual hosting.

Do you want to guarantee that one site cannot (maliciously or otherwise) read or write to another site's pages, or database? NSVHR virtual hosting.

Are you wondering whether to implement nsvhr using nssock or using nsunix? Don't worry, use either and just get your site up running for now.

There is a fair chance you can get nsvhr/nssock running without needing to patch AOLserver 3.2. There are a few bugs. You may not notice them. A few visitors might. But I have some patches that enable nsvhr/nsunix to work and improve nsvhr/nssock.

nsunix.... should be more efficient than nssock (requiring less copying of data). should allow you more concurrent connections (as it requires fewer open files per connection since it requires less copying of data). may provide better logging if you care since each virtually hosted server can get its own log file with the actual ip of the client. is less buggy when AOLserver is patched than running nsvhr/nssock when AOLserver is not patched. maybe more buggy than running nsvhr/nssock when AOLserver is patched. ...has been implemented at hundreds of sites.

 

DNS and MAIL

The virtual hosting discussed here is primarily name based, it supports multiple sites that each have different domain names. It follows that each virtual host you add will need DNS support.

If the new domain is just a subsite of an existing domain, you will probably be adding a CNAME to your existing IP address. I usually just add a CNAME that points to my primary host.

Assuming I am already handling fbi.gov, then to support www.fbi.gov, I add:

web             IN      A       192.168.0.16
www             IN      CNAME   web.fbi.gov.
But if you're adding an entirely new domain, then you'll need to add a whole new zone. I'll leave you to find a better DNS reference than I.

Don't forget to add support for a new domain to your mailer so that you can receive emails for postmaster@sopranos.org or customerservice@fbi.gov. If you're using qmail, it's pretty simple. Add the new domain to your rcpthosts, and either to your virtualdomains or your locals.

Implementing NSVHR

What you'll do is set up three separate AOLservers that will coordinate with each other: master, fbi, and sopranos. In general if you want to host n websites, you will need to set up n + 1 AOLservers.

  1. master will become a proxying server that will actually receive ALL of your visitors requests and somehow transfer each request to either
  2. fbi will host the fbi chat room and will handle all requests to web.rico.fbi.gov
  3. sopranos will host the soprano intranet and will handle requests to web.sopranos.org fbi or sopranos
Somehow transfer will differ depending on your choice of nsunix or nssock to implement your proxy.

If you use nssock, you will be choosing an AOL supported mechanism, that transfers every request and response by copying that request and response. Upside: it's AOL approved. Downsides: it potentially doesn't have the efficiency of the nsunix approach and it obscures the access log files of the proxied servers (fbi and sopranos) by recording the ip address of the master server as the ip address of your actual visitor.

If you use nsunix, you will be choosing mechanism a AOL created, but abandoned. With this approach, the request is basically copied but the response is sent directly back from the proxied server to the client browser. This is implemented with the magic of Unix domain sockets which allow one process to pass an open file descriptor to another process. Upside: it has much higher performance (especially when responses are typically "biggish") and your access logs record the correct ip of your visitor. Downside: you may be accepting me as your web savior. (Uh, since I fixed nsunix and so far, everyone has been relying on me for bug fixes.)

The basic architecture


Thanks to Tim Darling for this diagram of the basic architecture. Here main is master, foo is fbi, and bar is sopranos.
 
 

Get and Install AOLserver

The nsvhr/nssock/nsvhr patches I have created were created using AOLserver 3.3ad13, the ArsDigita release of AOLserver. I suspect the patches will apply with few problems when using the AOLserver 3.3 distribution.

Unpack the distribution. The ArsDigita distribution usually likes to unpack into a directory named root, and likes to install the binaries and server files into /usr/local/aolserver.

I usually unpack the distribution into /tmp and then mv that root directory to /web and rename it to indicate which version of AOLserver I am using.

These are the commands I usually take, but I'm just typing here, not testing them, so you may need to change these commands somewhat.

  $ cd /tmp
  $ tar zxvf aolserver-src.tar.gz
  $ mv /tmp/root /web/ad33.13
  $ cd /web/ad33.13/aolserver
  
Now the first thing you should probably do, is build the basic system and get it working. Do that now, and come back after you get AOLserver serving it's basic page. (After starting it, it should serve up pages on port 8000). Again, the commands are similar to this.
  $ make
  $ make install
  $ cd /usr/local/aolserver/bin
  $ ./nsd8x -ft ../sample-config.tcl -u nsadmin
Hey, you're back. Congratulations for getting AOLserver up and running. The next step is to apply my modifications.

You need some patches

And so I have created some for AOLserverVirtualHostingPatches. These patches have been developed over about 18 months and have been used world wide. They've been downloaded thousands of times, and I mostly hear they work. And I hope they work for you.

The exact instructions are included in the release notes of the patches. The basic procedure is to use the patch command to apply the patches, and then to rebuild all of AOLserver. Nb: it's very important that you rebuild all of AOLserver as some critical internal data structures have new slots.

Assuming you downloaded the patches to /tmp/aolserver3.3-vhr.patch, the following commands should apply the patch, but again, look at the exact instructions contained in the release notes.

  $ cd /web/ad33.13/aolserver
  $ patch -p5 <aolserver3.3-vhr.patch
The patch should apply with no errors and few messages.

Create the nsvhr/nsunix modules

I believe the default for AOLserver 3.2 is to create these modules, so you should be all set. Not so for AOLserver 3.3. If you need to create them you need to visit the top level AOLserver Makefile, and add nsvhr and nsunix to the Modules definition.

In /web/ad33.13/aolserver/Makefile:

MODULES   =  nssock nscgi nscp nslog nsperm nsext nspd nsvhr nsunix
Now rebuild all of AOLserver and install.
  $ make clean
  $ make
  $ make install

Configuration

The key to configuring AOLserver is through a configuration file. This file has been named in the past as nsd.tcl, config.tcl, and sample-config.tcl.

You will need to create one of these files for each AOLserver that you start, you may as well name it with the name of your server.

 

Load the modules

Now that you have rebuilt AOLserver, you should find two new modules in your AOLserver bin directory: nsvhr.so and nsunix.so. You want to direct your master server to load the nsvhr module and each of your other servers to load the nsunix module.
  ns_section ns/server/master/modules
  ns_param   nsvhr                    ${bindir}/nsvhr.so

  ns_section ns/server/fbi/modules
  ns_param   nsvhr                    ${bindir}/nsunix.so

  ns_section ns/server/sopranos/modules
  ns_param   nsvhr                    ${bindir}/nsunix.so

Registering methods

You have to tell the nsvhr module which methods (POST, GET, HEAD, PUT) to proxy.
  ns_section ns/server/${servername}/module/nsvhr
  ns_param   method                  GET     ;# Methods allowed to proxy
  ns_param   method                  POST    ;# Methods allowed to proxy (can have
  ns_param   method                  HEAD    ;# Methods allowed to proxy

NSVHR Configuration

Nsvhr, desires to take over your master server entirely. Your master should serve no requests by itself, but should server all pages using one of the proxied servers. Unfortunately, I know of no way to ensure this, but experience shows you need to keep the master from sourcing tcl pages, and you need to turn adp processing off. No worries! Your proxied servers will handle these just fine. So make sure the following directives are commented out.
  ns_section ns/server/master
  ns_param   EnableTclPages Off

  #  adp must be disabled for nsvhr to work
  #  I don't know how to EXPLICITLY turn it off,
  #  but make sure it's hasn't been turned on by default!

  #  ns_section ns/server/master/adp
  #  ns_param Map          /*.adp

  #  ns_section ns/server/master/adp/parsers
  #  ns_param fancy        .adp
  
You can also direct the nsvhr module on how to behave when errors occur, when AOLserver becomes busy, or when a request comes in with no HOST field.
  ns_section ns/server/${servername}/module/nsvhr
  ns_param   menuurl         url     ;# Redirect here if back-end times out
  ns_param   busyurl         url     ;# Redirect here if back-end times out
  ns_param   errorurl        url     ;# Redirect here on proxy errors
  ns_param   timeout         30      ;# Timeout waiting for back-end

NSUNIX, NSSOCK, or both?

This code has no guarantee. It's likely buggy. And so it's very useful to implement your system with both nsunix AND nssock. While nsunix should give you better performance, having an nssock backup is valuable.

Method One: nsvhr/nsunix

The master server loads the nsvhr module. Each server loads the nsunix module. Configure the nsvhr module to map requests from your domain to a "socket file"
  ns_section  ns/server/master/module/nsvhr/maps
  ns_param    web.sopranos.org      unix://sopranos.nsunix
  ns_param    web.sopranos.org:80   unix://sopranos.nsunix
  ns_param    web.rico.fbi.gov      unix://fbi.nsunix
  ns_param    web.rico.fbi.gov:80   unix://fbi.nsunix
Several utilities and browsers like wget seem to always include a port number. To support those utilities, it is necessary to include the maps that include port 80.

Configure the nsunix module with the hostname and the socketfile name.

  ns_section  ns/server/sopranos/module/nsunix
  ns_param    hostname              web.sopranos.org
  ns_param    socketfile            sopranos.nsunix
And finally, you will need to make sure the nsunix directory where the socket files live has been created. If your aolserver has been installed at /usr/local/aolserver, then check to make sure the directory /usr/local/aolserver/modules/nsunix has been created.

The master proxies the original requests to the server by reading the request and passing the file descriptor of the response to the right ACS server using Unix domain sockets. It is reasonably efficient. There is no copying of data (but there are probably several context switches involved with each request).

Because you are using nsunix and not nssock, you do not need any additional IP addresses.

Method Two: nsvhr/nssock and unroutable addresses

A less efficient mechanism is to configure the master to use nsvhr and the servers to use nssock. It's less efficient because the master sits between the browser and your server and will copy every request and each response that the server sends. In addition to the copies there are probably several context switches per request.

One benefit of this, is that your target server need not be AOLserver, and it does not even have to be on the same machine.

To do this, you can either place each ACS at an unroutable address such as 192.168.0.XXX, or at a different port as port 2080, 3080, etc. The choice is yours. These addresses will be used for socket communication ONLY on your machine, and if all works well, the clients will never see them.

This is relatively simple to configure.

Assuming you are at yourdomain.org and you have DNS CNAME records for sopranos.org and fbi.gov, then configure nsvhr as before, but this time use http-like urls:

    ns_section  ns/server/master/module/nsvhr/maps 
    ns_param    sopranos.org            http://www.sopranos.org:2080 
    ns_param    sopranos.org:80         http://www.sopranos.org:2080 
    ns_param    sopranos.org:2080       http://www.sopranos.org:2080 
    ns_param    www.sopranos.org        http://www.sopranos.org:2080 
    ns_param    www.sopranos.org:2080   http://www.sopranos.org:2080 
    ns_param    rico.fbi.gov:90         http://www.rico.fbi.gov:3080 
    ns_param    rico.fbi.gov            http://www.rico.fbi.gov:3080 
    ns_param    rico.fbi.gov:3080       http://www.rico.fbi.gov:3080 
    ns_param    www.rico.fbi.gov:80     http://www.rico.fbi.gov:3080 
    ns_param    www.rico.fbi.gov:3080   http://www.rico.fbi.gov:3080 

You also need to configure the nssock section of the servers to use the right names, addresses, and port numbers, but I'll leave that to you to determine.

Support

For support, you can try the following: If you have any suggestions, corrections, or additions to this howto, I encourage you to add a comment at the end of this howto.

An alternative: Implementing VAT

I implemented Tom Jackson's VAT hosting module in conjunction with nsvhr/nsunix style virtual hosting.

This yields an entirely AOLserver only solution in which

  • IP addresses are logged correctly (without further changes)
  • small static and dynamic sites can be handled by one AOLserver process
  • larger dynamic sites can each have their own AOLserver process
The basic strategy was to follow the usual nsvhr instructions, but to configure the master/module/nsvhr/maps such that smaller, static sites all map to the same unix domain socket:
ns_section ns/server/master/module/nsvhr/maps
ns_param fbisucks.gov     unix://sucks
ns_param sopranossuck.org unix://sucks
And then follow Tom's instructions to set up a VAT server (but implement that VAT server with an nsunix module fetching from sucks)
ns_section ns/server/vat/module/nsunix
ns_param socketfile sucks

Multiple ACS Installations

Follow the usual directions. For each installation, create a new user for that installation (and for ease of maintenance, create a new tablespace for that installation.)

Each ACS will have it's own separate AOLserver process and they can either share the same nsd.tcl file or have their own. When AOLserver starts you can either direct it to the appropriate nsd.tcl file with the -t flag, or you can tell it which server to start with the -s flag. Note: In AOLserver 3.3 and above, AOL has removed much of the functionality of the -s flag. It is now difficult to use one nsd.tcl file to define all of your server parameters.

Here are some excerpts

set server  [ns_info server]

# dbpass(username) password
set dbpass(master)   ""
set dbpass(sopranos) wokeupthismorning
set dbpass(fbi)      rico

# Global parameters
ns_section "ns/parameters"
ns_param   pidfile      "/home/nsadmin/a3/log/${server}.nspid"
ns_param   serverlog    "/home/nsadmin/a3/log/${server}.server.log"

ns_section ns/db/pool/main
ns_param User $server
ns_param Password $dbpass($server)

ns_section ns/db/pool/log
ns_param User $server
ns_param Password $dbpass($server)

#############################################
# one section good for all modules
# Access log -- nslog
ns_section "ns/server/${server}/module/nslog"
ns_param   file            "/home/nsadmin/a3/log/${server}.access.log"

ns_log notice "[ns_info config]: end $server config"

New Features for AOLserver 3.3ad13

  • Addition of X-FORWARDED-FOR headers within nsvhr and nssock to better make for better logs, use AOLserver ns_log's extendedheaders parameter to log the X-FORWARDED-FOR header within your proxy to include the IP address of the client browser.
  • Realtime addition/deletion of hosts (without requiring nsvhr server to restart) using the ns_vhr command

The future

In the future, I would like to see:
  • Database driven interface to defining and starting virtual hosts to get automate, clean up, and secure the nsd.tcl file.
  • nsunix functionality migrated INTO the nssock module
  • nsvhr/nsunix reverse proxying migrated to Apache
Care to help?

Appendix I: nsunix or nssock?

At the web/db forum at arsdigita, I was asked to explain a bit about unix domain sockets. This is what I wrote. It is quite likely inaccurate. All of what I know about UNIX domain sockets, I learned from Stephens books, (both Advanced Programming in the UNIX Environment, and UNIX Network Programming), AOLserver code, and a pithy comment from Rob Mayoff.

Google leads me to http://world.std.com/~jimf/papers/sockets/sockets.html, and http://www.ibr.cs.tu-bs.de/~harbaum/docs/bsd-tut/sockets.html, for some online tutorials, and http://www.dcc.ufba.br/~sandrosa/informatica/tutoriais/netguide/BOOKCHAPTER-8.html for a pretty complete text.

Basically, UNIX domain sockets are a style of interprocess communication that uses the socket API as its interface and structure. It allows two processes on the same machine to efficiently communicate by sending messages and one certain objects from one process to another. In particular, the one object that can be shared from one process to another is an open file descriptor. After reading just enough of a client browser's request to determine which host and therefore which client server AOLprocess should respond, Nsvhr/nsunix use this latter ability to pass the file descriptor from your machine to the client browser from the master proxy AOLserver process to the client server AOLserver process. The connection from nsvhr to nsunix is very brief, and lasts just long enough to pass the request headers and the file descriptor.

Once the file descriptor has been passed, the client AOLserver process can read the rest of the response itself, and will directly send the response back to the client.

In contrast, the better known Internet domain socket uses networking (basically serial copying) techniques. File descriptors cannot be passed, but you can chain multiple pipes together. That is what the nsvhr/nssock proxy combination do. Same as before, nsvhr in the master AOLserver process receives a request and determines who to send it to. This time, though, it sends it by opening up a much longer lasting connection to the nssock module of the AOLserver client process. This connection will be open for the time it takes the client AOLserver process to read the request as well as respond to it. In fact, the response from AOLserver client is written back to the nsvhr module in the AOLserver master proxy process, and then the AOLserver master proxy sends it back to the client browser.

 

Appendix II: AOLserver or Apache or Squid?

Almost everything you've seen described here can be done using Apache Virtual Hosting or Squid. Squid can do lots more too, supporting SSL and offering caching all at a very high performance.

Why use AOLserver then? Because it's there. I guess. Okay, here's a better reason. Because it works, and if you are using AOLserver to serve your pages, then you can simplify your operation by using AOLserver as your virtual server. Otherwise you have yet another piece of technology to learn, master, feed and care for. And because http://www.theashergroup.com/tag/articles/reverse-proxies I've found it's faster and more efficient.

Appendix III: Upgrading to AOLserver 3.3ad13

This came from the Things Web forum at theashergroup.

Upgrade Steps AOL_3.2ad12 to 3.3ad13+patches

You should be able to upgrade completely transparently to your system. With perhaps a minute or so of downtime. But it requires building a test version of your system first.
  1. Get AOLserver 3.3ad13 onto your system.
    Don't put it in the same source directory that your current version is in. You'll want to keep your current version for safety and comparison.
  2. Build it, and get it working in some temporary directory structure (you will need to modify aolserver/include/Makefile.global to have it install somewhere else -- look for the PREFIX variable.)
  3. Test AOLserver 3.3ad13 to make sure the basic system is working On it's own it will come up on port 8000.

    Then...
     
  4. Get my nsvhr/nsunix reverse proxy patches.
  5. Patch the system (complete instructions are in the release notes.)
  6. Build and install
  7. Bring it up just to make sure that AOLserver is still working at port 8000...

    Then to test nsvhr/nsunix itself will require a bit more complex testing setup.
     
  8. Copy one (or more) of your hosts' config.tcl files to your test area and modify them so that they starts at port 8000 + whatever port they had orginally started on. Also modify the name of their nsunix socket file so that it doesn't conflict with your working server.

    Add to each ns_section ns/.../module/nsunix section a location parameter, that points to the host, at port 8000. The purpose is to point redirects during testing back to your test version of the master.

    ns_section ns/server/virtual/module/nsunix
    ns_param location    http://virtual:8000
    ns_param socketfile  unix://virtual.nsunix.test
    
  9. Copy the master proxy config.tcl to your test area and modify it so that it starts at port 8000.
  10. Modify the master proxy config.tcl's nsvhr /map, if you need to, for httpish hosts to point them to their new location at port 8000 + whatever....
  11. Modify the nsvhr /map section so that nsunixish hosts are pointed to their new virtual.nsunix.test socket files.
  12. Bring up your new systems and test.... Repeat as necessary.

Moving into production, once you are satisfied that the new systems are working correctly, ...
  1. Add a location parameter (it's a new parameter) into the .../module/nsunix section of your production server's config.tcl.
  2. Modify the PREFIX parameter in aolserver/include/Makefile.global to point to your production system
  3. Stop your current webservers
  4. make install the new AOLserver
  5. Start your webservers back up
  6. Test (and maybe redo?)
I think that should do it.

Appendix IV: Security

securing the nscp port with ssh

Here's how you can create secure telnet sessions to your AOLserver's nscp port while ensuring no one else on the net can telnet to your AOLserver's nscp port. I think.

Steps

  1. Ensure sshd is running on your machine
  2. Setup your nscp port to only respond on the localhost interface
    ns_section ns/server/mysite/module/nscp
    ns_param port    9990
    ns_param address 127.0.0.1
    
    Restart your server
  3. On a remote host, create an ssh tunnel from the remote host to your site. Direct it to forward requests from port 4000 on the remote host to the nscp port on your site's localhost interface.
    mundy:~$  ssh -v -L 4000:localhost:9990 -l jerry web.mysite.org
    jerry's password: alexander
    Last login: Wed Nov 22 22:57:03 2000 from catbert
    [jerry@web jerry]$ 
    
  4. Now on the remote host, telnet to the remote host's localhost interface with the port of the ssh tunnel. You should then be prompted by the nscp running at mysite. So login. Remember to change the nscp user and password though....
    [jerry@web jerry]$ telnet mundy.org 4000
    Trying 209.133.104.3...
    Connected to mundy.org.
    Escape character is '^]'.
    login: nsadmin
    nsadmin
    Password: x
    x
    
    Welcome to mysite running at /web/ad10/aolserver/bin/mysite (pid 1005)
    AOLserver/3.2+ad10 (aolserver3_2_ad10) for linux built on Nov  1 2000 
    at 20:22:02
    CVS Tag: $Name: aolserver3_2 $
    mysite:nscp 1> 
    
I believe this is reasonably secure. Since you set up nscp to only bind on the localhost, no one can telnet to it without going through ssh.

securing your servers

If you're an ISP hosting lots of different sites for various clients, you probably have three worries. H4x0rs breaking one server and using that to compromise your entire system, h4x0rs breaking into one and using that to compromise your others, and malicious clients of yours attacking other clients on your system.

AOLserver appears reasonably secure. I haven't heard of any AOLserver 3x security exploits in the UNIX environment. Still, there are descriptions at ArsDigita and elsewhere on how to chroot your AOLserver. That can help protect your entire system from compromise. I'll leave details of that elsewhere.

But if you need to guarantee that one client can't harm another client, then the thing to do is (in order of importance?)

  1. Give each client its own AOLserver, (the point of this HOWTO)
  2. Make sure each client has its own database user, and
  3. Protect the pageroot for each client so that other clients cannot read or write them
  4. Create different scripts to configure each client's AOLserver server
  5. Create scripts to enable each client to start and stop its own AOLserver server on its own.
Here, a discussion on the ArsDigita web/db forum discusses how to protect one server's files from another....
Running AOL Server+ACS as user "nobody"?

Is it possible to run AOL Server+ACS as user "nobody" instead of user "nsadmin"?

I know it can be done with Oracle's OAS (I have done it with both V3 and V4) and I heard that Apache is running that way as well. Since user "nobody" has very limited powers I think it is much more secure than running under "nsadmin" (which belongs to the dba group!).

BTW, Cynthia had started a thread asking why should nsadmin belong to the dba group.

Why can't we go all the way and totally get rid of the "nsadmin" user? Any comments?

-- Simos Gabrielidis, March 16, 2001

Answers
nobody's "powers" are exactly those of any user; the kernel doesn't treat "nobody" differently from any other user.

The proper approach is to create a dedicated user for each web server you wish to run. If you are running foo.com, bar.com, and baz.com on one machine, you should create users "webfoo", "webbar", and "webbaz". Each user should have its own group. The foo.com web server should run as user webfoo and group webfoo. User webfoo should only own those files/directories on which the foo.com web service needs write access.

An entirely separate user, perhaps named "nsadmin" or "root", should own the AOLserver installation - nsd8x, nssock.so, etc.

-- Rob Mayoff, March 16, 2001


Rob, thanks for your response. So, if the "webfoo" service is under /web/webfoo, should the webfoo user be the owner of all the files under the webfoo directory, with write permission only to the files/directories that is necessary and read/execute permissions to everything else (under /web/webfoo)?

Thank you,

-- Simos Gabrielidis, March 16, 2001


No, webfoo should only own files that the foo web service needs write access to. If Mr. Badguy breaks your server and finds a way to make the server write to arbitrary files, you want to limit the damage. If Badguy can run arbitrary code, then he can change the permissions on any files owned by webfoo and then write to them. So don't let webfoo own a file unless the foo web service actually needs to write to the file.

So make /web/webfoo's contents be owned by root or nsadmin in general. Change specific files or directories to be owned by webfoo and writable by webfoo only as necessary.

-- Rob Mayoff, March 17, 2001


Thanks Rob! It's clear now!

-- Simos Gabrielidis, March 17, 2001
And here, are some discussions on how to use daemontools so that each client can start/stop its own AOLserver server.
Once more, here's Rob Mayoff's advice:

First, you need to get daemontools running on your computer.

  1.  

  2. Install daemontools. It's available from http://cr.yp.to/daemontools/install.html. It's very easy to install. Just unpack the archive, become root, and run "make setup check".

     

  3. Create directories named /service and /var/log/svscan.

     

  4. Create an rc.svscan script that runs svscan in /service. For example:
    #!/bin/sh
    PATH=/usr/local/bin:$PATH
    export PATH
    exec /usr/local/bin/svscan /service 2>&1 | /usr/local/bin/multilog t /var/log/svscan
    

    Where you put this script depends on your OS. I call it /etc/rc.d/rc.svscan on my Redhat 6.2 systems.

     

  5. Add a line to /etc/inittab to run rc.svscan at boot time. For example:
    svc:345:once:/etc/rc.d/rc.svscan
    

    Which runlevels you specify depends on your OS. Using "345" is appropriate for Redhat 6.2.

     

  6. Tell init to start svscan by running "telinit q".
For each AOLserver you want to run, you need to set up a service.
  1.  

  2. Pick a name for your service. Let's say we use nsd-dqd.com.

     

  3. Create a directory named /service/.nsd-dqd.com.

     

  4. In that directory, create a shell script named "run" that starts AOLserver. For example:
    #!/bin/sh
    . /etc/shell-mods.sh
    exec /usr/local/aolserver/bin/nsd8x -izkt /usr/local/web/dqd.com/nsd.tcl
    

     

  5. Rename the directory to /service/nsd-dqd.com.

     

  6. Within five seconds, svscan will notice the new directory and start a supervise process in it. Supervise will run the run script. Supervise will also create a directory named /service/nsd-dqd.com/supervise and put a few files in that directory.

     

  7. If AOLserver doesn't start, or starts and exits immediately, and there's nothing in the AOLserver log, check /var/log/svscan/current for messages.

     

  8. You can check the state of the service with the svstat command:
    svstat /service/nsd-dqd.com
    

    To use svstat, a user must have these permissions:

     

    • Execute permission on /service/nsd-dqd.com/supervise
    • Write permission on /service/nsd-dqd.com/supervise/ok
    • Read permission on /service/nsd-dqd.com/supervise/status

    By default, the supervise directory is only executable by root and supervise/ok is only writable by root. If you want all users to be able to check the state of the service, you can use these commands:

    chmod go+rx /service/nsd-dqd.com/supervise
    chmod go+w /service/nsd-dqd.com/supervise/ok
    

     

  9. You can control the service using the svc command. For example, you can restart the service (by sending it a SIGTERM) with this command:
    svc -t /service/nsd-dqd.com
    

    To use svc, a user must have these permissions:

     

    • Execute permission on /service/nsd-dqd.com/supervise
    • Write permission on /service/nsd-dqd.com/supervise/control

    By default, the supervise directory is only executable by root and supervise/control is only writable by root. If you want all users in group "arsdigit" to be able to control the service, you can use these commands:

    chgrp arsdigit /service/nsd-dqd.com/supervise
    chgrp arsdigit /service/nsd-dqd.com/supervise/control
    chmod g+rx /service/nsd-dqd.com/supervise
    chmod g+w /service/nsd-dqd.com/supervise/control
    


-- Rob Mayoff, December 19, 2000

Related Links


Jerry Asher, September, 2001

Copyright 2001 The Asher Group, All rights reserved.
 
 

Using ACS service contracts

Created by Gustaf Neumann, last modified by Gustaf Neumann 25 Sep 2019, at 10:03 PM

acs-service-contract is a package that "allows different packages to communicate via defined contracts". Essential, it defines an interface so that different packages can communicate with each other in a structured way.

Understanding how service contracts work

The best guide I've found on service contracts is Benjamin Bytheway's intro to service contracts. It currently uses the pl/pgsql API, instead of the newer Tcl API. However, this will give you an idea of what is going on, so that you can figure out the Tcl API. More information on the Tcl API is found here

Creating your service contract

Benjamin's tutorial describes using the pl/pgsql API. This would be defined in your .sql files, and instantiated when the package was initially installed by the APM. This would be easy enough, but we want to use the Tcl API, for two reasons:
  • it is easier to use
  • it is database neutral, so it works for both Oracle and PostgreSQL. This eases porting in the future.
Where, then, do you define the workflow when using Tcl?

One of the features in OpenACS that have been added fairly recently has been the ability to have Tcl functions called before and after the install of your package. I believe this is probably the best way to do it.

So, you should look at install-procs.tcl file (and possibly the install-procs.xql and install-procs-postgresql.xql files). This is a little confusing, but I'll spell it out in more detail:

The install-procs.tcl file is looked at when the package is first installed (my guess is that the name is hard-coded in as something for the APM to look at). There are several procedures that if defined are called at specified times. These are package_install, package_uninstall, package_upgrade, package_instantiate, and package_uninstantiate. There may be others as well. I haven't found any documentation for this, but I looked at the bug-tracker and gleaned this from there.

Here is an example of a service contract (stolen from bug-tracker):

...

ad_proc -private bug_tracker::install::package_install {} {
    Package installation callback proc
} {
    db_transaction {
        bug_tracker::install::register_implementations
        bug_tracker::bug::workflow_create
    }
}

ad_proc -private bug_tracker::install::package_uninstall {} {
    Package un-installation callback proc
} {
    db_transaction {
        bug_tracker::bug::workflow_delete
        bug_tracker::install::unregister_implementations
    }
}

ad_proc -private bug_tracker::install::package_instantiate {
    {-package_id:required}
} {
    Package instantiation callback proc
} {
    # Create the project
    bug_tracker::project_new $package_id

    bug_tracker::bug::instance_workflow_create -package_id $package_id
}

ad_proc -private bug_tracker::install::package_uninstantiate {
    {-package_id:required}
} {
    Package un-instantiation callback proc
} {

    bug_tracker::project_delete $package_id
    bug_tracker::bug::instance_workflow_delete -package_id $package_id

}

ad_proc -private bug_tracker::install::register_implementations {} {
    db_transaction {
        bug_tracker::install::register_format_log_title_impl
        ...
    }
}

ad_proc -private bug_tracker::install::register_format_log_title_impl {} {

    set spec {
        name "FormatLogTitle"
        aliases {
            GetObjectType bug_tracker::bug::object_type
            GetPrettyName bug_tracker::bug::format_log_title::pretty_name
            GetTitle      bug_tracker::bug::format_log_title::format_log_title
        }
    }
    
    lappend spec contract_name [workflow::service_contract::activity_log_format_title]
    lappend spec owner [bug_tracker::package_key]
    
    acs_sc::impl::new_from_spec -spec $spec
}

ad_proc -private bug_tracker::install::unregister_implementations {} {
    db_transaction {

        acs_sc::impl::delete \
                -contract_name [workflow::service_contract::activity_log_format_title] \
                -impl_name "FormatLogTitle"

    ...
}


Reference

 
 

Adding Workflow to a Project

Created by Gustaf Neumann, last modified by Gustaf Neumann 25 Sep 2019, at 10:00 PM

Adding Workflow to an OpenACS package

I'm trying to add Workflow to the project-manager I'm writing (in collaboration with other OpenACS programmers), so I thought I might as well document what needs to be done, so others can learn from my hard knocks.

Why use workflow, and for that matter, what is workflow?

Lars, the author of the workflow package, defines workflow like this: "[Workflow] provides a service to keep track of a process involving multiple people around some object. Workflow keeps track of the process you wish to follow, of where you currently are in the process (the current state), and who's supposed to do what.

I must confess that I've been awfully tempted not to use workflow. There seems to be quite a learning curve, and it's much easier to duplicate the functionality of workflow doing it your own way. However, several programmers with much more experience than I insist that it is really the way to do things. So if you have a choice, they would recommend using workflow. I'll reserve judgement until I figure out enough to see if it was worth the effort or not.

Documentation

Lars, the uber-programmer who created Workflow, fortunately wrote a lot of documentation for Workflow.

 

project-manager: before workflow

Here is what the application looked like before adding in workflow. I know there are problems with this code, and I don't recommend using it as a basis for your own coding. However, if will give you an idea of what changed. This code is pretty basic. It allows you to add and edit projects, and there is a page for viewing them as well.

project-manager-create.sql

--
-- packages/project-manager/sql/postgresql/project-manager-create.sql
--
-- @author jade@bread.com
-- @creation-date 2003-05-15
-- @cvs-id $Id: project-manager-create.sql,v 1.1 2003/05/15 21:52:31 oacs Exp $
--
--

\i project-manager-table-create.sql
\i project-manager-functions-create.sql
project-manager-table-create.sql
-- TODO:
-- 
-- which items in this data model need to use the content repository?
-- need to add in workflow (for status among other things)
-- need to take into account acs-rels
-- add categories to projects

--
-- packages/project-manager/sql/postgresql/project-manager-table-create.sql
--
-- @author jader@bread.com and everyone else involved in this thread: http://ope
nacs.org/forums/message-view?message_id=90742
-- @creation-date 2003-05-15
--

create table pm_project (
        project_id              integer
                                constraint project_manager_id_fk
                                references acs_objects(object_id) 
                                constraint pm_project_id_pk
                                primary key,
        project_name            varchar(255) 
                                constraint pm_project_name_nn
                                not null,
        -- a user-specified
        project_code            varchar(255),
        -- for subprojects
        parent_project_id       integer
                                constraint pm_project_parent_project_id_fk
                                references pm_project,
        goal                    varchar(4000),
        description             varchar(4000),
        -- is the deadline computed from the end date, or from 
        -- today?
        -- e = end, t = today
        deadline_scheduling     char(1) default 't'
                                constraint pm_project_dline_scheduling_ck
                                check (deadline_scheduling in ('t','e')),
        planned_start_date      timestamptz,
        planned_end_date        timestamptz,
        actual_start_date       timestamptz,
        actual_end_date         timestamptz,
        ongoing_p               char(1) default 'f' 
                                constraint pm_project_ongoing_p_ck
                                check (ongoing_p in ('t','f'))
);


create function inline_0 ()
returns integer as '
begin
    PERFORM acs_object_type__create_type (
        ''pm_project'',                         -- object_type
        ''Project'',                            -- pretty_name
        ''Projects'',                           -- pretty_plural
        ''acs_object'',                         -- supertype
        ''pm_project'',                         -- table_name
        ''project_id'',                         -- id_column
        null,                                   -- package_name
        ''f'',                                  -- abstract_p
        null,                                   -- type_extension_table
        ''pm_project__name''                    -- name_method
        );
    return 0;
end;' language 'plpgsql';
select inline_0 ();
drop function inline_0 ();

project-manager-functions-create.sql

--
-- packages/project-manager/sql/postgresql/project-manager-functions-create.sql
--
-- @author jade@bread.com
-- @creation-date 2003-05-15
-- @cvs-id $Id: project-manager-functions-create.sql,v 1.1 2003/05/15 21:52:31 o
acs Exp $
--
--

select define_function_args('pm_project__new','project_id,project_name,project_c
ode,parent_project_id,goal,description,deadline_scheduling,planned_start_date,pl
anned_end_date,actual_start_date,actual_end_date,ongoing_p,creation_date,creatio
n_user,creation_ip,context_id'); 

create or replace function pm_project__new (integer,varchar,varchar,integer,varc
har,varchar,char(1),timestamptz,timestamptz,timestamptz,timestamptz,char(1),time
stamptz,integer,varchar,integer)
returns integer as '
declare
  p_project_id                                  alias for $1;
  p_project_name                                alias for $2;
  p_project_code                                alias for $3;
  p_parent_project_id                           alias for $4;
  p_goal                                        alias for $5;
  p_description                                 alias for $6;
  p_deadline_scheduling                         alias for $7;
  p_planned_start_date                          alias for $8;
  p_planned_end_date                            alias for $9;
  p_actual_start_date                           alias for $10;
  p_actual_end_date                             alias for $11;
  p_ongoing_p                                   alias for $12;
  p_creation_date                               alias for $13;
  p_creation_user                               alias for $14;
  p_creation_ip                                 alias for $15;
  p_context_id                                  alias for $16;
  v_pm_project_id                               int;
begin
        v_pm_project_id := acs_object__new (
                p_project_id,
                ''pm_project'',
                p_creation_date,
                p_creation_user,
                p_creation_ip,
                p_context_id
        );
        insert into pm_project
          (project_id,project_name,project_code,parent_project_id,goal,descripti
on,deadline_scheduling,planned_start_date,planned_end_date,actual_start_date,act
ual_end_date,ongoing_p) 
        values
          (v_pm_project_id, p_project_name, p_project_code, p_parent_project_id,
 p_goal, p_description, p_deadline_scheduling, p_planned_start_date, p_planned_e
nd_date, p_actual_start_date, p_actual_end_date, p_ongoing_p);
        PERFORM acs_permission__grant_permission(
          v_pm_project_id,
          p_creation_user,
          ''admin''
    );
        return v_pm_project_id;
end;' language 'plpgsql';
/* The __delete function deletes a record and all related overhead. */
  

select define_function_args('pm_project___delete','project_id');
create or replace function pm_project__delete (integer)
returns integer as '
declare
  p_pm_project_id                               alias for $1;
begin
    delete from acs_permissions
                   where object_id = p_pm_project_id;
        delete from pm_project
                   where project_id = p_pm_project_id;
        raise NOTICE ''Deleting pm_project...'';
        PERFORM acs_object__delete(p_pm_project_id);
        return 0;
end;' language 'plpgsql';
/* When we created the acs object type above, we specified a
'name_method'.  This is the name of a function that will return the
name of the object.  This is a convention ensuring that all objects
can be identified.  Now we have to build that function.  In this case,
we'll return a field called title as the name. */

select define_function_args('pm_project___name','project_id');

create or replace function pm_project__name (integer)
returns varchar as '
declare
    p_pm_project_id      alias for $1;
    v_pm_project_name    pm_project.project_name%TYPE;
begin
        select project_name into v_pm_project_name
                from pm_project
                where project_id = p_pm_project_id;
    return v_pm_project_name;
end;
' language 'plpgsql';

project-manager-drop

-- packages/project-manager/sql/project-manager-drop.sql
-- drop script
--
-- @author jade@bread.com
-- @creation-date 2003-05-15
-- @cvs-id $Id: project-manager-drop.sql,v 1.1 2003/05/15 21:52:31 oacs Exp $
--

--drop package, which drops all functions created with define_function_args

select drop_package('pm_project');

--drop permissions
delete from acs_permissions where object_id in (select project_id from pm_projec
t);

--drop objects
create function inline_0 ()
returns integer as '
declare
        object_rec              record;
begin
        for object_rec in select object_id from acs_objects where object_type=''
pm_project''
        loop
                perform acs_object__delete( object_rec.object_id );
        end loop;
        return 0;
end;' language 'plpgsql';

select inline_0();
drop function inline_0();

--drop table
drop table pm_project;

--drop type
select acs_object_type__drop_type(
           'pm_project',
           't'
    );

index.tcl

ad_page_contract {
    Main view page for projects.

    @author jader@bread.com
    @creation-date 2003-05-15
    @cvs-id $Id: index.tcl,v 1.3 2003/05/16 22:23:58 oacs Exp $
    @param orderby indicates when the user clicks on a column to order by that \
column
    @return table_html preformatting html table constructed by querying the sam\
plenotes table

} {
    {orderby:optional {title}}
} -properties {
    table_html
}
# define the columns in the table
set table_def   {
    {edit "" {} {<td><a href="add-edit?project_id=$project_id">Edit</a></td>}}
    {view "" {} {<td><a href="one?project_id=$project_id">View</a></td>}}
    {project_name "Name"}
    {project_code "Project code"}
    {parent_project_id "Parent project"}
    {goal "Goal"}
    {description "Description"}
    {planned_start_date "Start date (planned)"}
    {planned_end_date "End date (planned)"}
    {ongoing_p "Ongoing?"}
}

# construct an html table from the samplenotes database table
set table_html [ad_table -Torderby $orderby project_query {} $table_def]

index.adp

<master>
<property name="title">Projects</property>
@table_html@
<p><a href="add-edit">Add a project</a></p>

index.xql

<?xml version="1.0"?>
<queryset>
  <fullquery name="project_query">
    <querytext>
        SELECT
        project_id,
        project_name,
        project_code,
        parent_project_id,
        goal,
        description,
        deadline_scheduling,
        to_char(planned_start_date,'YYYY MM DD') as planned_start_date,
        to_char(planned_end_date,'YYYY MM DD') as planned_end_date,
        ongoing_p
        FROM
        pm_project
    [ad_order_by_from_sort_spec $orderby $table_def]
    </querytext>
  </fullquery>
</queryset>

add-edit.tcl

ad_page_contract {
    Simple add/edit form for projects
} {
    project_id:integer,optional
    {planned_start_date ""}
    {planned_end_date ""}
} -properties {
    context_bar:onevalue
    title:onevalue
}


set user_id [ad_maybe_redirect_for_registration]

set package_id [ad_conn package_id]

if {[exists_and_not_null project_id]} {
    set title "Edit a project"
    set context_bar [ad_context_bar "Edit Project"]
} else {
    set title "Add a project"
    set context_bar [ad_context_bar "New Project"]
}


ad_form -name add_edit -form {
    project_id:key

    {project_name:text
        {label "Project name"}
    }

    {project_code:text
        {label "Project code"}
    }

    {parent_project_id:text(hidden)
        {value ""}}

    {goal:text(textarea)
        {label "Project goal"}
        {optional}
        {html { rows 5 cols 40 wrap soft}}}

    {description:text(textarea)
        {label "Description"}
        {optional}
        {html { rows 5 cols 40 wrap soft}}}

    {deadline_scheduling:text(select)
        {label "Scheduling"}
        {options {{"From today" "t"} {"From deadline" "e"}} {value $deadline_scheduling}} }

    {planned_start_date:date {value {[util::date acquire clock [clock scan $planned_start_date]]}} optional
        {label "Planned start date"}
        {format "MONTH DD YYYY"}
        {help}
    }

    {planned_end_date:date {value {[util::date acquire clock [clock scan $planned_end_date]]}} optional
        {label "Planned end date"}
        {format "MONTH DD YYYY"}
        {help}
    }

    {ongoing_p:text(select)
        {label "Project is ongoing?"}
        {options {{"Yes" "t"} {"No" "f"}} {value $ongoing_p}} }

} -select_query_name project_query -on_submit {

    set user_id [ad_conn user_id]
    set peeraddr [ad_conn peeraddr]

} -new_data {
    db_exec_plsql do_insert { *SQL* }

    ad_returnredirect "."
    ad_script_abort

} -edit_data {

    db_dml do_update { *SQL* }

} -after_submit {

    ad_returnredirect "index"
    ad_script_abort
}

add-edit.adp

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

<center>
<formtemplate id="add_edit" style="plain-ibr"></formtemplate>
</center>

add-edit.xql

<?xml version="1.0"?>
<queryset>
  <fullquery name="do_insert">
    <querytext>
        select pm_project__new(
        :project_id, 
        :project_name,
        :project_code,
        :parent_project_id,
        :goal,
        :description,
        :deadline_scheduling,
        to_timestamp(:planned_start_date,'YYYY MM DD HH24 MI SS'),
        to_timestamp(:planned_end_date,'YYYY MM DD HH24 MI SS'),
        null,
        null,
        :ongoing_p,
        now(),
        :user_id,
        :peeraddr,
        :package_id);
    </querytext>
  </fullquery>

  <fullquery name="do_update">
    <querytext>
        UPDATE
        pm_project
        SET 
                project_name            = :project_name,
                project_code            = :project_code,
                parent_project_id       = :parent_project_id,
                goal                    = :goal,
                description             = :description,
                deadline_scheduling     = :deadline_scheduling,
                planned_start_date      = to_timestamp(:planned_start_date,'YYYY MM DD HH24 MI SS'),
                planned_end_date        = to_timestamp(:planned_end_date,'YYYY MM DD HH24 MI SS'),
                ongoing_p               = :ongoing_p
        WHERE
                project_id = :project_id
    </querytext>
  </fullquery>

  <fullquery name="project_query">
    <querytext>
      select
        project_id,
        project_name,
        project_code,
        parent_project_id,
        goal,
        description,
        deadline_scheduling,
        to_char(planned_start_date,'YYYY MM DD') as planned_start_date,
        to_char(planned_end_date,'YYYY MM DD') as planned_end_date,
        ongoing_p
        FROM
        pm_project
       where project_id = :project_id
    </querytext>
  </fullquery>
</queryset>

Adding in workflow

Lars describes a six step process to getting Workflow into your application:
  1. Define your default process. The idea typically is to allow your end users to modify the process to suit their needs, but you'll want to provide a process which they can use as a starting point.
  2. Identify, declare, and implement the callbacks that your application will need.
  3. Write the code to set up the initial process, and to clone that process for each package instance.
  4. Integrate workflow support into your application's API.
  5. Integrate workflow support into your application's user interface.
  6. Integrate workflow into your application's queries

Step 1: Define your default process

What does it mean to define your default process? In my understanding, this is to define three things:
  • roles - who are the actors in your application
  • states - what are the valid states for this object
  • actions - what are valid actions on this object
So let's do that for the project-manager:
Roles for projects
  • creater
  • participant
  • manager
  • client_representative
States for projects
  • open
  • closed
Actions for projects
  • create
  • close
  • reopen
  • edit

project-procs.tcl

After this, we're supposed to define our workflow. The definition file goes in /tcl/project-procs.tcl. You'll need to copy a lot of the files from bug-tracker.
namespace eval project_manager::project {}

ad_proc -private project_manager::project::workflow_create {} {
    Create the 'project' workflow for project-manager
} {
    set spec {
        project {
            pretty_name "Project"
            package_key "project-manager"
            object_type "pm_project"
            callbacks { 
                # leave these blank for now
                # bug-tracker.FormatLogTitle 
                # bug-tracker.BugNotificationInfo
            }
            roles {
                participant {
                    pretty_name "Participant"
                    callbacks { 
                        # leave these blank for now
                        # workflow.Role_DefaultAssignees_CreationUser
                    }
                }
                manager {
                    pretty_name "Manager"
                    callbacks { 
                        # leave these blank for now
                        # workflow.Role_DefaultAssignees_CreationUser
                    }
                }
                client_representative {
                    pretty_name "Client representative"
                    callbacks {
                        # bug-tracker.ComponentMaintainer
                        # bug-tracker.ProjectMaintainer
                        # workflow.Role_PickList_CurrentAssignees
                        # workflow.Role_AssigneeSubquery_RegisteredUsers
                    }
                }
            }
            states {
                open {
                    pretty_name "Open"
                    # hide_fields { resolution fixed_in_version }
                }
                closed {
                    pretty_name "Closed"
                }
            }
            actions {
                create {
                    pretty_name "Create"
                    pretty_past_tense "Created"
                    new_state "open"
                    initial_action_p t
                }
                edit {
                    pretty_name "Edit"
                    pretty_past_tense "Edited"
                    privileges { write }
                    always_enabled_p t
                    edit_fields { 
                        # not sure what to do here
                        #component_id 
                        #summary 
                        #found_in_version
                        #role_assignee
                        #fix_for_version
                        #resolution 
                        #fixed_in_version 
                    }
                }
                close {
                    pretty_name "Close"
                    pretty_past_tense "Closed"
                    # what is this doing?
                    assigned_role "submitter"
                    assigned_states { resolved }
                    new_state "closed"
                    privileges { write }
                }
                reopen {
                    pretty_name "Reopen"
                    pretty_past_tense "Reopened"
                    enabled_states { resolved closed }
                    new_state "open"
                    privileges { write }
                }
            }
        }
    }

    set workflow_id [workflow::fsm::new_from_spec -spec $spec]
    
    return $workflow_id
}
Acceptance test
At this point, we probably should check to make sure that what we've done is correct. I have no clue as to how to do that.

Step two: callbacks service contracts

We now need to work to identify, declare, and implement the callbacks that our application will need. Why would we want to do this? These callbacks are functions that can do things like determine default assignees based on certain data in your application, get information about your application's object for use when sending out notifications, or perform "side-effects", such as actually changing the publication state of a content item when you execute the [Publish] action.

So, how do you go about adding in the callbacks? Well, first of all, you better make sure you understand how acs-service-contracts work.

See my page on acs-service-contracts for a background on this. Don't skip this unless you already are comfortable with acs-service-contracts.

We add in an /tcl/install-procs.tcl file. I simply copied it over from bug-tracker, which is probably the best example to use for Workflow code.

The point I'm currently confused on is how to choose what service contracts to implement. Help?


 

If the namespace portion looks confusing, then see my namespace page.

 

 
 

Getting Started With OpenACS

Created by Gustaf Neumann, last modified by Gustaf Neumann 25 Sep 2019, at 11:49 AM

Welcome to the OpenACS community

This is my collection of links and resources for people that are getting started with OpenACS. But before we get started, I'd like to share some thoughts on how to best get information and get up to speed with OpenACS.

I think the first thing to realize about OpenACS is that it is not just a technology, it is a vibrant open-source community. The operative word here is community. You are coming into this community as a newbee, or a new person. The OpenACS folks are extremely helpful, more so than any other technical group I've seen. However, you should think about what your resources are for getting information.

The way communities work is very similar to the way a bank account works. You make deposits by doing things that are beneficial to the community, and you make withdrawals by doing things that take the time and energy of the community. The more helpful you are, the more people are going to be willing to help you out. If you have a pattern of being demanding and unhelpful, people aren't going to help you out as much. This isn't malicious, it's just the way people work in general.

What you have to offer?

First of all, let's look at what you have to offer the community. You're new, so you can't offer much in the way of technical knowledge. However, you do have things to offer you can trade for detailed technical help from more knowledgeable individuals:
  • If there is no documentation for what you're trying to do, then the single most helpful thing you can do is write documenation as you learn. That's what I've done in my OpenACS articles. I use it both to keep track of what I've learned for my own reference, and as a way of sharing that with others. But it is also something I can trade for the time of more experienced developers. They know that if they help me out, I'll write up documentation for it, and they might not have to answer that question again.
  • As you find bugs, you can file them in the OpenACS bug-tracker.
  • If you have a particular project in mind, or something you're planning on creating, then building that project and sharing it with the community is good incentive for people to help you. They may be interested in what you're building.
  • When a newer newbee asks a question you know the answer to, answer it!
  • As a newcomer, you have a unique perspective to OpenACS. You can often see deficiencies and areas to improve that old-timers might not even notice. Feel free to bring them up as suggestions. Remember that the OpenACS community is a community, not a company. It isn't their responsibility to fix things for you, or make them better. But it is often in their self-interest to improve things, and they will. Flames won't get you anywhere, but thoughtful suggestions will.
  • Even though you aren't proficient with OpenACS (yet), you may have other skills that are useful. For example, some people have UI design skills, others may have Linux administration skills, or a security background.
I write this not because you're going to have trouble getting help. On the contrary, I've seen people in the OpenACS community help out people that are being very demanding and troublesome. My main hope in writing this is to give newbees a guide to how to most effectively get information.

How to get help and find the information you need.

  • First of all, try to track down documentation for what you're looking for. The first place to look is in the official OpenACS documentation. Familiarize yourself with this documentation, especially the API browser, which shows you all the commands you can run. Look at the package documentation (and note that sadly, some of the packages don't have documentation). Notice that there is documentation not just for getting installed, but also for developing an OpenACS package. Try looking in the FAQ
  • Second, try searching on Google. Try your search, but include the word OpenACS in the query. This finds answers a surprisingly high amount of the time.
  • Third, look through the forums. Use the search functionality to try and find what you're looking for.
  • Fourth, if your question is a quick one, then try asking on the OpenACS IRC channel.
  • Fifth, try asking your question on the OpenACS forums. Make sure you clearly describe what your problem is, what version of OpenACS you're using, what you did before and after the problem, and what the error message is. Show relevant portions of your error log. If you want people to be helpful and take their time on your question, make sure you take the time to ask the question in a way that someone can give you a meaningful answer.
  • See below for more helpful resources

Introductions to OpenACS

Helping out with OpenACS

  • How to get started as a worker bee for OpenACS. This describes how you can contribute if you have time and want to learn more about OpenACS by helping out with simple tasks to improve the toolkit

History of OpenACS

After the installation

Writing a package

Data model

Troubleshooting

If you're using Oracle, this thread describes how to set up Aolserver so that you receive the entire error message from the Oracle database driver. If you get an error message starting with SQL: [too long], then you need to read this. Sometimes, for long error messages, your error messages are truncated, which makes tracking down the errors more difficult.

Other resources

Forum thread on getting started - Several posts on getting started with OpenACS

Nima Mazloumi's list of resources for newbees - lists a bunch of resources to use to get up to speed
 
 
 

Next Page