View · Search · Index

Weblog

Filtered by category Cookbook, 1 - 10 of 21 Postings (all, summary)

Running OpenACS behind a proxy

Created by Gustaf Neumann, last modified by Michael Aram 18 Mar 2019, at 10:14 AM

If one is running OpenACS behind a reverse proxy such as NGINX or pound, one should use the following configuration options.

  1. Make sure, the proxy server adds the following request header fields:
    • X-Forwarded-For: containing the IP address if the client making the actual request
    • X-SSL-Request: this parameter should be set, when the incoming requests of the proxy was an HTTPS request. This way, OpenACS can treat connections as secure, even when the the connection between the reverse proxy and NaviServer is a plain HTTP connection.
       
  2. In the configuration file of NaviServer (or AOLserver), make sure, the following parameter are set:
    • Parameter ReverseProxyMode in the global parameters (under ns/parameters). This parameter is used by the Tcl code to obtain the right value via [ad_conn peeraddr] or [ad_conn behind_proxy_p]. When the reverse proxy sets the X-SSL-Request header field, also [ad_conn behind_secure_proxy_p] will be true.
    • Parameter checkforproxy in the nslog section. By activating it, the entries in the access log will have the value provided from the proxy via the X-Forwarded-For header. If this is not set, the access log will show always the IP address of the proxy server (last mile connection).

In order to check, whether the settings are correct, check the results of the following command calls in ds/shell (when acs-developer-support is installed)

  • ad_conn behind_proxy_p
  • ad_conn behind_secure_proxy_p

Note that when a server is running behind a secure proxy, but ad_conn behind_secure_proxy_p returns 0, the security ratings of the server will be downgraded, since no secure cookies will be used, etc. To check the settings, run the following command in ds/shell, which should return a non-empty result.

  • ad_get_cookie ad_user_login_secure ""

 

 

Cross Site Request Forgery (CSRF)

Created by Gustaf Neumann, last modified by Michael Aram 30 Jan 2019, at 10:26 AM

Starting with OpenACS 5.9.1, OpenACS offers support for protecting against Cross Site Request Forgery (CSRF). In essence, this attack can cause a user’s web browser to perform an unwanted action on a trusted site for which the user is currently authenticated. The user gets a page presented, which looks harmless, but contains links or images that perform actions with the users credentials without the users consent. Note that the CSP does not protect a user against clicks on a malicious link.

CSRF protection works by ensuring that values for an action (e.g. by from a HTML form) are only accepted from a user that has received the form before. OpenACS generates by its security-procs a secure CSRF token value and provides it to a developer it in a global variable ::__csrf_token. When requests secured with the CSRF token are received, it can be validated on the server side. Note, that this mechanism is similar to "signing" values in OpenACS.

CSRF protection concerns of two parts: add the CSRF token to the form (POST requests) or to the href, and checking the received in the queries expecting input from CSRF protected resources. The first part works technically quite similar as securing CSP via nonces. Add code to the Tcl or ADP page that outputs the global variable (the test for the token is mostly for backwards compatibility)

    <form ...>
        ...
        <if @::__csrf_token@ defined>
           <input type="hidden" name="__csrf_token" value="@::__csrf_token;literal@">
        </if>
        ...
    </form>

Secondly, the page contract on the receiving side has to validate the csrf token. This can be achieved by adding a call to csrf::validate to the validation part of a page contract.

ad_page_contract {
    @author ...
    @creation-date ...
} -query {
  ...
} -validate {
   ...
   csrf { csrf::validate }
}

In the code base of OpenACS, CSRF protection was added on several places (e.g. public pages, the list template, etc.) such the checks of OpenACS sites on vulnerability scanners improve. Technically, it would be desirable to secure more places against CSRF attacks in the future. However, it depends on the requirements of a site whether or not e.g. the API browser or search should be CSRF protected. With protection turned on, one cannot share e.g. a link to a search with some other user (or a search engine). A site admin has to decide, how protected/public such links should be.

