Templating an Existing Tcl Page

Templating System : Migration

In a Nutshell

When templatizing a legacy Tcl page, your task is to separate code and graphical presentation. The latter goes into an ADP file; it contains essentially HTML, augmented by a few special tags and the @variable@ construct. The code goes into a Tcl script. In other words, a templated page consists of two files, a Tcl part that puts its results in data sources, and an ADP page (the template), into which these data sources will be interpolated to yield a complete HTML page.

General

As usual, the Tcl page should start with a call to ad_page_contract. In its -properties block you promise the data sources that your script will provide; they were earlier called page properties, hence the name of the option. Then your script performs all the computations and queries needed to define these data sources. There are special mechanisms for handling multirow data sources; see below.

At the end of the Tcl script, you should call ad_return_template. The template runs after the Tcl script, and can use these data sources.

Make sure that the fancy adp parser is enabled in your AOL ini file.

      [ns/server/myserver/adp]
      DefaultParser=fancy

A few more hints

Forms

There is nothing special about building forms; just use the <form> tag as always. All HTML tags can be used in the ADP file (template).

A simple page

First I take a page from the news package as an example. For simplicity, I pick item-view, which does not use a <form>. I reformatted it a bit to make three panes fit next to each other and to line up corresponding code.

old Tcl codenew
packages/news/www/item-view.tclpackages/news/www/item-view.adp
# /packages/news/admin/index.tcl
ad_page_contract {

    View a news item.

    @author Jon Salz (jsalz@arsdigita.com)
    @creation-date 11 Aug 2000
    @cvs-id $‌Id$

} {
    news_item_id:integer,notnull
}








db_1row news_item_select {
    select *
    from news_items
    where news_item_id = :news_item_id
}

set body "
[ad_header $title]
<h2>$title</h2>
[ad_context_bar [list "" "News"] $title]

<hr>

<p>Released $release_date:

<blockquote>
$body
</blockquote>

[ad_footer]
"

doc_return 200 text/html $body
return

ad_page_contract {

    View a news item.

    @author Jon Salz (jsalz@arsdigita.com)
    @creation-date 11 Aug 2000
    @cvs-id $‌Id$

} {
    news_item_id:integer,notnull
} -properties {
  body:onevalue
  release_date:onevalue
  title:onevalue
  header:onevalue
  context_bar:onevalue
  footer:onevalue
}

db_1row news_item_select {
    select *
    from news_items
    where news_item_id = :news_item_id
}




set context_bar [ad_context_bar \
    [list "" "News"] $title]











ad_return_template
          


























<master>
<property name="doc(title)">@title@</property>
<property name="context">@context;literal@</property>

<hr>

<p>Released @release_date@:

<blockquote>
@body@
</blockquote>

          

Multi-Row Data Sources

Technically, the result of a query that may return multiple rows is stored in several arrays. This datasource is filled by a call to db_multirow , and the repeating part of the HTML output is produced by the <multiple> tag. The following example shows the part of the index page of the News module that uses the mechanism, not a whole page.
old Tcl codenew
packages/news/www/index.tclpackages/news/www/index.adp
ad_page_contract {

    Displays a list of 
    available news items.

    @param archive_p show archived
                      news items?
    @author Jon Salz (jsalz@mit.edu)
    @creation-date 11 Aug 2000
    @cvs-id $‌Id$
} {
}
ad_page_contract {

    Displays a list of available
    news items.

    @param archive_p show archived
                      news items?
    @author Jon Salz (jsalz@mit.edu)
    @creation-date 11 Aug 2000
    @cvs-id $‌Id$
} {
} -properties {
  header:onevalue
  context_bar:onevalue
  subsite_id:onevalue
  subsite:multirow
item:multirow
  footer:onevalue
}
 
.........
append body "
<ul>
"

db_foreach news_items_select {
    select news_item_id, title
    from news_items_obj
    where context_id = :subsite_id
    and sysdate >= release_date
    and (   expiration_date is null
         or expiration_date > sysdate)
} {
    append body "<li><a href="
        \"item-view?news_item_id="\
        "$news_item_id\"
        >$title</a>\n"


} if_no_rows {
    append body "<li>There are 
        currently no news items
        available.\n"
}

append body "
<p><li>You can use the <a href=
    \"admin/\">administration
    interface</a> to post a new
    item (there's currently no
    security in place).

</ul>
"
db_multirow item
 news_items_select {
    select news_item_id, title
    from news_items_obj
    where context_id = :subsite_id
    and sysdate >= release_date
    and (   expiration_date is null
         or expiration_date > sysdate)
}
          

<ul>


<multiple name=item>







  <li><a href=
      "item-view?news_item_id=<%
      %>@item.news_item_id@"
      >@item.title@</a>
</multiple>

<if @item:rowcount@ eq 0>
  <li>There are
  currently no news items
  available.
</if>


<p><li>You can use the <a href=
  "admin/">administration
  interface</a> to post a new
  item (there's currently no
  security in place).

</ul>
          
Notes:

If you have a more complicated db_foreach, where logic is performed inside the body, then it might be helpful to build your own multirow variable. In the excert below, taken from /pvt/alerts.tcl and /pvt/alerts.adp, the foreach logic made it hard to use the db_multirow because it needed a combination of the output from sql and also the output of Tcl procedures using that value.

