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

Weblog Page

Filtered by date 2017-06-20, 1 - 2 of 2 Postings (all, summary)

Using List Builder

Created by Gustaf Neumann, last modified by Gustaf Neumann 20 Jun 2017, at 08:43 AM

Using List Builder

List Builder (LB) was introduced in acs-templating 4.6.4, and it allows you to make tables very easily. It took me about two hours to convert a table of mine to LB, and the time is well worth it, because you don't have to manually put in all the code for sorting. It also seems very flexible.

Plus, since you are benefiting from my learning process, you probably won't take two hours to learn it!

Example

The best example I've found is at the logger admin page. It uses list-builder well, and does nifty things with filters, etc..

The basic idea

Walk with me as I convert my index.tcl and index.adp file to list-builder. This is the end of the .tcl file:
# An HTML block for the breadcrumb trail                                         
set context_bar [ad_context_bar]
set title "Organizations"

db_multirow orgs orgs_query { }
The index.adp file looks something like this:
<master>                                                                   

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

<if @orgs:rowcount@ gt 0>                                                  
        <table cellpadding="3" cellspacing="0">                            

        <tr>                                                               
        <th>Action                                                         
        <th>Name                                                           
        <th>Notes                                                          
</if>                                                                      

<multiple name="orgs">                                                     

        <tr>                                                               

        <td>                                                               
        <a href="one?organization_id=@orgs.organization_id@">Details</a>                                                                             
        <if @write_p@>                                                     
          <a href="add-edit?organization_id=@orgs.organization_id@">Edit</a>                                                                         
        </if>                                                              
        <if @delete_p@>                                                    
          <a href="delete?organization_id=@orgs.organization_id@">Delete</a>                                                                         
        </if>                                                              
        </td>                                                              

        <td>@orgs.name@</td>                                         
        <td>@orgs.notes@</td>                                        
 ...
        </tr>                                                              

</multiple>                                                                

<if @orgs:rowcount@ gt 0>                                                  
        </table>                                                           
</if>                                                                      

<if @create_p@>                                                            
<ul>                                                                       
<li><a href="add-edit">Add an organization</a></p>       
</ul>                                                                      
</if>
You would then change the .tcl file to look like this:
template::list::create \
    -name orgs \
    -multirow orgs \
    -key organization_id \
    -elements {
        organization_id {
        }
        name {
        }
        notes {
        }
    } \
    -main_class {
        narrow
    }

db_multirow -extend { item_url } orgs orgs_query {
} {
    set item_url [export_vars -base "one" {organization_id}]
}
Then you change the .adp file to look like this:
<master>                                                                   

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

<listtemplate name="orgs"></listtemplate>

Common errors

If you get an error like this:
Too many positional parameters specified
     while executing
"template::list::create__arg_parser"
     (procedure "template::list::create" line 1)
     invoked from within
"template::list::create \ "
     ("uplevel" body line 86)
     invoked from within
Then the problem is this:
This error means one of your arguments is being interpreted not as a named switch (-elements ...) but as a positional argument (no switch).

This could easiliy be a line that doesn't have the \ at the end, or that has a space character after the \, or some other little typo like that.

Thanks, Lars!

Where the data comes from

From Alex Vorobiev:
it is not clear from the docs, and may not be obvious, but the
-multirow switch lets developer point to a db_multirow statement.
in other words, one MUST create a db_multirow statement with a name
that matches -multrow switch of the list-builder.  the db_multirow
statement, in turn, points to an sql query in the corresponding
.xql file
db_multirow
-to add url columns, use extend with export_vars
-to get row number, use rownum; also see rowcount (in docs for the "multiple" template tag)

Adding in sorting

See adding in sorting From Alex Vorobiev:

for the default case, one must specify column name FOLLOWED by comma and sort order such as ASC or DESC

Several lines from the database in one list-builder cell?

OpenACS has a very cool <group> tag, which allows you to show several rows from the database in one row. For example, if you have the table foo, with columns x and y, with values of
x  y
---
1  3
1  4
2  5
You may want the output to look like:
1   3 4
2   5
or close to that, using an HTML table. Well, the <group> tag does that. Can we do that with list-builder?

Well, quite easily, it turns out.

Just put something like this in the definition of your column (preferably the last one):

        last_name {
            label "Who"
            display_template {
                <group column="item_id">@tasks.first_names@ @tasks.last_name@<br></group>
            }

        }
Unfortunately, this will mess up the alternating bands of light and dark, because the odd and even rows will not be valid.

Formatting your table

If you want your table to be more compact, put this in:
    -main_class {
        narrow
    } \

Adding in action buttons on top

Add as part of your definition:
    -actions {
        "Use process" "process-use" "Use a process"
        "Task calendar" "my_url" "View task calendar"
    } \
If you put variables or functions in there, you'll notice they don't get substituted. So instead, you do this:
    set actions [list "Use process" "process-use" "Use a process" "Task calendar" "my_url" "View task calendar"]

    -actions $actions \
Thanks, Dave.

Pagination

See pagination for list-builder