F. A. Q.

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 

Content Security Policy (CSP)

Created by Gustaf Neumann, last modified by Gustaf Neumann 18 Aug 2017, at 10:32 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).

 

 

 

Incoming E-Mail

Created by Malte Sussdorff, last modified by Benjamin Brink 05 Jul 2017, at 04:37 AM

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&#39;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:

  1. Install postfix
  2. Install smtp (for postfix)
  3. Install metamail (for acs-mail-lite)
  4. Edit /etc/postfix/main.cf
  5. Edit /etc/postfix/virtual   Add a regular expression to filter relevant incoming emails for processing by OpenACS. 
    @www.yourserver.com service0
  6. Edit /etc/postfix/master.cf - uncomment this line so postfix listens to emails from internet
    smtp inet n - n - - smtpd
  7. Create a mail directory as service0
    mkdir /home/service0/mail
  8. 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).
  9. 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
     
  10. Invoke postmap in OS shell to recompile virtual db:
    postmap /etc/postfix/virtual
  11. Restart Postfix. 
    /etc/init.d/postfix restart
  12. Restart OpenACS

 

Processing incoming e-mail

 

A sweeper procedure like acs_mail_lite::load_mails  should:

  1. scan the e-mails which are in the IncomingMaildir directory on a regular basis.
  2. check if any email came from an auto mailer.
  3. Parse new ones, and
  4. 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. 

Refactoring Recipes

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

  1. 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

  2. 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

  3. Brace expr expressions

    Deprecated code example

    [expr $money - 1]

    Recommended code example

    [expr {$money - 1}]

    Command line for finding/replacing code

    todo

  4. 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

  5. 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

  6. 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

  1. 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

  2. Use util::http instead of util_http*, ns_httpget, ::http, ::xo::HttpRequest

Substitute Deprecated Procedures

  1. 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.

  2. 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.

  3. 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

  4. 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'

  5. 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

Cookbook alt.toc

Created by Dave Bauer, last modified by Benjamin Brink 30 Jun 2017, at 05:29 PM

The OpenACS Cookbook. This is the place to link OpenACS tips and tricks, code fragments, etc.

 

Modifying the look of an installation

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

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.

 

 

 

Creating adp box tags for consistent html/css

Created by openacs community, last modified by Benjamin Brink 30 Jun 2017, at 05:15 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>}"

} 

XoWiki: List of the available includelets

Created by Gustaf Neumann, last modified by Benjamin Brink 30 Jun 2017, at 07:11 AM

This is a list of the includelets, which can be included in a xowiki page: 

