Forum OpenACS Q&A: Instructions on how to add categories to your package

Hi all,

below the steps to add categories to a package. Special thanks to Timo, Lars, Dirk, Malte for code and support.
For more info take a look on Documentation on HEAD.

  1. Make Categories optional for your package.
    Goto your package under APM and define the number parameter UseCategoriesP with default 0
  2. Edit the page where you need to extend your ad_form to support categories
    set use_categories_p [parameter::get -parameter "EnableCategoriesP" -default 0]
    set package_id [ad_conn package_id]
    
    Close the ad_form definition after the switch -form and put the rest of your ad_form definition in a if-else clause.
    Note: In the following entry_id is the categorized object_id you want support categories and the categories is enabled on package instance level (package_id). The form name is my_form
    # customize form depending on category capabilities
    if { $use_categories_p == 1 } {
    
        #create link to site-wide categories
        set category_map_url [export_vars -base "[site_node::get_package_url -package_key categories]cadmin/one-object" { { object_id $package_id } }]
    
        #extend the form to support categories
        category::ad_form::add_widgets -form_name new_quest_answ -container_object_id $package_id -categorized_object_id [value_if_exists entry_id]
    
        ad_form -extend -name my_form -select_query_name my_query -on_submit {
            set category_ids [category::ad_form::get_categories -container_object_id $package_id]
        } -new_data {
                #HERE COMES YOU DML CODE
                category::map_object -remove_old -object_id $entry_id $category_ids
                db_dml insert_asc_named_object "insert into acs_named_objects (object_id, object_name, package_id) values ( :entry_id, 'FAQ', :package_id)"
            }
        } -edit_data {
                #HERE COMES YOU DML CODE
            db_dml insert_asc_named_object "update acs_named_objects set object_name = 'FAQ', package_id = :package_id where object_id = :entry_id"
            category::map_object -remove_old -object_id $entry_id $category_ids
        } -after_submit {
            ad_returnredirect "somewhere"
            ad_script_abort
        }
    } else {
    # HERE COMES YOU OLD FORM DEFINITION
    }
    
  3. In the corresponding adp-file enter this
    <if @use_categories_p@>
        <a href=&34;@category_map_url@&34; >Site-Wide Categories
    </if>
    
  4. If you want to allow the user to switch between categories enabled and disabled do this
    • Add the following to your admin tcl page
      set use_categories_p [parameter::get -parameter "EnableCategoriesP"]
      set category_map_url [export_vars -base &34;[site_node::get_package_url -package_key categories]cadmin/one-object&34; { { object_id $package_id } }]
      set return_url [ns_conn url]
      
    • And this in the adp file
      <a href=toggle-cat-mode?<&37;=[export_url_vars return_url]&37;>>Configure categories mode
      <if @use_categories_p@>
         <a href=&34;@category_map_url@&34; >Site-Wide
         Categories
      </if>
      
    • Create the toggle-cat-mode-Page
      ad_page_contract {
      
          This page allows a faq admin to change the categories usage mode.
      
          @author Nima Mazloumi (nima.mazloumi@gmx.de)
          @creation-date  2004-08-11
      
      } {
          {return_url ""}
      }
      
      set title "Configure category mode"
      set context [list $title]
      
      set use_categories_p [parameter::get -parameter "EnableCategoriesP"]
      
      ad_form -name categories_mode -form {
          {enabled_p:text(radio)
              {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"}}
      } -on_submit {
          parameter::set_value  -parameter "EnableCategoriesP" -value $enabled_p
          if {![empty_string_p $return_url]} {
              ns_returnredirect $return_url
          }
      }
      
    • And this in the corresponding adp
      <master>
      <property name="title">@title@</property>
      <property name="context">@context@</property>
      
      <formtemplate id="categories_mode"></formtemplate>
      
  5. Now we will enable category filters for your view page. I assume that this page is called view
    • Add this parameter to the view.tcl,
      ad_page_contract {
      
        YOUR TEXT
      
      } {
      	YOURPARAMS
          {category_id:integer,optional {}}
      }
      
    • Change your sql-query depending on whether categories is enabled or not
      # Use Categories?
      set use_categories_p [parameter::get -parameter "EnableCategoriesP" -default 0]
      
      if { $use_categories_p == 1 && [exists_and_not_null category_id] } {
      
      	# Add to your from clause 'category_object_map com, acs_named_objects nam'
      	# Add to your 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'
          set select_sql_query "YOUR NEW QUERY"
      
      } else {
      
          set select_sql_query "YOUR ORIGINAL QUERY"
      
      }
      
    • Append this at the end of view.tcl
      # Site-Wide Categories
      if { $use_categories_p == 1} {
          set package_url [ad_conn package_url]
          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"
                  return
              }
              # Show Category in context bar
              append context_base_url /cat/$category_id
              lappend context [list $context_base_url $category_name]
              set type "all"
          }
      
          # Cut the URL off the last item in the context bar
          if { [llength $context] > 0 } {
              set context [lreplace $context end end [lindex [lindex $context end] end]]
          }
      
          db_multirow -unclobber -extend { category_name tree_name } categories categories {
              select c.category_id as category_id, c.tree_id
              from   categories c, category_tree_map ctm
              where  ctm.tree_id = c.tree_id
              and    ctm.object_id = :package_id
          } {
              set category_name [category::get_name $category_id]
              set tree_name [category_tree::get_name $tree_id]
          }
      }
      
    • And now change view.adp. Note that you need to adjust the queries to forward those parameters you need to display your items correctly YOURPARAMS. Also change YOURPACKAGE to your your correct package name.
      <if @use_categories_p@>
       <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> </multiple>
      <a href=&34;@package_url@view?@YOURPARAMS@&34;>All Items </if>
    • Finally add a index.vuh file to the root of your package www folder
      set url /[ad_conn extra_url]
      
      if {[regexp {^/+cat/+([^/]+)/*} $url \
                        ignore_whole sw_category_id]} {
                # SWC (Site-wide categories)
                # As the categories package currently doesn't have short names we
                # are using IDs. We don't check whether this is a valid integer here
                # because index.tcl will take care of that.
                rp_form_put sw_category_id $sw_category_id
      }
      rp_internal_redirect "/packages/YOURPACKAGE/www/view"
      
  6. Finally make sure to delete the entries in acs_named_object when your items are deleted.
    db_dml delete_named_object "delete from acs_named_objects where object_id = :entry_id"
    

Greetings

Nima

Collapse
Posted by Jade Rubick on
Nima, could you add this to the documentation for categories? You could even just take the HTML you posted, and put it in a tutorial page or something.
Added this to documentation on HEAD. Hope I didn't make any mistake :)