From Alex Vorobiev:
-multirow vs -page_query it is not immediately clear from the docs, and may not be obvious, but when using pagination, both switches are necessary. the purpose of the -page_query is to fetch the ENTIRE result set, whereas the sql statement used by multirow would fetch only the current page set.
caching with current paginator (not jon griffin's new paginator) use "-page_flush_p 1" to prevent paginator from caching the page set and confusing the hell out of you (when newly added records are suddenly not appearing on the page)

Returning CSV files

If you'd like to return a comma-separated-value file, follow Lars' instructions on returning a CSV from list-builder. These CSV files are opened by Excel or other spreadsheet programs, and make an easy way of getting information out of the database for office workers.

Getting list-builder to work on OpenACS 4.6.3

From Alex Vorobiev:
- copy acs-templating/tcl-list-procs.tcl and
acs-templating/resources/lists from CVS/HEAD to the appropriate
locations in 4.6.3 distribution
- 4.6.3 does not support the new "noquote" tag; any such tags can
be safely removed from templates in versions of openacs prior to 5.0
Caroline Meeks adds:
- you have to use list-procs.tcl version 1.3.  
- Restart the server, don't just watch the files.

Displaying hierarchies

Alex Vorobiev:
[ any information below that mentions pagination refers to old "broken"
paginator ]
- basically not possible with the current implementation of list-builder, 
see http://openacs.org/forums/message-view?message_id=28810
- poor man's hierarchies: one can build db_multirow as a join that
would fetch parent and children records in the same query, and then
use <group column=group_key> tag to iterate over the records with
the same "key" until the key changes (see "group" template tag docs).
in this case, pagination is very hairy, especially with joins:
     -> group "key" has to be same as the -page_query "key", where
     "key" is the query column
     -> if multirow query and page_query use the same joins, then the
     page_where_clause will list the same "key" id multiple times:
     for instance, project_id in ('455','455','466') - notice 455
     appears twice.  this causes fewer rows (than page_size) to be
     displayed on a page.  the solution is to change page_query -
     use table aliasing 'spm_projects j', the same as in the
     multirow query, but no joins.  if we don't do table aliasing,
     then the order by will break (order by j.project_id)

Joins with identical column names in two or more tables tables

Alex Vorobiev:
- pagination: add the aliased column name to -key: j.project_id
- sorting: add the aliased column name to orderby statements in the orderby block
- sorting by columns in more than one table (j.project_id
p.proposal_id) breaks because both tables need to appear in the
page_query, which in turn breaks # of records per page...
i thought that wrapping the page_query select in a "select distinct"
or select with a group by would yield, but both actually re-sort by
the distinct or groupby column, so there is no way to preserve the
sort order of the inner select statement... giving up for now until the new
paginator is out which may make all of this a moot point.

Misc. issues

Documentation

  • The best documentation is the api-doc documentation for template::list (since the openacs.org website is on a version of OpenACS before 5.0, it doesn't include documentation for list-builder. You'll need to look on your installation).
  • Look in cvs.openacs.org to see examples of list-builder usage. Lars has added it in to bug-tracker, forums, and logger, so you can use them as examples. Project-manager (in /contrib) also uses list-builder.
  • More documentation is available.

Moving an OpenACS instance to another server

Created by Gustaf Neumann, last modified by Gustaf Neumann 20 Jun 2017, at 08:30 AM

Background

First of all, let me describe my situation:

I have two OpenACS servers, each serving a different site. I'm going to move site A to site B's hardware, so that both of them will run off of the same hardware.

The name of the database will be called siteA.

This all took me less than an hour, including writing up this documentation. However, I leave off with the documentation before the whole job is done :-)

Steps

  1. Make a dump of the database from site A.
    pg_dump -f db_backup_030729.dmp siteA
    
  2. Any information that changes on the web site after the database backup will be lost in the transition. I was worried about new registrations after that date, so I looked at the admin page for users to see who had been registered recently. If there were any additions after the transition, I would add them back in manually. You could also take down the site during the transition.
  3. I have siteA on CVS, and siteB on CVS too. However, both of them are using CVS repositories on their own server. So I want to move one repository to the other location. Fortunately, they have different names (siteA and siteB in the /var/cvs directory). The CVS guide to moving repositories claims that it is a simple matter to move your repository from one server to another. We'll see.
  4. As root, tar up the repository for siteA.
    cd /cvsroot
    tar -cf siteA.tar siteA
    gzip siteA.tar
    
  5. Transfer cvs repository to site B:
    Log in to site B, as a normal user
    sftp root@siteA.com
    sftp> cd /cvsroot
    sftp> get siteA.tar.gz
    sftp> cd /web/siteA/database-backup
    sftp> get db_backup_030729.dmp
    sftp> exit
    su -
    cd ~username
    mv siteA.tar.gz /cvsroot
    cd /cvsroot
    tar -xzf siteA.tar.gz
    
  6. Oops, we need to create the user to own these files. This command may vary depending on which distribution you have (read the man pages first).
    useradd -m -g web siteA
    su - siteA
    
    Follow the rest of the directions on how to set up the user on the OpenACS Official Documentation. Specifically, on setting up the .bashrc and/or .bash_profile files. Make sure you add in the CVSROOT.
  7. Change permissions on the cvs repository. As root:
    ls -lh
    chown -R siteA.web siteA
    
  8. Check out a copy of the repository
    su - siteA
    cd /web
    cvs checkout siteA
    
  9. Continue with the OpenACS Official Install Guide except...
  10. When you get to the part about setting up the database, instead get the data from your database backup. See Restoring from backup for details.

    If you get an error like this while restoring:

    psql -f db_backup_030622.dmp siteA
    You are now connected as new user postgres.
    psql:db_backup_030622.dmp:12: ERROR:  function plpgsql_call_handler already exists with same argument types
    psql:db_backup_030622.dmp:20: ERROR:  Language plpgsql already exists
    psql:db_backup_030622.dmp:22: \connect: FATAL 1:  user "siteA" does not exist
    
    The problem is you have users with different names. The way I got around this is...

    I opened up the .dmp file in emacs and replaces all the references to the old siteA user to the new one.