Index: openacs-4/packages/acs-core-docs/www/tutorial-categories.html =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-core-docs/www/tutorial-categories.html,v diff -u -N -r1.8 -r1.8.4.1 --- openacs-4/packages/acs-core-docs/www/tutorial-categories.html 17 Jul 2006 05:38:32 -0000 1.8 +++ openacs-4/packages/acs-core-docs/www/tutorial-categories.html 3 Feb 2008 12:07:41 -0000 1.8.4.1 @@ -1,12 +1,13 @@ -Categories

Categories

extended by Nima Mazloumi

+ +Categories

Categories

extended by Nima Mazloumi

OpenACS docs are written by the named authors, and may be edited by OpenACS documentation staff.

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 + /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 @@ -28,39 +29,39 @@

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/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/admin/index.tcl + /var/lib/aolserver/$OPENACS_SERVICE_NAME/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 } }]
+		  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/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/admin/index.adp + /var/lib/aolserver/$OPENACS_SERVICE_NAME/packages/myfirstpackage/www/admin/index.adp file:

-   			<a href="@category_map_url@"<#categories.Site_wide_Categories#</a>
-          

The link created by the above code (category_map_url) + <a href="@category_map_url@"<#categories.Site_wide_Categories#</a> +

The link created by the above code (category_map_url) 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 + package_id so that category trees will be associated with the object identified by this - package_id. The categorization + 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. + 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 + 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:

    +          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:

     			    #extend the form to support categories
     			    set package_id [ad_conn package_id]
     			    
    @@ -71,117 +72,117 @@
         			} -new_data {
         				....
     					category::map_object -remove_old -object_id $item_id $category_ids
    -            		db_dml insert_asc_named_object "insert into acs_named_objects (object_id, object_name, package_id) values ( :item_id, :title, :package_id)"
    +            		db_dml insert_asc_named_object "insert into acs_named_objects (object_id, object_name, package_id) values ( :item_id, :title, :package_id)"
     	    		} -edit_data {
                 		....
    -        			db_dml update_asc_named_object "update acs_named_objects set object_name = :title, package_id = :package_id where object_id = :item_id"
    +        			db_dml update_asc_named_object "update acs_named_objects set object_name = :title, package_id = :package_id where object_id = :item_id"
             			category::map_object -remove_old -object_id $item_id $category_ids
         			} -after_submit {
    -        				ad_returnredirect "."
    +        				ad_returnredirect "."
             				ad_script_abort
         			}
    -			

    While the category::ad_form::add_widgets proc is taking +

    While the category::ad_form::add_widgets proc is taking care to extend your form with associated categories you need to ensure that your items are mapped to the corresponding category object yourself. Also since the categories package knows nothing from - your objects you have to keep the acs_named_objects table updated with + your objects you have to keep the acs_named_objects table updated with any changes taking place. We use the items title so that they are listed in the categories browser by title.

    Make sure that you also delete these entries if your item is delete. Add this to your corresponding delete page:

    -			db_dml delete_named_object "delete from acs_named_objects where object_id = :item_id"
    -			

    note-edit.tcl requires a -note_id to determine which record + db_dml delete_named_object "delete from acs_named_objects where object_id = :item_id" +

    note-edit.tcl 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 +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, +note_id and +confirm_p. If confirm_p is present, we delete the record, set redirection back to the index, and abort -script execution.

    The database commands:

    [$OPENACS_SERVICE_NAME@yourserver www]$ emacs note-delete.xql
    <?xml version="1.0"?>
    +script execution.

    The database commands:

    [$OPENACS_SERVICE_NAME@yourserver www]$ emacs note-delete.xql
    <?xml version="1.0"?>
     <queryset>
    -  <fullquery name="do_delete">
    +  <fullquery name="do_delete">
         <querytext>
           select samplenote__delete(:note_id)
         </querytext>
       </fullquery>
    -  <fullquery name="get_name">
    +  <fullquery name="get_name">
         <querytext>
           select samplenote__name(:note_id)
         </querytext>
       </fullquery>
    -</queryset>

    And the adp page:

    [$OPENACS_SERVICE_NAME@yourserver www]$ emacs note-delete.adp
    <master>
    -<property name="title">@title@</property>
    -<property name="context">{@title@}</property>
    +</queryset>

    And the adp page:

    [$OPENACS_SERVICE_NAME@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>
    +<formtemplate id="note-del-confirm"></formtemplate>
     </form>

    The ADP is very simple. The -formtemplate tag outputs the HTML +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.

  • We will now make categories optional on package instance level and also add a configuration page to allow the package admin to enable/disable categories for his package. -

    Go to the APM and create a number parameter with the name "EnableCategoriesP" - and the default value "0".

    Add the following lines to your index.tcl:

    + 		  

    Go to the APM and create a number parameter with the name "EnableCategoriesP" + and the default value "0".

    Add the following lines to your index.tcl:

               set return_url [ns_conn url]
    -          set use_categories_p [parameter::get -parameter "EnableCategoriesP"]
    +          set use_categories_p [parameter::get -parameter "EnableCategoriesP"]
               

    Change your to this:

     			<a href=configure?<%=[export_url_vars return_url]%>>Configure</a>
     			<if @use_categories_p@>
    -   			<a href="@category_map_url@"<#categories.Site_wide_Categories#</a>
    +   			<a href="@category_map_url@"<#categories.Site_wide_Categories#</a>
        			</if>
               

    Now create a configure page

               	ad_page_contract {
         			This page allows an admin to change the categories usage mode.
     			} {
    -    			{return_url ""}
    +    			{return_url ""}
     			}
     
    -			set title "Configure category mode"
    +			set title "Configure category mode"
     			set context [list $title]
    -			set use_categories_p [parameter::get -parameter "EnableCategoriesP"]
    +			set use_categories_p [parameter::get -parameter "EnableCategoriesP"]
     
     			ad_form -name categories_mode -form {
         			{enabled_p:text(radio)
    -        			{label "Enable Categories"}
    +        			{label "Enable Categories"}
             			{options {{Yes 1} {No 0}}}
             			{value $use_categories_p}
         			}
         			{return_url:text(hidden) {value $return_url}}
    -    			{submit:text(submit) {label "Set Mode"}}
    +    			{submit:text(submit) {label "Set Mode"}}
     			} -on_submit {
    -    			parameter::set_value  -parameter "EnableCategoriesP" -value $enabled_p
    +    			parameter::set_value  -parameter "EnableCategoriesP" -value $enabled_p
         			if {![empty_string_p $return_url]} {
             			ns_returnredirect $return_url
         			}
     			}
                

    and add this to its corresponding ADP page

               	<master>
    -			<property name="title">@title@</property>
    -			<property name="context">@context@</property>
    +			<property name="title">@title@</property>
    +			<property name="context">@context@</property>
     
    -			<formtemplate id="categories_mode"></formtemplate>
    +			<formtemplate id="categories_mode"></formtemplate>
     	      

    Reference this page from your admin page

     		#TCL:
     		set return_url [ad_conn url]
     
     		#ADP:
     		<a href=configure?<%=[export_url_vars return_url]%>>Configure</a>
    -		

    Change the note-edit.tcl:

    +		

    Change the note-edit.tcl:

     		# Use Categories?
    -		set use_categories_p [parameter::get -parameter "EnableCategoriesP" -default 0]
    +		set use_categories_p [parameter::get -parameter "EnableCategoriesP" -default 0]
     		if { $use_categories_p == 1 } {
     			# YOUR NEW FORM DEFINITION
     		} else {
         		# YOUR OLD FORM DEFINITION
     		}
     	
  • You can filter your notes using categories. The below example does not support multiple filters and displays a category in a flat format.

    The first step is to - define the optional parameter category_id for - index.tcl:

    + 	  define the optional parameter category_id for 
    + 	  index.tcl:

      	  	ad_page_contract {
       		YOUR TEXT
     		} {
    @@ -190,18 +191,18 @@
     		}
      	  

    Now you have to check whether categories are enabled or not. If this is the case and a category id is passed you need to extend your sql select query to support filtering. One - way would be to extend the mfp::note::get proc to - support two more swiches -where_clause and - -from_clause.

    - 	  	set use_categories_p [parameter::get -parameter "EnableCategoriesP" -default 0]
    + 	  way would be to extend the mfp::note::get proc to 
    + 	  support two more swiches -where_clause and
    + 	  -from_clause.

    + 	  	set use_categories_p [parameter::get -parameter "EnableCategoriesP" -default 0]
     
     		if { $use_categories_p == 1 && [exists_and_not_null category_id] } {
     
    -			set from_clause "category_object_map com, acs_named_objects nam"
    -			set_where_clause "com.object_id = qa.entry_id and
    +			set from_clause "category_object_map com, acs_named_objects nam"
    +			set_where_clause "com.object_id = qa.entry_id and
     								nam.package_id = :package_id and
     								com.object_id = nam.object_id and
    -								com.category_id = :category_id"
    +								com.category_id = :category_id"
     			
     			...
     								
    @@ -223,14 +224,14 @@
         		if { ![empty_string_p $category_id] } {
             		set category_name [category::get_name $category_id]
             		if { [empty_string_p $category_name] } {
    -            		ad_return_exception_page 404 "No such category" "Site-wide \
    -          			Category with ID $category_id doesn't exist"
    +            		ad_return_exception_page 404 "No such category" "Site-wide \
    +          			Category with ID $category_id doesn't exist"
                 		return
             		}
             		# Show Category in context bar
             		append context_base_url /cat/$category_id
             		lappend context [list $context_base_url $category_name]
    -        		set type "all"
    +        		set type "all"
         		}
     
         		# Cut the URL off the last item in the context bar
    @@ -250,20 +251,20 @@
     		}
     		

    and to the corresponding index ADP page:

     		<if @use_categories_p@>
    - 			<multiple name="categories">
    + 			<multiple name="categories">
                		<h2>@categories.tree_name@
    -           		<group column="tree_id">
    -             		<a href="@package_url@cat/@categories.category_id@?@YOURPARAMS@&category_id=@categories.category_id@">@categories.category_name@
    +           		<group column="tree_id">
    +             		<a href="@package_url@cat/@categories.category_id@?@YOURPARAMS@&category_id=@categories.category_id@">@categories.category_name@
                		</group>
              	</multiple>
    -		<a href="@package_url@view?@YOURPARAMS@">All Items</if>
    - 	  

    Finally you need a an index.vuh in your - www folder to rewrite the URLs correctly, the section called “Using .vuh files for pretty urls”:

    +		<a href="@package_url@view?@YOURPARAMS@">All Items</if>
    + 	  

    Finally you need a an index.vuh in your + www folder to rewrite the URLs correctly, Section�, “Using .vuh files for pretty urls”:

      	  	set url /[ad_conn extra_url]
     
     		if {[regexp {^/+cat/+([^/]+)/*} $url \
               ignore_whole category_id]} {
               rp_form_put category_id $category_id
     		}
    -		rp_internal_redirect "/packages/YOURPACKAGE/www/index" 	  
    +		rp_internal_redirect "/packages/YOURPACKAGE/www/index" 	  
      	  

    Now when ever the user select a category only notes that belong to this category are displayed.

  • View comments on this page at openacs.org