Index: openacs-4/packages/acs-core-docs/www/xml/developers-guide/tutorial-advanced.xml =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-core-docs/www/xml/developers-guide/tutorial-advanced.xml,v diff -u -r1.19.2.2 -r1.19.2.3 --- openacs-4/packages/acs-core-docs/www/xml/developers-guide/tutorial-advanced.xml 18 Apr 2004 11:55:53 -0000 1.19.2.2 +++ openacs-4/packages/acs-core-docs/www/xml/developers-guide/tutorial-advanced.xml 5 Jul 2004 19:47:36 -0000 1.19.2.3 @@ -15,6 +15,9 @@ you've completed the basic tutorial. Write the Requirements and Design Specs + Before you get started you should make yourself familiar with + the tags that are used to write your documentation. For tips on + editing SGML files in emacs, see . It's time to document. For the tutorial we'll use pre-written documentation. When creating a package from scratch, start by copying the documentation template from @@ -29,13 +32,13 @@ www/doc directory. For this tutorial, you should instead install the pre-written documentation files for the tutorial app. Log in - as service0, create the standard + as $OPENACS_SERVICE_NAME, create the standard directories, and copy the prepared documentation: - [service0 service0]$ cd /var/lib/aolserver/service0/packages/myfirstpackage/ -[service0 myfirstpackage]$ mkdir -p www/doc/xml -[service0 myfirstpackage]$ cd www/doc/xml -[service0 xml]$ cp /var/lib/aolserver/service0/packages/acs-core-docs/www/files/myfirstpackage/* . -[service0 xml]$ + [$OPENACS_SERVICE_NAME $OPENACS_SERVICE_NAME]$ cd /var/lib/aolserver/$OPENACS_SERVICE_NAME/packages/myfirstpackage/ +[$OPENACS_SERVICE_NAME myfirstpackage]$ mkdir -p www/doc/xml +[$OPENACS_SERVICE_NAME myfirstpackage]$ cd www/doc/xml +[$OPENACS_SERVICE_NAME xml]$ cp /var/lib/aolserver/$OPENACS_SERVICE_NAME/packages/acs-core-docs/www/files/myfirstpackage/* . +[$OPENACS_SERVICE_NAME xml]$ OpenACS uses DocBook for documentation. DocBook is an XML standard for semantic markup of documentation. That means that the tags you use indicate meaning, not intended @@ -47,16 +50,14 @@ <revhistory>). Add a new record to the document version history. Look for the <authorgroup> tag and - add yourself as a second author. Save and exit. For tips on - editing SGML files in emacs, see + add yourself as a second author. Save and exit. Process the xml file to create html documentation. The html documentation, including supporting files such as pictures, is stored in the www/docs/ directory. A Makefile is provided to generate html from the xml, and copy all of the supporting files. If Docbook is set up correctly, all you need to do is: - [service0 xml]$ make + [$OPENACS_SERVICE_NAME xml]$ make cd .. ; /usr/bin/xsltproc ../../../acs-core-docs/www/xml/openacs.xsl xml/index.xml Writing requirements-introduction.html for chapter(requirements-introduction) Writing requirements-overview.html for chapter(requirements-overview) @@ -72,7 +73,7 @@ Writing admin-guide.html for chapter(admin-guide) Writing bi01.html for bibliography Writing index.html for book -[service0 xml]$ +[$OPENACS_SERVICE_NAME xml]$ Verify that the documentation was generated and reflects your changes by browsing to http://yoursite:8000/myfirstpackage/doc @@ -84,20 +85,20 @@ traverse the directory tree manually and add as you go. (More on CVS) - [service0 xml]$ cd .. -[service0 doc]$ cd .. -[service0 www]$ cd .. -[service0 myfirstpackage]$ cd .. -[service0 packages]$ cvs add myfirstpackage/ -Directory /cvsroot/service0/packages/myfirstpackage added to the repository -[service0 packages]$ cd myfirstpackage/ -[service0 myfirstpackage]$ cvs add www -Directory /cvsroot/service0/packages/myfirstpackage/www added to the repository -[service0 myfirstpackage]$ cd www -[service0 www]$ cvs add doc -Directory /cvsroot/service0/packages/myfirstpackage/www/doc added to the repository -[service0 www]$ cd doc -[service0 doc]$ cvs add * + [$OPENACS_SERVICE_NAME xml]$ cd .. +[$OPENACS_SERVICE_NAME doc]$ cd .. +[$OPENACS_SERVICE_NAME www]$ cd .. +[$OPENACS_SERVICE_NAME myfirstpackage]$ cd .. +[$OPENACS_SERVICE_NAME packages]$ cvs add myfirstpackage/ +Directory /cvsroot/$OPENACS_SERVICE_NAME/packages/myfirstpackage added to the repository +[$OPENACS_SERVICE_NAME packages]$ cd myfirstpackage/ +[$OPENACS_SERVICE_NAME myfirstpackage]$ cvs add www +Directory /cvsroot/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www added to the repository +[$OPENACS_SERVICE_NAME myfirstpackage]$ cd www +[$OPENACS_SERVICE_NAME www]$ cvs add doc +Directory /cvsroot/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/doc added to the repository +[$OPENACS_SERVICE_NAME www]$ cd doc +[$OPENACS_SERVICE_NAME doc]$ cvs add * cvs add: cannot add special file `CVS'; skipping cvs add: scheduling file `admin-guide.html' for addition cvs add: scheduling file `bi01.html' for addition @@ -120,32 +121,32 @@ cvs add: scheduling file `user-guide.html' for addition cvs add: scheduling file `user-interface.dia' for addition cvs add: scheduling file `user-interface.png' for addition -Directory /cvsroot/service0/packages/myfirstpackage/www/doc/xml added to the repository +Directory /cvsroot/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/doc/xml added to the repository cvs add: use 'cvs commit' to add these files permanently -[service0 doc]$ cd xml -[service0 xml]$ cvs add Makefile index.xml +[$OPENACS_SERVICE_NAME doc]$ cd xml +[$OPENACS_SERVICE_NAME xml]$ cvs add Makefile index.xml cvs add: scheduling file `Makefile' for addition cvs add: scheduling file `index.xml' for addition cvs add: use 'cvs commit' to add these files permanently -[service0 xml]$ cd ../../.. -[service0 myfirstpackage]$ cvs commit -m "new package" +[$OPENACS_SERVICE_NAME xml]$ cd ../../.. +[$OPENACS_SERVICE_NAME myfirstpackage]$ cvs commit -m "new package" cvs commit: Examining . cvs commit: Examining www cvs commit: Examining www/doc cvs commit: Examining www/doc/xml -RCS file: /cvsroot/service0/packages/myfirstpackage/www/doc/admin-guide.html,v +RCS file: /cvsroot/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/doc/admin-guide.html,v done Checking in www/doc/admin-guide.html; -/cvsroot/service0/packages/myfirstpackage/www/doc/admin-guide.html,v <-- admin-guide.html +/cvsroot/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/doc/admin-guide.html,v <-- admin-guide.html initial revision: 1.1 done (many lines omitted) -[service0 myfirstpackage]$ +[$OPENACS_SERVICE_NAME myfirstpackage]$
Upgrading a local CVS repository - +
@@ -199,11 +200,11 @@ access to data that users aren't interested in or aren't allowed to see you will need dedicated admin pages. The conventional place to put those dedicated admin pages is in the - /var/lib/aolserver/service0/packages/myfirstpackage/www/admin + /var/lib/aolserver/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/admin directory. -[service0 www]$ mkdir admin -[service0 www]$ cd admin +[$OPENACS_SERVICE_NAME www]$ mkdir admin +[$OPENACS_SERVICE_NAME www]$ cd admin Even if your application doesn't need any admin pages of its own you will usually need at least one simple page with a bunch of links to existing @@ -212,7 +213,7 @@ categories. The listing below adds a link to the Parameters UI of our package. -[service0 admin]$ vi index.adp +[$OPENACS_SERVICE_NAME admin]$ vi index.adp <master> <property name="title">@title;noquote@</property> @@ -222,7 +223,7 @@ <li><a href="@parameters_url@" title="Set parameters" class="action_link">Set parameters</a></li> </ul> -[service0 admin]$ vi index.tcl +[$OPENACS_SERVICE_NAME admin]$ vi index.tcl ad_page_contract {} { } -properties { @@ -258,7 +259,7 @@ In order to display the link to the admin page only to users that have admin privileges add the following code near the top of -/var/lib/aolserver/service0/packages/myfirstpackage/www/admin/index.tcl: +/var/lib/aolserver/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/admin/index.tcl: @@ -274,7 +275,7 @@ In -/var/lib/aolserver/service0/packages/myfirstpackage/www/admin/index.adp put: +/var/lib/aolserver/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/admin/index.adp put: <if @admin_p@ ne nil> @@ -322,7 +323,7 @@ The way to achieve this is is to provide a link to the Category Management pages. Add the following snippet to your - /var/lib/aolserver/service0/packages/myfirstpackage/www/admin/index.tcl + /var/lib/aolserver/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/admin/index.tcl file: @@ -332,7 +333,7 @@ and the following snippet to your - /var/lib/aolserver/service0/packages/myfirstpackage/www/admin/index.adp + /var/lib/aolserver/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/admin/index.adp file: @@ -421,7 +422,7 @@ script execution. The database commands: - [service0@yourserver www]$ emacs note-delete.xql + [$OPENACS_SERVICE_NAME@yourserver www]$ emacs note-delete.xql <?xml version="1.0"?> <queryset> <fullquery name="do_delete"> @@ -436,7 +437,7 @@ </fullquery> </queryset> And the adp page: - [service0@yourserver www]$ emacs note-delete.adp + [$OPENACS_SERVICE_NAME@yourserver www]$ emacs note-delete.adp <master> <property name="title">@title@</property> <property name="context">{@title@}</property> @@ -451,170 +452,14 @@ - - Categories - You can associate any ACS Object with one or more categories. - In this tutorial we'll show how to equip your application with user - interface to take advantage of the Categories service. - - - We'll start by installing the Categories service. Go to - /acs/admin and install it. This step - won't be necessary for the users of your applications because you'll create - a dependency with the Package Manager which will take care that the - Categories service always gets installed when your application gets - installed. - - - Now that we have installed the Categories service we can proceed to - modifying our application so that it can take advantage of it. We'll do it - in three steps: - - - - The Categories service provides a mechanism to associate one or - more category trees that are relevant to - a particular application instance. One example of such tree is a tree of - geographical locations. Continents are on the top of such tree, - each continent containing countries etc. Another tree might - contain market segments etc. Before users of your application - can take advantage of the Categories service there needs to be a - way for administrators of your application to choose which - category trees are applicable for the application. - - The way to achieve this is is to provide a link - to the Category Management pages. Add the following snippet to your - /var/lib/aolserver/service0/packages/myfirstpackage/www/admin/index.tcl - file: - - -set category_map_url [export_vars -base \ - "[site_node::get_package_url -package_key categories]cadmin/one-object" \ - { { object_id $package_id } }] - - - and the following snippet to your - /var/lib/aolserver/service0/packages/myfirstpackage/www/admin/index.adp - file: - - -<li><a href="@category_map_url@" - class="action_link">Site-Wide Categories</a> - - The link created by the above code will take the admin to the generic - admin UI where he can pick category trees that make sense for this - application. The same UI also includes facilities to build and edit - category trees. Notice that the only parameter in this example is - package_id so that category trees - will be associated with the object identified by this - package_id. The categorization - service is actually more general than that: instead of - package_id you could use an ID of - some other object that serves as a "container" in your application. - For example, if your discussion forums application supports multiple - forums you would use forum_id to - associate category trees with just that one forum rather than the - entire application instance. - - - Once the category trees have been selected users need a way - to categorize items. The easiest way to do this is by adding the - category widget type of the - form builder to note-edit.tcl. - To achieve this we'll need to use the -extend - switch to the ad_form command. Here's the - note-edit.tcl page with added sections - emphasized. -ad_page_contract { - This is the view-edit page for notes. + + Profile your code - @author Your Name (you@example.com) - @cvs-id $Id$ + + by Jade Rubick + - @param item_id If present, assume we are editing that note. Otherwise, we - are creating a new note. - -} { - item_id:integer,optional -} - -ad_form -name note -form { - {item_id:key} - {title:text {label Title}} -} - -category::ad_form::add_widgets \ - -form_name note \ - -container_object_id [ad_conn package_id] \ - -categorized_object_id [value_if_exists item_id] - -ad_form -extend -name note \ - -new_request { - permission::require_permission -object_id [ad_conn package_id] -privilege create - set page_title "Add a Note" - set context [list $page_title] -} -edit_request { - permission::require_write_permission -object_id $item_id - mfp::note::get \ - -item_id $item_id \ - -array note_array - - set title $note_array(title) - - set page_title "Edit a Note" - set context [list $page_title] -} -on_submit { - set category_ids [category::ad_form::get_categories \ - -container_object_id [ad_conn package_id]] -} -new_data { - mfp::note::add \ - -title $title \ - -item_id $item_id - - category::map_object \ - -remove_old \ - -object_id $item_id \ - $category_ids - - set message "Note $title added" -} -edit_data { - mfp::note::edit \ - -item_id $item_id \ - -title $title - - category::map_object \ - -remove_old \ - -object_id $item_id \ - $category_ids - - - set message "Note $title changed" -} -after_submit { - ad_returnredirect -message $message "." - ad_script_abort -} - - Note how we have replaced what was a single - ad_form invocation with two. The - -extend flag is used to build a form - incrementally. We had to do it so that we can insert the call to - category::ad_form::add_widgets. This - procedure will add as many category widgets as there are trees associated with - our package_id. The complementary proc - category::ad_form::get_categories will take - care of collecting the values after the form has been submitted. The block - -on_submit will get executed at this time, - followed by execution of either -new_data or - -edit_data, depending on whether we are adding - a new note or editing an existing one. - - - - - - - Profile your code There are several facilities for profiling your code in OpenACS. The first thing to do is to install the developer-support package and play around with it. But there @@ -877,7 +722,7 @@ look at the forums package. - + Hierarchical data by Jade Rubick @@ -990,15 +835,15 @@ Using .vuh files for pretty urls .Vuh files are special cases of .tcl files, used for rewriting incoming urls. We can use a vuh file to prettify the uri for our notes. Instead of note-edit?item_id=495, we can use note/495. To do this, we will need a new .vuh file for redirection and we will need to change the referring links in note-list. First, add the vuh: - [service0 service0]$ cd /var/lib/aolserver/service0/packages/myfirstpackage/www -[service0 www]$ emacs note.vuh + [$OPENACS_SERVICE_NAME $OPENACS_SERVICE_NAME]$ cd /var/lib/aolserver/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www +[$OPENACS_SERVICE_NAME www]$ emacs note.vuh Paste this into the file: example missing We parse the incoming request and treat everything after the final / as the item id. Note that this simple redirection will lose any additional query parameters passed in. Many OpenACS objects maintain a pretty-name, which is a unique, human-readable string, usually derived from title, which makes an even better 'pretty url' than a numeric id; this requires that your display page be able to look up an item based on pretty id. We use rp_form_put to store the item id in the internal register that the next page is expecting, and then redirects the request in process internally (ie, without a browser refresh). Next, modify note-list so that its link is of the new form.: - [service0 www]$ emacs ../lib/note-edit.tcl + [$OPENACS_SERVICE_NAME www]$ emacs ../lib/note-edit.tcl db_multirow \ -extend { @@ -1020,9 +865,169 @@ package. Commonly, you would use ad_conn package_url to build the URL. Otherwise, some of your links may be relative to the virtual directory (note/) instead of the actual directory that the note is - being served from + being served from. + + + + Laying out a page with CSS instead of tables + + .LRN home page with table-based layout + + + + + + A sample of the HTML code (full source) + <table border="0" width="100%"> + <tr> + <td valign="top" width="50%"> + <table class="element" border=0 cellpadding="0" cellspacing="0" width="100%"> + <tr> + <td colspan=3 class="element-header-text"> + <bold>Groups</bold> + </td> + </tr> + <tr> + <td colspan=3 class="dark-line" height="0"><img src="/resources/acs-subsite/spacer.gif"></td></tr> + <tr> + <td class="light-line" width="1"> + <img src="/resources/acs-subsite/spacer.gif" width="1"> + </td> + <td class="element-text" width="100%"> + <table cellspacing="0" cellpadding="0" class="element-content" width="100%"> + <tr> + <td> + <table border="0" bgcolor="white" cellpadding="0" cellspacing="0" width="100%"> + <tr> + <td class=element-text> + MBA 101 + + + .LRN Home with CSS-based layout + + + + + + A sample of the HTML code (full source) + <div class="left"> + <div class="portlet-wrap-shadow"> + <div class="portlet-wrap-bl"> + <div class="portlet-wrap-tr"> + <div class="portlet"> + <h2>Groups</h2> + <ul> + <li> + <a href="#">Class MBA 101</a> + If the CSS is removed from the file, it looks somewhat different: + + + + + + + + + + Sending HTML email from your application + + by Jade Rubick + + + Sending email is fairly simple using the acs-mail-lite + package. Sending HTML email is only slightly more complicated. + + + set subject "my subject" + + set message "<b>Bold</b> not bold" + + set from_addr "me@myemail.com" + + set to_addr "me@myemail.com" + + # the from to html closes any open tags. + set message_html [ad_html_text_convert -from html -to html $message] + + # some mailers chop off the last few characters. + append message_html " " + set message_text [ad_html_text_convert -from html -to text $message] + + set message_data [build_mime_message $message_text $message_html] + + set extra_headers [ns_set new] + + ns_set put $extra_headers MIME-Version [ns_set get $message_data MIME-Version] + ns_set put $extra_headers Content-ID [ns_set get $message_data Content-ID] + ns_set put $extra_headers Content-Type [ns_set get $message_data Content-Type] + set message [ns_set get $message_data body] + + acs_mail_lite::send \ + -to_addr $to_addr \ + -from_addr $from_addr \ + -subject $subject \ + -body $message \ + -extraheaders $extra_headers + + + + + Basic Caching + + Based on a post by Dave Bauer. + + + + Implement your proc as my_proc_not_cached + + + Create a version of your proc called my_proc which wraps the non-cached version in the caching mechanism. In this example, my_proc_not_cached takes one argument, -foo, so the wrapper passes that on. The wrapper also uses the list command, to ensure that the arguments get passed correctly and to prevent commands passed in as arguments from being executed. + ad_proc my_proc {-foo} { + Get a cached version of my_proc. +} { + return [util_memoize [list my_proc_not_cached -foo $foo]] +} + + + In your code, always call my_proc. There will be a seperate cache item for each unique call to my_proc_not_cached so that calls with different arguments are cached seperately. You can flush the cache for each cache key by calling util_memoize_flush my_proc_not_cached args. + + + + The cached material will of course become obsolete over time. There are two ways to handle this. + + + + Timed Expiration: pass in max_age to util_memoize. If the content is older than max_age, it will be re-generated. + + + + Direct Flushing. In any proc which invalidates the cached content, call util_memoize_flush my_proc_not_cached args. + + + + + If you are correctly flushing the cached value, then it will need to be reloaded. You may wish to pre-load it, so that the loading delay does not impact users. If you have a sequence of pages, you could call the cached proc in advance, to increase the chances that it's loaded and current when the user reaches it. Or, you can call (and discard) it immediately after flushing it. + + + + + Scheduled Procedures + Put this proc in a file /packages/myfirstpackage/tcl/scheduled-init.tcl. Files in /tcl with the -init.tcl ending are sourced on server startup. This one executes my_proc every 60 seconds: + ad_schedule_proc 60 myfirstpackage::my_proc + +This executes once a day, at midnight: + ad_schedule_proc \ + -schedule_proc ns_schedule_daily \ + [list 0 0] \ + myfirstpackage::my_proc + + See ad_schedule_proc for more information. + + + + Future Topics @@ -1038,7 +1043,6 @@ How and when to put procedures in a tcl procedure library More on ad_form - data validation, other stuff. (plan to draw from Jon Griffin's doc) - How and when to implement caching partialquery in xql How to use the html/text entry widget to get the "does this look right" confirm page