The following includelets can be used in a page
  • {{activity-graph -max_edges 70 -cutoff 0.1 -max_activities:integer 100 -show_anonymous message}}

    Include an activity graph

  • {{available-includelets}}

    List the available includelets of this installation.

  • {{book -category_id -menu_buttons edit -folder_mode false -locale "" -range "" -allow_reorder "" -with_footer false}}

    Show contents in book mode.

  • {{bookmarklet-button -siteurl "" -label ""}}

    Include bookmarklet button that makes it easy to add the current page as a bookmark in the browser of the client.

  • {{categories -tree_name "" -tree_style:boolean 1 -no_tree_name:boolean 0 -count:boolean 0 -summary:boolean 0 -locale "" -open_page "" -order_items_by title,asc -style mktree -category_ids "" -except_category_ids "" -allow_edit false -ordered_composite}}

    List the specified category tree.

  • {{categories-recent -max_entries:integer 10 -tree_name "" -locale "" -pretty_age off}}

    Display recent entries by categories.

  • {{chat -title "" -chat_id "" -mode "" -path "" -skin -login_messages_p -logout_messages_p -avatar_p -timewindow}}

    Include a chat in the current page

  • {{child-resources -skin:optional yui-skin-sam -show_types ::xowiki::Page,::xowiki::File,::xowiki::Form,::xowiki::FormPage -regexp:optional -with_subtypes:boolean,optional false -orderby:token,optional last_modified,desc -publish_status:wordchar ready -view_target "" -html-content -parent . -columns objects edit object_type name last_modified mod_user duplicate delete -hide "" -menubar ""}}

    Include the content of the current folder somewhat similar to explorer.

  • {{collab-graph -max_edges 70 -cutoff 0.1 -show_anonymous message -user_id}}

    Include a collaboration graph

  • {{community-link -text "" -url ""}}

    Include a link to the community including the current page. This includelet is designed to work with dotlrn.

  • {{composite-form -edit_links:boolean false -pages "" -ordered_pages}}

    Create a form from the selection

  • {{copy-item-button -page_id -alt copy -book_mode false}}

    Button to copy a page

  • {{create-item-button -page_id -alt new -book_mode false}}

    Button to create a new page based on the current one

  • {{creation-date -source "" -format %m-%d-%Y}}

    Include the creation date of the current or specified page in the provided format.

  • {{current-irc-log -date ""}}

  • {{delete-item-button -page_id -title Delete -alt delete -book_mode false}}

    Button to delete the current or a different page

  • {{delicious -description "" -tags "" -url}}

    Add a button to submit article to delicious.

  • {{digg -description "" -url}}

    Add a button to submit article to digg.

  • {{edit-item-button -page_id -title Edit -alt edit -book_mode false -link "" -target ""}}

    Button to edit the current or a different page

  • {{flowplayer -mp4:required}}

    Include an mp4 image using flowplayer

  • {{folders -show_full_tree false -context_tree_view false}}

    List the folder tree of the current instance

  • {{form-menu -form_item_id:integer -parent_id -form -buttons new answers -button_objs -return_url}}

    Include a form menu for the specified Form

  • {{form-stats -form -parent_id -property _state -orderby count,desc -renderer table}}

    Include form statistics for the specofied Form page.

  • {{form-usages -form_item_id:integer -form -parent_id -package_ids "" -orderby _last_modified,desc -view_field _name -publish_status all -field_names -hidden_field_names "" -extra_form_constraints "" -inherit_from_forms "" -category_id -unless -where -csv true -voting_form -voting_form_form "" -voting_form_anon_instances t -generate -with_form_link true -with_categories -wf -bulk_actions "" -buttons edit delete -renderer ""}}

    Show usages of the specified form.

  • {{get -variable -form_variable -source ""}}

    Get an instance variable from the current or from a different page.

  • {{graph}}

  • {{gravatar -email:required -size 80}}

    Include gravatar picture for the specified email

  • {{html-file -title "" -extra_css "" -levels 0 -file:required}}

    Include the specified HTML file

  • {{iframe -title "" -url:required -width 100% -height 500px}}

    Include an iframe contining the specified URL

  • {{item-button}}

  • {{jquery-carousel}}

    Display a sequence of pages via jquery-carousel, based on book includelet.

  • {{jquery-cloud-carousel}}

    Display a sequence of pages via jquery-cloud-carousel, based on book includelet.

  • {{jquery-infinite-carousel}}

    Display a sequence of pages via jquery-infinite-carousel, based on book includelet.

  • {{jquery-spacegallery}}

    Display a sequence of pages via jquery-spacegalleryl, based on book includelet.

  • {{kibana -chart openacs-status-codes -from now-24h -to now -hash "" -width 800 -height 400}}

  • {{last-visited -max_entries:integer 20}}

    Display last visited pages.

  • {{link-with-local-return-url -text "" -url ""}}

    Insert a link with extra return URL pointing the current object. This is particularly useful in cases, where a return URL must be created for a page that does not yet exist at time of definition (e.g. for link pointing to concrete workflow instances)

  • {{most-frequent-visitors -max_entries:integer 15}}

    List the most frequent visitors.

  • {{most-popular -max_entries:integer 10 -interval}}

    Display most popular pages of this wiki instance.

  • {{my-categories -summary 1}}

    List the categories associated with the current page.

  • {{my-general-comments}}

    List the general comments available for the current page.

  • {{my-references}}

    List the pages which are referring to the current page.

  • {{my-refers}}

    List the pages which are referred to the current page.

  • {{my-tags -summary 1}}

    List the tags associated with the current page.

  • {{my-yahoo-publisher -publisher "" -rssurl}}

    Name of the publisher, when posting URLs to my yahoo (use in connection with with_yahoo_publisher).

  • {{presence -interval 10 minutes -max_users:integer 40 -show_anonymous summary -page}}

    Show users actively in the wiki.

  • {{random-form-page -form:required -publish_status ready -expires 600}}

    Include random form page (instance of the specified form)

  • {{recent -max_entries:integer 10 -allow_edit:boolean false -allow_delete:boolean false -pretty_age off}}

    Display recent modified entries.

  • {{references-graph -folder . -page "" -link_type link -rankdir LR -fontsize 12}}

    Include a graph of the (partial) link structure in a wiki, starting either with a page or a folder. When a page is provided, the local link structure of this page is visualized (including incoming and outgoing links of the page; e.g. -page "." for the current page). Alternatively, the content of a folder can be shown.

  • {{rss-button -span 10d -name_filter -entries_of -title}}

    Include an RSS button referring to pages of the specified time span.

  • {{rss-client -url:required -max_entries:integer 15}}

    Include RSS content

  • {{selection -edit_links:boolean true -pages "" -ordered_pages "" -source -menu_buttons edit -range ""}}

    Provide a selection of pages

  • {{set-parameter}}

    Set a parameter accessible to the current page (for certain tailorings), accessible in the page via e.g. the query parameter interface.

  • {{slidy}}

    Display a sequence of pages via W3C slidy, based on book includelet

  • {{tags -limit:integer 20 -summary:boolean 0 -popular:boolean 0 -page}}

    Display specified tags.

  • {{timeline -user_id -data timeline-data -interval1 DAY -interval2 MONTH}}

    Include a timeline of changes (based on yahoo timeline API)

  • {{toc -style "" -renderer "" -open_page "" -book_mode false -folder_mode false -ajax false -expand_all false -remove_levels 0 -category_id -locale "" -source "" -range "" -allow_reorder "" -include_in_foldertree true}}

    Show table of contents of the current wiki. The "toc" includelet renders the page titles of the current files based on the value of the "page_order" attributes. Only those pages are rendered that have a non-empty "page_order" field.

  • {{unread-items -max_entries:integer 20}}

    List unread items.

  • {{unresolved-references}}

    List the pages with unresolved references in the current xowiki/xowf package. This is intended for use by admins.

  • {{user-timeline -user_id -data timeline-data -interval1 DAY -interval2 MONTH}}

    Include a timeline of changes of the current or specified user (based on yahoo timeline API)

  • {{view-item-button -page_id -title View -alt view -link "" -book_mode false}}

    Button to view the current or a different page

  • {{wf-todo -workflow "" -user_id -ical 0 -max_entries}}

  • {{yui-carousel -title "" -item_size 600x400 -image_size -num_visible 1 -play_interval 0 -auto_size 0 -folder -glob "" -form ""}}

    Include YUI carousel showing the pages of the specified or current folder.

Next Page
previous October 2019
Sun Mon Tue Wed Thu Fri Sat
29 30 1 2 3 4 5
(2) 6 7 8 9 10 11 (1) 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31 1 2

Popular tags

17 , 5.9.0 , 5.9.1 , ad_form , ADP , ajax , aolserver , asynchronous , bgdelivery , bootstrap , bugtracker , CentOS , COMET , compatibility , CSP , CSRF , cvs , debian , emacs , engineering-standards , fedora , FreeBSD , guidelines , host-node-map , hstore , includelets , install , installation , installers , install-ns
No registered users in community xowiki
in last 30 minutes
Contributors

OpenACS.org