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.4.2.3 -r1.4.2.4 --- openacs-4/packages/acs-core-docs/www/xml/developers-guide/tutorial-advanced.xml 16 Jan 2004 14:27:49 -0000 1.4.2.3 +++ openacs-4/packages/acs-core-docs/www/xml/developers-guide/tutorial-advanced.xml 2 Feb 2004 18:07:47 -0000 1.4.2.4 @@ -6,51 +6,21 @@ ]> Advanced Topics - - This section is a work in progress. - by Joel Aufrecht - - - Overview This tutorial covers topics which are not essential to creating a minimal working package. Each section can be used independently of all of the others; all sections assume that you've completed the basic tutorial. - - - How to enforce security so that users can't - change other users records - - How to use the content management tables so that - ... what? - How to change the default stylesheets for Form - Builder HTML forms. - How to make your package searchable with OpenFTS/Oracle - How to make your package send email notifications - How to prepare pagelets for inclusion in other pages - How and when to put procedures in a tcl procedure library - How to add general_comments to your pages - 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 - APM package dependencies - - - Write the Requirements and Design Specs 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 /var/lib/aolserver/openacs-dev/packages/acs-core-docs/xml/docs/xml/package-documentation-template.xml to - yourpackage/www/docs/xml/index.xml. + myfirstpackage/www/docs/xml/index.xml. You then edit that file with emacs to write the requirements and design sections, generate the html, and start coding. Store any supporting files, like page maps or schema @@ -61,11 +31,11 @@ pre-written documentation files for the tutorial app. Log in as service0, create the standard directories, and copy the prepared documentation: - [service0@yourserver service0]$ cd /var/lib/aolserver/service0/packages/samplenote/ -[service0@yourserver samplenote]$ mkdir -p www/doc/xml -[service0@yourserver samplenote]$ cd www/doc/xml -[service0@yourserver xml]$ cp /var/lib/aolserver/service0/packages/acs-core-docs/www/files/samplenote/* . -[service0@yourserver xml]$ + [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 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 @@ -86,7 +56,7 @@ 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@yourserver xml]$ make + [service0 xml]$ make cd .. ; /usr/bin/xsltproc ../../../acs-core-docs/www/xml/openacs.xsl xml/index.xml Writing requirements-introduction.html for sect1(requirements-introduction) Writing requirements-overview.html for sect1(requirements-overview) @@ -102,9 +72,9 @@ Writing admin-guide.html for chapter(admin-guide) Writing bi01.html for bibliography Writing index.html for book -[service0@yourserver xml]$ +[service0 xml]$ Verify that the documentation was generated and reflects - your changes by browsing to http://yoursite:8000/samplenote/doc + your changes by browsing to http://yoursite:8000/myfirstpackage/doc Add the new package to CVS @@ -114,20 +84,20 @@ traverse the directory tree manually and add as you go. (More on CVS) - [service0@yourserver xml]$ cd .. -[service0@yourserver doc]$ cd .. -[service0@yourserver www]$ cd .. -[service0@yourserver samplenote]$ cd .. -[service0@yourserver packages]$ cvs add samplenote/ -Directory /cvsroot/service0/packages/samplenote added to the repository -[service0@yourserver packages]$ cd samplenote/ -[service0@yourserver samplenote]$ cvs add www -Directory /cvsroot/service0/packages/samplenote/www added to the repository -[service0@yourserver samplenote]$ cd www -[service0@yourserver www]$ cvs add doc -Directory /cvsroot/service0/packages/samplenote/www/doc added to the repository -[service0@yourserver www]$ cd doc -[service0@yourserver doc]$ cvs add * + [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 * 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 @@ -150,140 +120,288 @@ 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/samplenote/www/doc/xml added to the repository +Directory /cvsroot/service0/packages/myfirstpackage/www/doc/xml added to the repository cvs add: use 'cvs commit' to add these files permanently -[service0@yourserver doc]$ cd xml -[service0@yourserver xml]$ cvs add Makefile index.xml +[service0 doc]$ cd xml +[service0 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@yourserver xml]$ cd ../../.. -[service0@yourserver samplenote]$ cvs commit -m "new package" +[service0 xml]$ cd ../../.. +[service0 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/samplenote/www/doc/admin-guide.html,v +RCS file: /cvsroot/service0/packages/myfirstpackage/www/doc/admin-guide.html,v done Checking in www/doc/admin-guide.html; -/cvsroot/service0/packages/samplenote/www/doc/admin-guide.html,v <-- admin-guide.html +/cvsroot/service0/packages/myfirstpackage/www/doc/admin-guide.html,v <-- admin-guide.html initial revision: 1.1 done (many lines omitted) -[service0@yourserver samplenote]$ +[service0 myfirstpackage]$ - - Delete with confirmation - We need a way to delete records. We'll create a - recursive confirmation page. - Add this column to the table_def in index.tcl - {delete "" {} {<td><a href="note-delete?note_id=$note_id">Delete</a></td>}} - Create the delete confirmation/execution page. - [service0@yourserver www]$ emacs note-delete.tcl - ad_page_contract { - A page that gets confirmation and then delete notes. + + Adding Comments + You can track comments for any ACS Object. Here we'll track + comments for notes. On the note-edit.tcl/adp pair, which is used to + display individual notes, we want to put a link to add comments at + the bottom of the screen. If there are any comments, we want to + show them. + First, we need to generate a url for adding comments. In note-edit.tcl: + + set comment_add_url "[general_comments_package_url]comment-add?[export_vars { + { object_id $note_id } + { object_name $title } + { return_url "[ad_conn url]?[ad_conn query]"} + }]" + + This calls a global, public tcl function that the + general_comments package registered, to get its url. You then + embed in that url the id of the note and its title, and set the + return_url to the current url so that the user can return after + adding a comment. + We need to create html that shows any existing comments. + We do this with another general_comments function: + set comments_html [general_comments_get_comments + -print_content_p 1 $note_id] + First, we pass in an optional parameter that that says to actually + show the contents of the comments, instead of just the fact that + there are comments. Then you pass the note id, which is also the + acs_object id. + We put our two new variables in the note-edit.adp + page. + <a href="@comment_add_url@">Add a comment</a> + @comments_html@ + + + Admin Pages + + There are at least two flavors of admin user interface: + + + Admins use same pages as all other users, except + that they are offered admin links and buttons where appropriate. + For example, if admins have privilege to bulk-delete items you + could provide checkboxes next to every item seen on a list and the + Delete Selected button on the bottom of the list. + + Dedicated admin pages. If you want admins to have + 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 + directory. + +[service0 www]$ mkdir admin +[service0 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 + administration UI such as Category Management or standard Parameters UI. + Adding the link to Category Management is described in the section on + categories. The listing below adds a link to the Parameters UI of our + package. + +[service0 admin]$ vi index.adp + +<master> +<property name="title">@title;noquote@</property> +<property name="context">@context;noquote@</property> - @author joel@aufrecht.org - @creation-date 2003-02-12 - @cvs-id $Id$ -} { - note_id:integer - confirm_p:optional +<ul class="action-links"> + <li><a href="@parameters_url@" title="Set parameters" class="action_link">Set parameters</a></li> +</ul> + +[service0 admin]$ vi index.tcl + +ad_page_contract {} { +} -properties { + context_bar } -set title "Delete Note" +set package_id [ad_conn package_id] -if {![exists_and_not_null confirm_p]} { - # first pass, not confirmed. Display a form for confirmation - set note_name [db_string get_name { *SQL }] - set title "Delete $note_name" - template::form::create note-del-confirm - template::element::create note-del-confirm note_id -value $note_id -widget hidden - template::element::create note-del-confirm confirm_p -value 1 -widget hidden - template::element::create note-del-confirm submit \ - -label "Confirm deletion of $note_name" \ - -widget submit -} else { - # second pass, confirmed. Call the database to delete the record - db_1row do_delete { *SQL* } - ad_returnredirect "index" - ad_script_abort -} - This page requires a -note_id to determine which record -should be deleted. It also looks for a confirmation variable, which -should initially be absert. If it is absent, we create a form to -allow the user to confirm the deletion. Note that in -entry-edit.tcl we used ad_form to access the Form Template -commands; here, we call them directly because we don't need the extra -features of ad_form. The form calls itself, but -with hidden variables carrying both -note_id and -confirm_p. If confirm_p is present, -we delete the record, set redirection back to the index, and abort -script execution. +set admin_p [ad_require_permission $package_id admin] - The database commands: - [service0@yourserver www]$ emacs note-delete.xql - <?xml version="1.0"?> -<queryset> - <fullquery name="do_delete"> - <querytext> - select samplenote__delete(:note_id) - </querytext> - </fullquery> - <fullquery name="get_name"> - <querytext> - select samplenote__name(:note_id) - </querytext> - </fullquery> -</queryset> - And the adp page: - [service0@yourserver www]$ emacs note-delete.adp - <master> -<property name="title">@title@</property> -<property name="context">{@title@}</property> -<h2>@title@</h2> -<formtemplate id="note-del-confirm"></formtemplate> -</form> - The ADP is very simple. The -formtemplate tag outputs the HTML -form generated by the ad_form command with the matching name. Test it by adding the new files in the APM and then deleting a few samplenotes. - - - General_comments - You can track comments for any ACS Object. Here we'll track - comments for notes. On the notes.tcl/adp pair, which is used to - display individual notes, we want to put a link to add comments at - the bottom of the screen. If there are any comments, we want to - show them. - First, we need to generate a url for adding comments. In notes.tcl: - -set comment_add_url "[general_comments_package_url]comment-add?[export_vars { - { object_id $note_id } - { object_name $title } - { return_url "[ad_conn url]?[ad_conn query]"} -}]" +set context [list] + +set title "Administration" + +set parameters_url [export_vars -base "/shared/parameters" { + package_id { return_url [ad_return_url] } +}] + - This calls a global, public tcl function that the - general_comments package registered, to get its url. You then - embed in that url the id of the note and its title, and set the - return_url to the current url so that the user can return after - adding a comment. - We need to create html that shows any existing comments. - We do this with another general_comments function: - set comments_html [general_comments_get_comments - -print_content_p 1 $note_id] - First, we pass in an optional parameter that that says to actually - show the contents of the comments, instead of just the fact that - there are comments. Then you pass the note id, which is also the - acs_object id. - We put our two new variables in the notes.adp - page. - <a href="@comment_add_url@">Add a comment</a> -@comments_html@ + + +Now that you have the first admin page it would be nice to have a link to it +somewhere in the system so that admins don't have to type in the +/admin every time they need to reach it. You +could put a static link to the toplevel +index.adp but that might be distracting for +people who are not admins. Besides, some people consider it impolite to first +offer a link and then display a nasty "You don't have permission to access this +page" message. + + +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: + + + +set package_id [ad_conn package_id] + +set admin_p [permission::permission_p -object_id $package_id \ + -privilege admin -party_id [ad_conn untrusted_user_id]] + +if { $admin_p } { + set admin_url "admin" + set admin_title Administration +} + + +In +/var/lib/aolserver/service0/packages/myfirstpackage/www/admin/index.adp put: + + +<if @admin_p@ ne nil> + <a href="@admin_url@">@admin_title@</a> +</if> + + + + + + + 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 + your application. 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 "meat" of the + note-edit.tcl page: + +ad_form -name note -form { + {item_id:key} + {title:text {label Title}} +} + +set package_id [ad_conn package_id] + +set category_trees [category_tree::get_mapped_trees $package_id] + +foreach tree $category_trees { + foreach { tree_id name subtree_id } $tree {} + ad_form -extend -name note -form \ + [list [list category_id_${tree_id}:integer(category),optional \ + {label $name} \ + {html {single single}} \ + {category_tree_id $tree_id} \ + {category_subtree_id $subtree_id} \ + {category_object_id {[value_if_exists entry_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] +} -new_data { + mfp::note::add \ + -title $title +} -after_submit { + ad_returnredirect "." + ad_script_abort +} + + + + + Prepare the package for distribution. Browse to the package manager. Click on @@ -437,4 +555,25 @@ look at the forums package. + Future Topics: + + How to enforce security so that users can't + change other users records + + How to use the content management tables so that + ... what? + How to change the default stylesheets for Form + Builder HTML forms. + How to make your package searchable with OpenFTS/Oracle + How to prepare pagelets for inclusion in other pages + 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 + APM package dependencies + + See also the OpenACS Programming FAQ