Filtered by category Cookbook, 21 - 30 of 38 Postings (
all,
summary)
Created by openacs community, last modified by Gustaf Neumann 26 Aug 2020, at 02:01 PM
Box tag code:
# The box tag is intended to make the markup around a "box"
# standard sitewide so that you can use the same css everywhere to
# style the site.
template_tag box { chunk params } {
set class [ns_set iget $params class]
if {[template::util::is_nil class]} {
set class box
}
set id [ns_set iget $params id]
if {![template::util::is_nil id]} {
set id " id=\\\"$id\\\""
}
template::adp_append_code "append __adp_output \"<div class=\\\"$class\\\"$id><div class=\\\"boxTop\\\"></div><div class=\\\"boxContent\\\">\""
template::adp_compile_chunk $chunk
template::adp_append_code "append __adp_output {</div><div class=\"boxBottom\"></div></div>}"
}
Created by Malte Sussdorff, last modified by Gustaf Neumann 24 Aug 2020, at 01:43 PM
Incoming E-Mail in OpenACS works with the latest version of acs-mail-lite in a general fashion using callbacks.
The original version of this documentation is found via archive.org at: http://www.cognovis.de/developer/en/incoming_email
We will take a look on what needs to be done to get incoming e-mail working and then continue on to see how packages can benefit.
Project notes: ACS Mail Lite sends via SMTP which permits the use of an external server to handle email. For scalability, consider expanding the incoming E-mail paradigm to likewise use Tcllib's imap4 or NaviServer's nsimap so that most all email can be handled on separate servers.
Install incoming E-Mail
First, one must have an understanding of postfix basics. See http://www.postfix.org/BASIC_CONFIGURATION_README.html.
These instructions use the following example values:
- hostname: www.yourserver.com
- oacs user: service0
- OS: Linux
- email user: service0
- email's home dir: /home/service0
- email user's mail dir: /home/service0/mail
Important: The email user service0 does not have a ".forward" file. This user is only used for running the OpenACS website. Follow careful use of email rules by following strict guidelines to avoid email looping back unchecked.
For postfix, the email user and oacs user do not have to be the same. Furthermore, postfix makes distinctions between virtual users and user aliases. Future versions of this documentation should use examples with different names to help distinguish between standard configuration examples and the requirements of ACS Mail Lite package.
Postfix configuration parameters:
myhostname=www.yourserver.com
myorigin=$myhostname
inet_interfaces=$myhostname, localhost
mynetworks_style=host
virtual_alias_domains = www.yourserver.com
virtual_maps=regexp:/etc/postfix/virtual
home_mailbox=mail/
Here is the sequence to follow if installing email service on system for first time. If your system already has email service, adapt these steps accordingly:
- Install postfix
- Install smtp (for postfix)
- Install metamail (for acs-mail-lite)
- Edit /etc/postfix/main.cf
- Edit /etc/postfix/virtual Add a regular expression to filter relevant incoming emails for processing by OpenACS.
@www.yourserver.com service0
- Edit /etc/postfix/master.cf - uncomment this line so postfix listens to emails from internet
smtp inet n - n - - smtpd
- Create a mail directory as service0
mkdir /home/service0/mail
- Configure ACS Mail Lite parameters
BounceDomain: www.yourserver.com
BounceMailDir: /home/service0/mail
EnvelopePrefix: bounce
The EnvelopePrefix is for bounce e-mails only.
NOTE: Parameters should be renamed:
BounceDomain to IncomingDomain
BounceMailDir to IncomingMaildir
EnvelopePrefix to BouncePrefix
..to reflect that acs-mail-lite is capable of dealing with other types of incoming e-mail.
Furthermore, setting IncomingMaildir parameter clarifies that incoming email handling is setup. This is useful for other packages to determine if they can rely on incoming e-mail working (e.g. to set the reply-to email to an e-mail address which actually works through a callback if the IncomingMaildir parameter is enabled).
- Configure Notifications parameters
EmailReplyAddressPrefix: notification
EmailQmailQueueScanP: 0
We want acs-mail-lite incoming handle the Email Scanning, not each package separately.
Configure other packages likewise
- Invoke postmap in OS shell to recompile virtual db:
postmap /etc/postfix/virtual
- Restart Postfix.
/etc/init.d/postfix restart
- Restart OpenACS
Processing incoming e-mail
A sweeper procedure like acs_mail_lite::load_mails should:
- scan the e-mails which are in the IncomingMaildir directory on a regular basis.
- check if any email came from an auto mailer.
- Parse new ones, and
- process them by firing off callbacks.
Vinod has made a check for auto mailers by using procmail as follows. Maybe we could get this dragged into Tcl code (using regexp or a Procmail recipe parser) instead, thereby removing the need for setting up procmail in the first place.
Revised procmail filters:
:0 w * ^subject:.*Out of Office AutoReply /dev/null
:0 w * ^subject:.*Out of Office /dev/null :0 w * ^subject:.*out of the office /dev/null
:0 w * ^subject:.*NDN /dev/null :0 w * ^subject:.*[QuickML] Error: /dev/null
:0 w * ^subject:.*autoreply /dev/null :0 w * ^from.*mailer.*daemon /dev/null
To make things granular a separate parsing procedure should deal with loading the e-mail into the Tcl interpreter and setting variables in an array for further processing.
ad_proc parse_email {
-file:required
-array:required
} {
...
}
An email is split into several parts: headers, bodies and files.
The headers consists of a list with header names as keys and their corresponding values. All keys are lower case.
The bodies consists of a list with two elements: content-type and content.
The files consists of a list with three elements: content-type, filename and content.
An array with all the above data is upvarred to the caller environment.
Processing an email should result in an array like this:
HEADERS
- message_id
- subject
- from
- to
- date
- received
- references
- in-reply-to
- return-path
- .....
X-Headers:
- X-Mozilla-Status
- X-Virus Scanned
- .....
We do not know which headers are going to be available in the e-mail. We set all headers found in the array. The callback implementation then checks if a certain header is present or not.
#get all available headers
set keys [mime::getheader $mime -names]
set headers [list]
# create both the headers array and all headers directly for the email array
foreach header $keys {
set value [mime::getheader $mime $header]
set email([string tolower $header]) $value
lappend headers [list $header $value]
}
set email(headers) $headers
Bodies
An e-mail usually consists of one or more bodies. With the advent of complex_send, OpenACS supports sending of multi-part e-mails which are needed if you want to send out and e-mail in text/html and text/plain (for old mail readers).
switch [mime::getproperty $part content] {
"text/plain" {
lappend bodies [list "text/plain" [mime::getbody $part]]
}
"text/html" {
lappend bodies [list "text/html" [mime::getbody $part]]
}
}
Files
OpenACS supports tcllib mime functions. Getting incoming files to work is a matter of looking for a part where there exists a "Content-disposition" part. All these parts are file parts. Together with scanning for email bodies, code looks something like this:
set bodies [list]
set files [list]
#now extract all parts (bodies/files) and fill the email array
foreach part $all_parts {
# Attachments have a "Content-disposition" part
# Therefore we filter out if it is an attachment here
if {[catch {mime::getheader $part Content-disposition}]} {
switch [mime::getproperty $part content] {
"text/plain" {
lappend bodies [list "text/plain" [mime::getbody $part]]
}
"text/html" {
lappend bodies [list "text/html" [mime::getbody $part]]
}
}
} else {
set encoding [mime::getproperty $part encoding]
set body [mime::getbody $part -decode]
set content $body
set params [mime::getproperty $part params]
if {[lindex $params 0] == "name"} {
set filename [lindex $params 1]
} else {
set filename ""
}
# Determine the content_type
set content_type [mime::getproperty $part content]
if {$content_type eq "application/octet-stream"} {
set content_type [ns_guesstype $filename]
}
lappend files [list $content_type $encoding $filename $content]
}
}
set email(bodies) $bodies
set email(files) $files
Note that the files ie attachments are actually stored in the /tmp directory from where they can be processed further. It is up to the callback to decide if to import the file into OpenACS or not. Once all callbacks have been fired files in /tmp will have to be deleted again though.
Firing off callbacks
Now that we have the e-mail parsed and have an array with all the information, we can fire off the callbacks. The firing should happen in two stages.
The first stage is where we support a syntax like "object_id@yoursite.com".
Second, incoming e-mail could look up the object_type, and then call the callback implementation specific to this object_type. If object_type = 'content_item', use content_type instead.
ad_proc -public -callback acs_mail_lite::incoming_object_email { -array:required -object_id:required } { }
callback acs_mail_lite::incoming_object_email -impl $object_type -array email -object_id $object_id
ad_proc -public -callback acs_mail_lite::incoming_object_email -impl user {
-array:required
-object_id:required
} {
Implementation of mail through support for incoming emails
} {
# get a reference to the email array
upvar $array email
# make the bodies an array
template::util::list_of_lists_to_array $email(bodies) email_body
if {[exists_and_not_null email_body(text/html)]} {
set body $email_body(text/html)
} else {
set body $email_body(text/plain)
}
set reply_to_addr "[party::get_by_email $email(from)]@[ad_url]"
acs_mail_lite::complex_send \
-from_addr $from_addr \
-reply_to $reply_to_addr \
-to_addr $to_addr \
-subject $email(subject) \
-body $body \
-single_email \
-send_immediately
}
Object id based implementations are useful for automatically generating "reply-to" addresses. With ProjectManager and Contacts object_id is also handy, because Project / TaskID is prominently placed on the website. If you are working on a task and you get an e-mail by your client that is related to the task, just forward the email to "$task_id@server.com" and it will be stored along with the task. Highly useful :).
Obviously you could have implementations for:
-
forums_forum_id: Start a new topic
-
forums_message_id: Reply to an existing topic
-
group_id: Send an e-mail to all group members
-
pm_project_id: add a comment to a project
-
pm_task_id: add a comment to a task and store the files in the projects folder (done)
Once the e-mail is dealt with in an object oriented approach we are either done with the message (an object_id was found in the to address) or we need to process it further.
ad_proc -public -callback acs_mail_lite::incoming_email {
-array:required
-package_id
} {
}
array set email {}
parse_email -file $msg -array email
set email(to) [parse_email_address -email $email(to)]
set email(from) [parse_email_address -email $email(from)]
# We execute all callbacks now
callback acs_mail_lite::incoming_email -array email
For this a general callback should exist which can deal with every leftover e-mail and each implementation will check if it wants to deal with this e-mail. How is this check going to happen? As an example, a package could have a prefix, as is the case with bounce e-mails as handled in acs_mail_lite::parse_bounce_address (see below):
ad_proc -public -callback acs_mail_lite::incoming_email -impl acs-mail-lite {
-array:required
-package_id:required
} {
@param array An array with all headers, files and bodies. To access the array you need to use upvar.
@param package_id The package instance that registered the prefix
@return nothing
@error
} {
upvar $array email
set to [acs_mail_lite::parse_email_address -email $email(to)]
ns_log Debug "acs_mail_lite::incoming_email -impl acs-mail-lite called. Recepient $to"
util_unlist [acs_mail_lite::parse_bounce_address -bounce_address $to] user_id package_id signature
# If no user_id found or signature invalid, ignore message
# Here we decide not to deal with the message anymore
if {[empty_string_p $user_id]} {
if {[empty_string_p $user_id]} {
ns_log Debug "acs_mail_lite::incoming_email impl acs-mail-lite: No equivalent user found for $to"
} else {
ns_log Debug "acs_mail_lite::incoming_email impl acs-mail-lite: Invalid mail signature $signature"
}
} else {
ns_log Debug "acs_mail_lite::incoming_email impl acs-mail-lite: Bounce checking $to, $user_id"
if { ![acs_mail_lite::bouncing_user_p -user_id $user_id] } {
ns_log Debug "acs_mail_lite::incoming_email impl acs-mail-lite: Bouncing email from user $user_id"
# record the bounce in the database
db_dml record_bounce {}
if {![db_resultrows]} {
db_dml insert_bounce {}
}
}
}
}
Alternatively we could just check the whole to address for other things, e.g. if the to address belongs to a group (party)
ad_proc -public -callback acs_mail_lite::incoming_email -impl contacts_group_mail {
-array:required
{-package_id ""}
} {
Implementation of group support for incoming emails
If the to address matches an address stored with a group then send out the email to all group members
@author Malte Sussdorff (malte.sussdorff@cognovis.de)
@creation-date 2005-12-18
@param array An array with all headers, files and bodies. To access the array you need to use upvar.
@return nothing
@error
} {
# get a reference to the email array
upvar $array email
# Now run the simplest mailing list of all
set to_party_id [party::get_by_email -email $email(to)]
if {[db_string group_p "select 1 from groups where group_id = :to_party_id" -default 0]} {
# make the bodies an array
template::util::list_of_lists_to_array $email(bodies) email_body
if {[exists_and_not_null email_body(text/html)]} {
set body $email_body(text/html)
} else {
set body $email_body(text/plain)
}
acs_mail_lite::complex_send \
-from_addr [lindex $email(from) 0] \
-to_party_ids [group::get_members -group_id $to_party_id] \
-subject $email(subject) \
-body $body \
-single_email \
-send_immediately
}
}
Or check if the to address follows a certain format.
ad_proc -public -callback acs_mail_lite::incoming_email -impl contacts_mail_through {
-array:required
{-package_id ""}
} {
Implementation of mail through support for incoming emails
You can send an e-amil through the system by sending it to user#target.com@yoursite.com
The email will be send from your system and if mail tracking is installed the e-mail will be tracked.
This allows you to go in direct communication with a customer using you standard e-mail program instead of having to go to the website.
@author Malte Sussdorff (malte.sussdorff@cognovis.de)
@creation-date 2005-12-18
@param array An array with all headers, files and bodies. To access the array you need to use upvar.
@return nothing
@error
} {
# get a reference to the email array
upvar $array email
# Take a look if the email contains an email with a "#"
set pot_email [lindex [split $email(to) "@"] 0]
if {[string last "#" $pot_email] > -1} {
....
}
}
Alternatives to this are:
- ${component_name}-bugs@openacs.org (where component_name could be openacs or dotlrn or contacts or whatever), to store a new bug in bug-tracker
- username@openacs.org (to do mail-through using the user name, which allows you to hide the actual e-mail of the user whom you are contacting).
Cleanup
Once all callbacks have been fired off, e-mails need to be deleted from the Maildir directory and files which have been extracted need to be deleted as well from the /tmp directory.
Created by Dave Bauer, last modified by Gustaf Neumann 24 Aug 2020, at 01:43 PM
Sending email on certain events in OpenACS/.LRN is done very haphazardly. This needs to be rewritten so there is a simple way to figure out when an email will be sent, and allow proper handling of user preferences, administrative parameters, and customization.
There are cases where the system (OpenACS or .LRN) needs to send out email, for example, when a new user joins, requests a password reset, or is added to a subsite or .LRN community.
Right now there isn't any system-wide way to mange this email. In some cases, the administrator is notified an email will be sent and is given the option to edit the email before it is sent, but there is no one way this is done. There are several pages that call ns_sendmail explicitly, or acs_mail_lite::send explicitly. There are more places this happens in .LRN. Unfortunately there is also a "magic" place where email is sent that is totally unexpected. Inside the dotlrn_community::membership_approve procedure, there is a call to dotlrn_community::send_member_email, which will send an email to the user when the membership is approved, if 1) a parameter is set and 2) an administrator has created and enabled an email message to be sent.
In addition there is a email sent using the "spam" package in dotlrn/www/admin/users-add-to-community where a dotlrn sitewide administrator can add users to a community, and the users are automatically emailed. In this case the administrator is not notified that an email is sent, or given an opportunity to customize or suppress the email. This causes problems when an administrator attempts to fix a problem by adding a user to a community automatically, and the user is sent a confusing message.
The dotlrn package allows for a custom email to get written by the community admin for each community, but the admin is not allowed to choose if the email is sent when an individual member is added. It is either on or off, always sent, or never sent.
dotlrn-ecommerce extends this by adding several more events for application submission, approval, rejection, etc. And admin can edit these emails on a sitewide or per community basis. In most cases the email is automatically sent, in one o r two cases the admin can edit the email, but not suppress it.
This leads to unwanted email. Often an admin must manually add or remove someone from a subsite or community or otherwise handle a problem. This can lead to welcome emails being sent at the wrong time, confusing the users.
A system wide solution would allow packages to create events where email is sent by the system (besides subscribed notifications). This solution would provide an includable interface for creating and editing a default email message for the events. It would also provide an interface to notify an admin that an email will be sent, giving options to suppress the email or edit the content of the email before sending it.
The beginnings of this feature exist in the dotlrn_member_emails table, dotlrn_community::send_member_email procedure, and the dotlrn-ecommerce package which has a few pages that replicate this interface, but probably needs more generalization to allow working with subsites as well as dotlrn communities. There is also the reusable include for editing the default emails under dotlrn/lib/member-email
This proposal would provide a comprehensive solution for handling system level email events, allowing admins to know when an email is sent, and provide a consistent user interface to manage the emails.
MS:
You could achieve this in a general way by using the acs-lang interface at least for the subject and body. For each object_id you would create a new message key, e.g. acs-translations.welcome_email_subject_${object_id} and acs-translations.welcome_email_body_${object_id}. If you have multiple emails per community / subsite, you would rename them to acs-translations.confirmation_email_subject_${object_id} aso. A general interface would then be provided to look for all language key combinations of acs-translations.email_xxx, allowing you to edit the messages for all communities and subsites. If you want to edit them for only one, then you can look for all who have the same object_id.
A default message would be given with acs-translations.email_subject_welcome, which the mail sending could default to in case no specific language key exists for the community / object_id. Furthermore, acs-translations.welcome_email_help message key is present describing what this email is about. A package like dotlrn would register the three default e-mail keys to start off with this and then the email-handling package can do the rest (e.g. with email-handler::send -to_party_ids -from_addr -email_type "welcome" -object_id).
This approach has the major advantage that you have internationalization by default.
Alternatively you could mimic the message handling done by contacts which allows you to have multiple message types, e.g. email, which you can fill in with default values, which has it's own I18N by having a locale stored in the DB table. But if you ask me, the acs-translations idea sounds better to me :). Though, you can obviously do this with your own tables as well, but you would loose on the nice features acs-lang has to offer.
Files that currently could trigger email
add_user calls add_user_to_community
add_user_to_community calls membership_approve (if applicable). membership_approve calls send_member_email unconditionaly
so any call to add_user or add_user_to_community could result in a call to send_member_email
dotlrn_community::add_user
dotlrn/www/admin/add-instructor-3.tcl
users-add-to-community.tcl
member-add-3.tcl
members-chunk-table.tcl
members.tcl
register.tcl
dotlrn-ecommerce/www/admin/gwu-section-new.tcl
ecommerce/shopping-cart-add.tcl.backup
register/index.tcl
tcl/implementation-procs.tcl
dotlrn_community::add_user_to_community
dotlrn/tcl/class-procs.tcl
club-procs.tcl
community-procs.tcl
dotlrn-callback-procs.tcl
dotlrn_community::membership_approve
dotlrn/www/approve.tcl
dotlrn/www/admin/commmunity-members-add-to-community.tcl
users-add-to-community-email.tcl
dotlrn-ecommerce/www/admin/application-approve.tcl
Created by Gustaf Neumann, last modified by Gustaf Neumann 24 Aug 2020, at 01:40 PM
Starting with version 5.9.1, OpenACS supports Content Security Policies (CSP), which is a means to secure websites against a range of Cross Side Scripting (XSS) attacks. In short, a CSP allows a developer to deactivate unneeded features in the browser of the client to provide there a sandbox with the minimum required capabilities. The CSP can allow e.g. just to retrieve .js files just form certain sites, or it can disallow script tags within the page, which might be injected by an attacker (for a more detailed introduction and tutorial, see CSP Reference, Google Developer Guide for CSP).
In general, a CSP defines the rules what should be allowed in a page. This could be done static for the whole page, but this means that the CSP rules must allow everything which is needed on a page with the highest requirements (e.g. a page with a richtext editor needs probably a script-src 'unsafe-eval' directive). This could render CSP pretty useless.
Therefore, OpenACS supports a CSP generator, which generates a CSP rule-set for every page dynamically based on the requirements of the page. A web developer can specify the requirements of a page/proc with the command security::csp::require. For example, the current OpenACS theme uses in its plain-master the following directives.
security::csp::require img-src ipv6-test.com
security::csp::require style-src maxcdn.bootstrapcdn.com
security::csp::require script-src maxcdn.bootstrapcdn.com
security::csp::require font-src 'self'
security::csp::require font-src maxcdn.bootstrapcdn.com
Based on the directives of the pages and the directives of the master templates, the security policy of the pages is built (typically in the blank-master). For example, the content security policy of the start page of OpenACS is
default-src 'self';
font-src 'self' maxcdn.bootstrapcdn.com data:;
img-src ipv6-test.com 'self';
report-uri /SYSTEM/csp-collector.tcl;
script-src maxcdn.bootstrapcdn.com 'self' 'nonce-49DBB4A924EA648C3025F7DD8C2553DC0EC700D1';
style-src maxcdn.bootstrapcdn.com 'self' 'unsafe-inline';
With this CSP, openacs.org gets an A+ rating from securityheaders.io.
Deactivating CSP for a Site
Per default, the content security policies are turned on. All packages of the oacs-5-9 branch can be used with the enabled content security policies. However, when a website contains legacy code using JavaScript, for which no content security policies are defined, this will result into non-functioning pages. Therefore, a website administrator can set the package parameter CSPEnabledP (in the package parameters of ACS Kernel in "security" section) to "0" to deactivate the CSP.
For Developers
In order to make old packages (not included in the oacs-5-9 branch) or newly developed packages CSP compliant, one should be aware that all inline code is considered harmful. This includes <script> elements, but also "javascript:" URIs or on* event handlers.
<script> Elements
The CSP guidelines recommend to replace the such elements in favor of JavaScript files obtained from the same source as the page itself. However, this is not always practical, especially, when JavaScript is generated dynamically. In such cases, two approaches are possible to make the script tag acceptable (without allowing all scripts on the page). CSP 2 offers the ability to add nonces or cryptographic hashes to secure this elements. OpenACS supports the first approach.
A nonce value is essentially a one-time value which can't be predicted by an attacker. OpenACS generates by its security-procs such as value and saves it in a global variable ::__csp_nonce. This can be used in the Tcl code or in an ADP page like in the following example:
<script language="JavaScript"
type="text/javascript"
<if @::__csp_nonce@ not nil> nonce="@::__csp_nonce;literal@"</if>
>
...
</script>
Event handlers and "javascript:" URI
Most work are probably changes concerning event handlers (e.g. onclick, onblur, ...) and "javascript:" URIs (having "javascript" in the protocol part of the URI). In general, such code pieces must be refactored (see e.g. 1 or 2 for examples).
OpenACS 5.9.1 offers to ease this process the function template::add_event_listener, which can be used to register event handlers in a compliant fashion either per HTML ID or per CSS class (see cal-item-new.tcl or in forums/lib/message/row2.tcl for examples, how add_event_listener can be used).
Created by Gustaf Neumann, last modified by Gustaf Neumann 21 Aug 2020, at 12:47 PM
Newer versions of NaviServer produce by default an error, when a script tries to write a connection, which as already closed by the server (e.g. doing two ad_returndirect in a sequence). Such program code does not make sense and indicates an error that should be fixed.
In general, one can silence the behavior or fix it. To silence the behavior, on can alter the NaviServer config file to silently swallow these error conditions named "rejectalreadyclosedconn". See e.g. Sample configuration file
Recent version of OpenACS (oacs-5-10) have such error conditions already fixed in all the actively maintained packages in the oacs-5-10 branch. In order to fix custom packages, one has to watch out for the following situations:
- Request pages: all after every command that finishes a request a call to "ad_script_abort" is required. Such commands are essentially all "ns_return*" commands and the "ad_*" counterparts.
- Filters: in case, an application handles such commands in filters, the filter has to return with "filter_return" to stop the filter chain (i.e. not to call further filters). Notice that the documentation of ns_register states that the request page is as well handled to trigger e.g. the access log entry. So, in rare conditions, one has to check as well for additional closed cases by using "ns_conn isconnected".
Sometimes, it might be still tricky to find such occurrences. On recent versions of OpenACS one can use the xo* machinery to activate/deactivate Tcl command traces for such commands via ds/shell, like e.g. in:
xo::broadcast send {
set traced_cmds [info commands ::ns_return*]
foreach cmd $traced_cmds {trace add execution $cmd enter {::tcltrace::before}}
}
"xo::broadcast" sends the command to every tcl based thread that executes the provided command. Tracing can be deactivated by replacing "trace add" by "trace remove" in this command.
Plain OpenACS (in oacs-5-10) has as well some support for this via acs-tcl/tcl/tcltrace-init.tcl.
Created by Gustaf Neumann, last modified by Gustaf Neumann 13 Jul 2020, at 02:24 PM
On sites with high number of configured threads and high number of activated packages, the VM size of nsd might become large. One measures is to compile NaviServer and Tcl with the flag SYSTEM_MALLOC, and to run it with a memory efficient malloc library such as TCMalloc (using in the service file LD_PRELOAD=/usr/lib/libtcmalloc.so").
However, even then memory might become short, when "exec" is performed, even via the nsproxy module. The nsproxy module was developed to reduce memory consumption by running a separate process which communicates via pipes to nsd. The nsproxy module is designed to run multiple worker processes (the number can be configured), but when this number runs out (more such worker processes are needed) a new nsproxy worker process has to be created. This happens via a fork() system call under Linux, which can result in an out-of-memory message like the following:
Error: exec: ns_fork() failed: Cannot allocate memory
Error: exec failed: not enough memory
What should one do in such cases? In general, the first rule is to reduce the "exec" calls as far as possible, since these are relative slow and resource intensive, and NaviServer/Tcl provide a large number of built-ins or library functions, which should be used if possible.
Secondly, it is preferable to start many nsproxy workers rather soon in the live-time of nsd (e.g. at startup, when it has a small footprint) and ensure that the nsproxy module keeps these worker process alive a long time (by setting "idletimeout" to a high value)
ns_section ns/server/${server}/module/nsproxy {
# ns_param recvtimeout 5000
# ns_param waittimeout 1000
# ns_param idletimeout 300000
ns_param idletimeout 700000000
}
When all configured nsproxy worker processes are running all the time, there is not need to fork later, and the error above will not occur anymore. The following snippet shows, how to start all nsproxy worker processes by creatin an ns_job queue with sufficient threads, and start a simple command executed by via nsproxy asynchronously (using the "-detached" flag) in parallel.
set concurrency [ns_proxy configure ExecPool -maxslaves]
if {"q1" ni [ns_job queues]} {
ns_job create q1 $concurrency
}
#
# Queue a sufficient number of jobs to be executed in parallel
#
time {ns_job queue -detached q1 {exec sleep 1}} [expr {$concurrency * 2}]
Additionally, versions of NaviServer beyond 4.99.20 show via
ns_proxy stats ExecPool
the number of running worker processes of the ExecPool (also included in the process page of nsstats).
Created by Robert Taylor, last modified by Gustaf Neumann 01 May 2020, at 11:33 AM
Topic:
How to configure a Network Place under Windows XP to access a WebDAV folder in file storage.
Overview:
Creating a Network Place will allow you to drag and drop multiple files and even folders from your machine to your OpenACS or .LRN website. Once you have done these eleven (arguably ten) simple (really!) steps to create a permanent connection on your computer to your file storage location. The Network Place will be accessible through My Network Places.
Once you have established a Network Place to your File Storage documents, you can transfer multiple files, create/delete/edit folders, delete files. Creating and modifying files and folders through the Network Place is the same as you would do using Explorer. You can also open, edit and save files without the hassle of downloading, saving and uploading them on your machine!
Instructions:
 |
Step 1: To create a Network Place, open Windows Explorer.
Then, right click on "My Network Places" and select Open.
|
 |
Step 2: Under Network Tasks, Select "Add a Network Place."
|
 |
Step 3: Click "Next" to start the Wizard.
|
 |
Step 4: Highlight "Choose Another Network Location" (if it is not already) and click "Next."
|
|
|
Step 5: Copy the WebDAV URL listed in the file storage folder you want to access and paste it in as the "Internet or Network Address" for your connection.
|
 |
Step 6: Select "Yes" when you are prompted with the Security Alert dialog.
|

|
Step 7: Enter the email address you use to log into the site (or username if your site uses usernames) and password in the "Connect to:" dialog box. This establishes your connection to the Network Place.
|
 |
Step 8: Create a name for the new Network Place or accept the default and then click "Next."
|
 |
Step 9: Leave "Open this network place when I click Finish" checked.
Click "Finish" to complete the Wizard.
|
|
Step 10: The first time you do this, you may be prompted again with the Security alert and a login screen. Please say "Yes" to the alert.
|
 |
Step 11: You may also need to login again using your site email address and password. (This is a Microsoft thing, not a bug and we can't do anything about it.)
|
 |
Voila! Your new Network Place will open and you will see the folders as they appear in file-storage on your site. You can now open Explorer and select, drag and drop multiple files or folders from your machine into SloanSpace. After you do this, refresh your browser to see the files/folders.
The Network Place is permanent, so you only need to set it up once and reconnect to it when you need to (through My Network Places.
|
Created by Robert Taylor, last modified by Gustaf Neumann 17 Oct 2017, at 08:40 AM
Previous version of FAQ (with many more Q and A): /faq
A cookbook of procedures is available at: OpenACS Cookbook
Created by Michael Aram, last modified by Michael Aram 04 Jul 2017, at 05:31 PM
The intention of this page is to collect small refactoring snippets that one can/should apply to an existing OpenACS installation in order to improve and modernize its code base. Most of the recipes have already been applied to the official core packages.
Modernize Tcl
-
Refactor foreach {var1.. varN} $list {break} to lassign
See http://wiki.tcl.tk/1530
Deprecated code example
foreach {dog cat} $animalslist break
Recommended code example
lassign $animalslist dog cat
Command line for finding/replacing code
todo
-
Refactor multiple lindex operations to lassign
Deprecated code example
set dog [lindex $animalslist 0]
set cat [lindex $animalslist 1]
Recommended code example
lassign $animalslist dog cat
Command line for finding/replacing code
todo
-
Brace expr expressions
Deprecated code example
[expr $money - 1]
Recommended code example
[expr {$money - 1}]
Command line for finding/replacing code
todo
-
Replace string equal with eq in expressions
Deprecated code example
if {[string equal "" $dog]} {error "I want a dog!"}
Recommended code example
if {$dog eq ""} {error "I want a dog!"}
Command line for finding/replacing code
todo
-
Replace lsearch with in or ni in expressions
Deprecated code example
if {[lsearch -exact $animalslist $dog] != -1 } {error "I dont want a dog!"}
Recommended code example
if {$dog in $animalslist} {error "I dont want a dog!"}
Command line for finding/replacing code
todo
-
Replace eval with {*} if possible
Deprecated code example
eval mycommand $args
Recommended code example
mycommand {*}$args
Command line for finding/replacing code
todo
Best Practices
-
Use bind variables in SQL statements
Deprecated code example
db_string check "SELECT * FROM animals WHERE color = $color;"
Recommended code example
db_string check "SELECT * FROM animals WHERE color = :color;"
Command line for finding/replacing code
todo
-
Use util::http instead of util_http*, ns_httpget, ::http, ::xo::HttpRequest
Substitute Deprecated Procedures
-
Replace empty_string_p with eq ""
Deprecated code example
if {[empty_string_p $dog]} {error "I want a dog!"}
if {![empty_string_p $cat]} {error "I dont want a cat!"}
Recommended code example
if {$dog eq ""} {error "I want a dog!"}
if {$cat ne ""} {error "I dont want a cat!"}
Command line for finding/replacing code
todo
Rationale
Byte-compiled comparisons are faster.
-
Replace exists_and_not_null with info exists and ne
Deprecated code example
if {[exists_and_not_null cat]} {error "I dont want a cat!"}
Recommended code example
if {[info exists cat] && $cat ne "" } {error "I dont want a cat!"}
Command line for finding/replacing code
todo
Rationale
Byte-compiled comparisons are faster.
-
Replace ad_parameter with parameter::get
Deprecated code example
ad_parameter -package_id 123 SystemURL ""
Recommended code example
parameter::get -package_id 123 SystemURL -default ""
Command line for finding/replacing code
todo
-
Replace ad_require_permission with permission::require_permission
Deprecated code example
ad_require_permission $oid "read"
Recommended code example
permission::require_permission -object_id $oid -privilege "read"
Command line for finding/replacing code
fgrep -rl "ad_require_permission" packages/ | xargs sed -i.sedbak -r 's/ad_require_permission\s+([^\s]*)\s+([^\s]*)/permission::require_permission -object_id \1 -privilege \2/g'
-
Replace util_unlist with lassign
Deprecated code example
util_unlist $animalslist dog cat
Recommended code example
lassign $animalslist dog cat
Command line for finding/replacing code
todo
Created by Ryan Gallimore, last modified by Benjamin Brink 30 Jun 2017, at 05:23 PM
Please refer to OACS Theming for newer templating information & tutorial.
Template Files
Almost all pages on an OpenACS site use ACS Templating, and so their appearance is driven by a layer of different files. Let's examine how this works:
-
A templated page uses an ADP/Tcl pair. The first line in the ADP file is usually:
<master>
If it appears exactly like this, without any arguments, the template processor uses default-master for that subsite. For pages in /var/lib/aolserver/$OPENACS_SERVICE_NAME/www, this is /var/lib/aolserver/$OPENACS_SERVICE_NAME/www/default-master.adp and the associated .tcl file.
-
The default-master is itself a normal ADP page. It draws the subsite navigation elements and invokes site-master (/var/lib/aolserver/$OPENACS_SERVICE_NAME/www/site-master.adp and .tcl)
-
The site-master draws site-wide navigation elements and invokes blank-master (/var/lib/aolserver/$OPENACS_SERVICE_NAME/www/blank-master.adp and .tcl).
-
Blank-master does HTML housekeeping and provides a framework for special sitewide navigation "meta" elements such as Translator widgets and Admin widgets.
Figure 1. Site Templates

CSS Files
/packages/acs-subsite/www/resources/site-master.css contains styles for the following elements:
- Header
- User Bar
- Footer
- Navigation
- Widgets (Buttons)
- Text Styles
- Portlets
- acs-developer-support (along with /packages/acs-developer-support/www/resources/acs-developer-support.css)
/packages/acs-subsite/www/resources/default-master.css defines styles for the following elements:
- Table, TR, TD
- Calendar package
- combo boxes (for date dropdowns)
/packages/acs-templating/www/resources contains many other css stylesheets that are appropriately named. Each package may also often have its own stylesheet.
Selva Theme
Selva simplifies the work of customizing the look and feel of your OpenACS/dotLRN website. You will find Selva's documentation on each installation of openacs, once the package is installed, you will find its documentation as follows: http://yoursite/doc/theme-selva.
Since Selva is based purely on css you can have a better accessibility on designing your site.
Steps in placing logo using Selva
- Go to http://yoursite/admin/site-map/
- Search on theme-selva package and Click on its parameter link
- Look for the parameter named " logoUrl" and place the the filename or URL of the Logo that you want to put in.
- Go to http://yoursite/admin/site-map/, look for "Main Site" and click on parameters, then set the DefaultMaster parameter to /packages/theme-selva/www/selva-master
Note: This instruction assumed that you have already installed the package theme-selva in your openacs installation.