XOTcl for OpenACS

XOTcl for OpenACS

Advanced Topics on OpenACS

Stefan Sobernig, Franz Wirl

Course 582 / Advanced Course 1 New Media


Table of Contents

  • Part 1: Relationship, Relationship Types, and Roles in OpenACS Object System (acs-rels)
    • Part 2: OpenACS authorisation/ permission infrastructure
    • Part 3: How to re-use categories
    • Part 4: How to re-use general-comments

    acs-rels / Object relationships

    A conceptual scheme

    acs-rels / Object relationships / example

    The example of cross-references between Notes

    acs-rels / Object relationships

    • acs-rels allow to model and materialise (persist) advanced associations betwen OpenACS Objects (::xo::db::Object, acs_object)
    • Associations are refined to support the notion of association roles, i.e. possibly allowing for multiple kinds of relationship between two objects. Association are, hereafter, known as role assignments.
    • Inter-object roles (UML: association roles, XOTcl: slots) groups into a role type (UML: association class), e.g. the role type CrossReference allows to reference multiple ::demo::Notes from another ::demo::Note.
    • Generally speaking, direction/ navigability is important. Conceptually (and in OpenACS) uni- and bi-directional associations/ role assignments are possible, i.e. either one or two roles must be given

    acs-rels / Object relationships / interface

    acs-rels / Object relationships / cross-references for ::demo::Note

    Creating the role type "CrossReference"
    # 1. create the role "references" first
    ::xo::db::sql::acs_rel_type create_role -role references
    
    # 2. create the role type "CrossReferences"
    ::xo::db::sql::acs_rel_type create_type \
        -rel_type CrossReference  \
        -pretty_name "A cross reference for ::demo::Notes"  \
        -pretty_plural "Cross references for ::demo::Notes"\
        -table_name cross_references -id_column cross_reference_id \
        -package_name CrossReference  \
        -object_type_one content_item  \
        -min_n_rels_one 0  \
        -max_n_rels_one "" \
        -object_type_two content_item  \
        -role_two references \
        -min_n_rels_two 0  \
        -max_n_rels_two ""
    
    # 3. drop the role type
    
    ::xo::db::sql::acs_rel_type drop_type -rel_type CrossReference

    acs-rels / Object relationships / cross-references for ::demo::Note

    Create a role assignment (1)
    # 1. provide context resolver
    
    ::demo::Package initialize -ad_doc {
    
      Referencing a note
    
      @author Stefan Sobernig
      @cvs-id $Id$
      
    }
    
    # 2. create two ::demo::Note objects to be associated
    
    set referent [::demo::Note new_persistent_object \
    		  -name my_referent \
    		  -title "The referring note" \
    		  -text "Some text (see references)" \
    		  -number 1 \
    		  -package_id $package_id \
    		  -set parent_id [$package_id folder_id]]
    set referee [::demo::Note new_persistent_object \
    		 -name my_referee \
    		 -title "The referred note" \
    		 -text "Some text (see backlinks)" \
    		 -number 2 \
    		 -package_id $package_id \
    		 -set parent_id [$package_id folder_id]]

    acs-rels / Object relationships / cross-references for ::demo::Note

    Create a role assignment (2)
    # 3. establish the association (role assignment)
    # Beware the subtle difference between establishing
    # a relationship type (role type) between:
    # a) two revision of a content item ("::demo::Note" as object_type restriction in acs_rel_type + revision_id for acs_rel->new())
    # b) two content items ("content_item" as as object_type restriction in acs_rel_type + revision_id for acs_rel->new())
    
    set rel_id [::xo::db::sql::acs_rel new \
        -rel_type CrossReference \
        -object_id_one [$referent item_id] \
        -object_id_two [$referee item_id]]
    
    # 4. delete association (role assignment)
    
    ::xo::db::sql::acs_rel delete -rel_id $rel_id
    
    ad_returnredirect "."

    acs-rels / Object relationships / cross-references for ::demo::Note

    A relationship-aware select statement
    #
    # 1. provide context resolver
    #
    
    ::demo::Package initialize -ad_doc {
    
      Getting cross-references for a note
    
      @author Stefan Sobernig
      @cvs-id $Id$
      
    }
    
    #
    # 2. resolve referent
    #
    
    set referent_id [::xo::db::CrClass lookup -name "my_referent"]
    
    #
    # 3. formulate an instance_select_query on ::demo::Note that
    # is constraint to the referenced notes from a particular one.
    # this needs a nested select statement as refinement. 
    #
    
    if {$referent_id != 0} {
      # found an so-named item
      set sql_statement [::demo::Note instance_select_query \
    			 -folder_id [$package_id folder_id] \
    			 -where_clause "ci.item_id IN ((select object_id_two from acs_rels rels where rels.object_id_one = $referent_id))"] 
    
      #
      # 4. 
      #
      db_foreach select_references $sql_statement {
        # the following variables will be initialised (from the
        # instance_select_query default select header): item_id, name,
        # publish_status, object_type
        
        # ...
      }
    }
    
    ad_return "."

    Permission infrastructure

    • Follows the idea of separating SUBJECT - ROLE - PRIVILEGES (role-based access model), each representing a hierarchy on its own.
      • SUBJECT: "parties" (persons, user, communities)
      • ROLE: approximated by groups (aggregating other groups) and group types
      • PRIVILEGES: predefined ones, e.g. admin = {read create write delete}
    • Two separated steps:
      • "role assignment" (ROLE -> SUBJECT)
      • "privilege assignment" (PRIVILEGE -> ROLE)
    • Role assignment: Assigning users/ groups to groups
      • see /admin/ group and member management
    • Privilege assignment (per ACS Object):
      • see /permissions/ (or per-package-instance /admin/site-map)

    Permission infrastructure / interface (1)

    Interface for authorisation (SUBJECT-PRIVILEGE enforcement)

    Permission infrastructure / interface (2)

    Permission infrastructure / interface examples
    ::demo::Package initialize -ad_doc {
    
      permission checks
    
      @param object_id 
    
    } -parameter {
      {-object_id:integer}
    }
    
    #
    # 1. require authentication
    #
    
    auth::require_login
    
    #
    # We need to verify the current user's privileges
    #
    
    #
    # 1. legacy style
    #
    
    permission::permission_p \
        -party_id  [::xo::cc user_id] \
        -object_id $object_id \
        -privilege delete
    
    #
    # 2. XOTcl Core style
    #
    
    ::xo::cc permission -object_id $object_id -privilege write

    Categories

    1. Install categories package through APM
    2. Development cycle:
      1. Create a new category tree: /categories/cadmin
      2. Map the new category tree to your application package: /categories/cadmin/object-map?object_id=<package_id> (you may find your package_id by looking at the /admin/site-map or have it returned from your ::xo::Package context resolver)
      3. Provide for category selection in your web forms: ::Generic::Form
      4. Provide for category-aware selects

    Categories / form integration

    How to embed the categories widget with ::Generic::Form
    #
    # add the -with_categories switch to
    # the Generic::Form instantiation
    #
    # this will render a multiple-select box
    # and take care of persisting category
    # settings ...
    #
    
    ::Generic::Form create form1 -volatile \
        -data $item \
        -fields {
          {item_id:key}
          {name:text {label Name}}
          {number:text {label Number}}
          {text:richtext(richtext),nospell,optional {label Content} {options {editor xinha}}}
          {description:text(textarea),nospell,optional {label Description} {html {cols 60 rows 2}}}
        } -with_categories true

    Categories / category-constraint selects

    Constrainging selects
    #
    # 1. provide context resolver
    #
    
    ::demo::Package initialize -ad_doc {
    
      Getting categorized notes
    
      @author Stefan Sobernig
      @cvs-id $Id$
      
    } -parameter {
     {-category_id:required}
    }
    
    
    #
    # 2. formulate an instance_select_query on ::demo::Note that
    # is constraint to those assigned a particular category_id.
    #
     set sql_statement [::demo::Note instance_select_query \
    			 -folder_id [$package_id folder_id] \
    			 -from_clause ", category_object_map com"
    			 -where_clause "com.object_id = ci.item_id and
    				        com.category_id = $category_id"] 
    
    
    #
    # 3. 
    #
    db_foreach select_references $sql_statement {
        # the following variables will be initialised (from the
        # instance_select_query default select header): item_id, name,
        # publish_status, object_type
        
        # ...
      }
    }
    
    ad_return "."