old Tcl codenew
packages/acs-core-ui/www/pvt/alerts.tclpackages/acs-core-ui/www/pvt/alerts.adp
ad_page_contract {
    @cvs-id $‌Id: migration.html,v 1.4.2.2 2022/04/27 12:22:29 gustafn Exp $
} {
}
ad_page_contract {
    @cvs-id $‌Id: migration.html,v 1.4.2.2 2022/04/27 12:22:29 gustafn Exp $
} {
} -properties {
    header:onevalue
    decorate_top:onevalue
    ad_footer:onevalue
    discussion_forum_alert_p:onevalue
    bboard_keyword_p:onevalue
    bboard_rows:multirow
    classified_email_alert_p:onevalue
    classified_rows:multirow
    gc_system_name:onevalue
}
 
.........


if { [db_table_exists "bboard_email_alerts"] } {
 
 set counter 0








 db_foreach alerts_list "
 select bea.valid_p, bea.frequency,
        bea.keywords, bt.topic, bea.rowid
 from bboard_email_alerts bea, bboard_topics bt
 where bea.user_id = :user_id
 and bea.topic_id = bt.topic_id
 order by bea.frequency" {
   incr counter

   if { $valid_p == "f" } {
     # alert has been disabled 
     set status "Disabled"
     set action "
     <a href="\"/bboard/alert-reenable\">
"     Re-enable</a>"
   } else {
     # alert is enabled
     set status "
     <font color=red>Enabled</font>"
     set action "
     <a href="\"/bboard/alert-disable\">
"     Disable</a>"
   }

   append existing_alert_rows "<tr>
   <td>$status</td>
   <td>$action</td>
   <td>$topic</td>
   <td>$frequency</td>"

   if { [bboard_pls_blade_installed_p] == 1 } {
     append existing_alert_rows "
     <td>\"$keywords\"</td>"
   }
   append existing_alert_rows "</tr>\n"

 }

 if  { $counter > 0 } {
   set wrote_something_p 1
   set keyword_header ""
   if { [bboard_pls_blade_installed_p] == 1 } {
     set keyword_header "<th>Keywords</th>"
   }
   append page_content "
   <h3>Your discussion forum alerts</h3>

   <blockquote>
   <table>
   <tr>
   <th>Status</th>
   <th>Action</th>
   <th>Topic</th>
   <th>Frequency</th>
   $keyword_header
   </tr>

   $existing_alert_rows
   </table>
   </blockquote>
   "
 }
}
          
set discussion_forum_alert_p 0

if { [db_table_exists "bboard_email_alerts"] } {
  set discussion_forum_alert_p 1

  set rownum 0

  if { [bboard_pls_blade_installed_p] == 1 } {
    set bboard_keyword_p 1
  } else {
    set bboard_keyword_p 0
  }
        
  db_foreach alerts_list "
  select bea.valid_p, bea.frequency,
         bea.keywords, bt.topic, bea.rowid
  from bboard_email_alerts bea, bboard_topics bt
  where bea.user_id = :user_id
  and bea.topic_id = bt.topic_id
  order by bea.frequency" {
  incr rownum

  if { $valid_p == "f" } {
    # alert has been disabled for some reasonset bboard_rows:[set rownum](status) "disable"
    set bboard_rows:[set rownum](action_url) "
    /bboard/alert-reenable"
  } else {
    # alert is enabledset bboard_rows:[set rownum](status) "enable"
    set bboard_rows:[set rownum](action_url) "
    /bboard/alert-disable"
  }

  set bboard_rows:[set rownum](topic) $topic
  set bboard_rows:[set rownum](frequency) $frequency
  set bboard_rows:[set rownum](keywords) $keywords
        
  } if_no_rows {
    set discussion_forum_alert_p 0
  }
  set bboard_rows:rowcount $rownum
  
}

          
          













<if @discussion_forum_alert_p@ eq 1>

<h3>Your discussion forum alerts</h3>

<blockquote>
   <table>
   <tr><th>Status</th>
       <th>Action</th>
       <th>Topic</th>
       <th>Frequency</th>
     <if @bboard_keyword_p@ eq 1>
       <th>Keyword</th>
     </if>
   </tr>
 <multiple name=bboard_rows>

   <tr>
      <if @bboard_rows.status@ eq "enabled">
       <td><font color=red>Enabled</font></td>
       <td><a href="@bboard_rows.action_url@">
       Disable</a></td>
      </if>
      <else>
       <td>Disabled</td>
       <td><a href="@bboard_rows.action_url@">
       Re-enable</a></td>
      </else>
       <td>@bboard_rows.topic@</td>
       <td>@bboard_rows.frequency@</td>
     <if @bboard_rows.bboard_keyword_p@ eq 1>
       <td>@keyword</td>
     </if>
   </tr>
 
 </multiple>
   </table>
</blockquote> 

</if>
          

Christian Brechbühler, Hiro Iwashima
Last modified: $‌Id: migration.html,v 1.4.2.2 2022/04/27 12:22:29 gustafn Exp $