Class ::xo::db::CrClass

::xo::db::CrClass[i] create ... \
           [ -edit_form edit_form ] \
           [ -folder_id (default "-100") ] \
           [ -form form ] \
           [ -mime_type (default "text/plain") ] \
           [ -non_cached_instance_var_patterns (default "__*") ] \
           [ -storage_type (default "text") ] \
           [ -supertype (default "content_revision") ]

Defined in

Class Relations

  • class: ::xotcl::Class[i]
  • superclass: ::xo::db::Class[i]
  • instmixin: ::xo::db::CrCache[i]
  • mixin: ::xo::db::CrCache::Class[i]
::xotcl::Class create ::xo::db::CrClass \
     -superclass ::xo::db::Class \
     -instmixin ::xo::db::CrCache \
     -mixin ::xo::db::CrCache::Class

Methods (to be applied on the object)

  • delete (scripted, public)

     xo::db::CrClass[i] delete [ -item_id item_id ]

    Delete a CrItem in the database

    Switches:
    -item_id (optional)

    Testcases:
    xotcl_core_tutorial_4
    set object_type [:get_object_type -item_id $item_id]
    $object_type delete -item_id $item_id
  • ensure_item_ids_instantiated (scripted, public)

     xo::db::CrClass[i] ensure_item_ids_instantiated [ -initialize on|off ] \
        -item_ids item_ids 

    Make sure, the objects all of the provided items_ids are instantiated (i.e. the same-named objects do exist as executable commands in the current thread).

    Switches:
    -initialize (optional, boolean, defaults to "true")
    -item_ids (required)

    Testcases:
    link_tests
    foreach item_id $item_ids {
      #if {![::nsf::is object ::$item_id]} { ns_log notice "===== we have to fetch ::$item_id"}
      :get_instance_from_db -item_id $item_id -initialize $initialize
    }
  • get_child_item_ids (scripted, public)

     xo::db::CrClass[i] get_child_item_ids -item_id item_id 

    Return a list of content items having the provided item_id as direct or indirect parent. The method returns recursively all item_ids.

    Switches:
    -item_id (required)
    Returns:
    list of item_ids

    Testcases:
    xowiki_test_cases
    #
    # The following construct (commented out) is fully PostgreSQL and
    # Oracle compliant.  However, all newer Oracle installations
    # should as well support the recursive query below as well, which
    # requires less DB interactions.
    #
    # set items [list]
    # foreach item_id [::xo::dc list -prepare integer get_child_items  #                      "select item_id from cr_items where parent_id = :item_id"] {
    #   lappend items $item_id {*}[my [self proc] -item_id $item_id]
    # }
    # return $items
    
    return [::xo::dc list -prepare integer get_child_items {
      WITH RECURSIVE child_items AS (
           select item_id from cr_items
           where parent_id = :item_id
      UNION ALL
        select i.item_id from cr_items i, child_items
        where i.parent_id = child_items.item_id
      )
      select * from child_items
    }]
  • get_instance_from_db (scripted, public)

     xo::db::CrClass[i] get_instance_from_db [ -item_id item_id ] \
        [ -revision_id revision_id ] [ -initialize on|off ]

    Instantiate the live revision or the specified revision of an CrItem. The XOTcl object is destroyed automatically on cleanup (end of a connection request).

    Switches:
    -item_id (optional, integer, defaults to "0")
    -revision_id (optional, integer, defaults to "0")
    -initialize (optional, boolean, defaults to "true")
    Returns:
    fully qualified object containing the attributes of the CrItem

    Testcases:
    test_cr_items, create_workflow_with_instance, xowiki_test_cases, link_tests, slot_interactions, path_resolve
    set object ::[expr {$revision_id ? $revision_id : $item_id}]
    if {$object eq "::0"} {
      set msg "get_instance_from_db must be called with either item_id or revision_id different from 0"
      ad_log error $msg
      error $msg
    }
    if {![::nsf::is object $object]} {
      set object_type [:get_object_type -item_id $item_id -revision_id $revision_id]
      set class [::xo::db::Class object_type_to_class $object_type]
      set object [$class get_instance_from_db -item_id $item_id -revision_id $revision_id -initialize $initialize]
    }
    return $object
  • get_name (scripted, public)

     xo::db::CrClass[i] get_name -item_id item_id 

    Get the name of a content item either from an already instantiated object or from the database without instantiating it. If item_id is not a valid item_id, we throw an error.

    Switches:
    -item_id (required)
    Returns:
    parent_id

    Testcases:
    xowiki_test_cases
    # TODO: the following line is deactivated, until we get rid of the "folder object" in xowiki
    #if {[nsf::is object ::$item_id]} {return [::$item_id parent_id]}
    ::xo::dc 1row -prepare integer get_name "select name from cr_items where item_id = :item_id"
    return $name
  • get_object_type (scripted, public)

     xo::db::CrClass[i] get_object_type -item_id item_id  \
        [ -revision_id revision_id ]

    Return the object type for an item_id or revision_id.

    Switches:
    -item_id (required, integer)
    -revision_id (optional, integer, defaults to "0")
    Returns:
    object_type typically an XOTcl class

    Testcases:
    test_xo_db_object, test_cr_items
    #
    # Use a request-spanning cache. When the object_type would change,
    # we require xo::broadcast or server restart.
    #
    set key ::xo::object_type($item_id,$revision_id)
    if {[info exists $key]} {
      return [set $key]
    }
    set entry_key [expr {$item_id ? $item_id : $revision_id}]
    set $key [xo::xotcl_object_type_cache eval -partition_key $entry_key $entry_key {
      if {$item_id} {
        ::xo::dc 1row -prepare integer get_class_from_item_id  "select content_type as object_type from cr_items where item_id=:item_id"
      } else {
        ::xo::dc 1row -prepare integer get_class_from_revision_id  "select object_type from acs_objects where object_id=:revision_id"
      }
      return $object_type
    }]
  • get_parent_id (scripted, public)

     xo::db::CrClass[i] get_parent_id -item_id item_id 

    Get the parent_id of a content item either from an already instantiated object or from the database without instantiating it. If item_id is not a valid item_id, we throw an error.

    Switches:
    -item_id (required)
    Returns:
    parent_id

    Testcases:
    xowiki_test_cases
    # TODO: the following line is deactivated, until we get rid of the "folder object" in xowiki
    #if {[nsf::is object ::$item_id]} {return [::$item_id parent_id]}
    ::xo::dc 1row -prepare integer get_parent "select parent_id from cr_items where item_id = :item_id"
    return $parent_id
  • id_belongs_to_package (scripted, public)

     xo::db::CrClass[i] id_belongs_to_package [ -item_id item_id ] \
        [ -revision_id revision_id ] -package_id package_id 

    Check if the provided item_id or revision_id belongs to the provided package.

    Switches:
    -item_id (optional, integer, defaults to "0")
    -revision_id (optional, integer, defaults to "0")
    -package_id (required, integer)
    Returns:
    boolean success

    Testcases:
    create_form_with_form_instance, check_page_template_constraint
    set id [expr {$revision_id ? $revision_id : $item_id}]
    if {$id eq 0} {
      return 0
    }
    set what [expr {$item_id != 0 ? "item_id" : "revision_id"}]
    return [::xo::dc 0or1row -prepare integer,integer check_package [subst {
      select 1 from cr_items, acs_objects
      where $what = :$what and object_id = :$what
      and package_id = :package_id
      fetch first 1 rows only
    }]]
  • lookup (scripted, public)

     xo::db::CrClass[i] lookup -name name  [ -parent_id parent_id ] \
        [ -content_type content_type ]

    Check, whether a content item with the given name exists. When content_type is provided (e.g. -content_type "::%") then a like operation is applied on the value.

    Switches:
    -name (required)
    -parent_id (optional, defaults to "-100")
    -content_type (optional)
    Returns:
    item_id If the item exists, return its item_id, otherwise 0.

    Testcases:
    xotcl_core_tutorial_4, create_folder_with_page, xowiki_test_cases, path_resolve
    if {[info exists content_type]} {
      set result [::xo::dc get_value lookup_by_name_and_ct {
        select item_id from cr_items
        where name = :name and parent_id = :parent_id
        and content_type like :content_type
      } 0]
    } else {
      set result [::xo::dc get_value lookup_by_name {
        select item_id from cr_items
        where name = :name and parent_id = :parent_id
      } 0]
    }
    return $result

Methods (to be applied on instances)

  • create_object_type (scripted, public)

     <instance of xo::db::CrClass[i]> create_object_type

    Create an oacs object_type and a table for keeping the additional attributes.

    Testcases:
    xotcl_core_tutorial_4
    :check_table_atts
    
    set :supertype [:info superclass]
    switch -- ${:supertype} {
      ::xotcl::Object -
      ::xo::db::CrItem {set :supertype content_revision}
    }
    if {![info exists :pretty_plural]} {set :pretty_plural ${:pretty_name}}
    
    ::xo::dc transaction {
      ::acs::dc call content_type create_type  -content_type  ${:object_type}  -supertype     ${:supertype}  -pretty_name   ${:pretty_name}  -pretty_plural ${:pretty_plural}  -table_name    ${:table_name}  -id_column     ${:id_column}  -name_method   ${:name_method}
    
      :folder_type register
    }
  • delete (scripted, public)

     <instance of xo::db::CrClass[i]> delete -item_id item_id 

    Delete a content item from the content repository.

    Switches:
    -item_id (required)
    id of the item to be deleted

    Testcases:
    xotcl_core_tutorial_4, xowiki_test_cases
    ::acs::dc call content_item del -item_id $item_id
  • drop_object_type (scripted, public)

     <instance of xo::db::CrClass[i]> drop_object_type

    Delete the object type and remove the table for the attributes. This method should be called when all instances are deleted. It undoes everying what create_object_type has produced.

    Testcases:
    xotcl_core_tutorial_4
    set object_type ${:object_type}
    ::xo::dc transaction {
      :folder_type unregister
      ::acs::dc call content_type drop_type  -content_type ${:object_type}  -drop_children_p t  -drop_table_p t
    }
  • fetch_object (scripted, public)

     <instance of xo::db::CrClass[i]> fetch_object -item_id item_id  \
        [ -revision_id revision_id ] -object object  \
        [ -initialize on|off ]

    Load a content item into the specified object. If revision_id is provided, the specified revision is returned, otherwise the live revision of the item_id. If the object does not exist, we create it.

    Switches:
    -item_id (required)
    -revision_id (optional, defaults to "0")
    -object (required)
    -initialize (optional, boolean, defaults to "true")
    Returns:
    cr item object

    Testcases:
    xotcl_core_tutorial_4, xowiki_test_cases
    # :log "-- generic fetch_object [self args]"
    if {![nsf::is object $object]} {
      # if the object does not yet exist, we have to create it
      :create $object
    }
    set raw_atts [::xo::db::CrClass set common_query_atts]
    #:log "-- raw_atts = '$raw_atts'"
    
    set atts [list]
    foreach v $raw_atts {
      switch -glob -- $v {
        publish_status {set fq i.$v}
        storage_type   {set fq i.$v}
        creation_date  {set fq o.$v}
        creation_user  {set fq o.$v}
        package_id     {set fq o.$v}
        default        {set fq n.$v}
      }
      lappend atts $fq
    }
    
    foreach {slot_name slot} [array get :db_slot] {
      switch -glob -- $slot {
        ::xo::db::CrItem::slot::text {
          #
          # We need the rule, since insert the handling of the sql
          # attribute "text" is somewhat magic. On insert, one can use
          # the automatic view with column_name "text, on queries, one
          # has to use "data". Therefore, we cannot use simply
          # -column_name for the slot.
          #
          lappend atts "n.data AS text"
        }
    
        ::xowiki::Page::slot::text {
          #
          # This is just a hotfix for now.
          #
          #ns_log notice [$slot serialize]
          lappend atts "n.data as text"
        }
    
        ::xo::db::CrItem::slot::name {
          lappend atts i.[$slot column_name]
        }
        ::xo::db::Object::slot::context_id {
          #
          # If we are fetching by revision_id, skip the context_id,
          # since on object-save-operations, we want to keep the
          # context_id of the item, and not the context_id from the
          # revision.
          #
          if {$revision_id == 0} {
            #
            # Fetch by item_id.
            #
            lappend atts o.[$slot column_name]
          }
        }
        ::xo::db::Object::slot::* {
          lappend atts o.[$slot column_name]
        }
        default {
          lappend atts n.[$slot column_name]
        }
      }
    }
    if {$revision_id} {
      $object set revision_id $revision_id
      set sql [subst {
        select [join $atts ,], i.parent_id
          from ${:table_name}i n, cr_items i, acs_objects o
         where n.revision_id = :revision_id
           and i.item_id = n.item_id
           and o.object_id = n.revision_id
      }]
      set selection [lindex [::xo::dc sets  -prepare integer  fetch_object_from_revision_id $sql] 0]
      $object mset [ns_set array $selection]
    } else {
      #
      # We fetch the creation_user and the modifying_user by returning
      # the creation_user of the automatic view as modifying_user. In
      # case of troubles, comment next line out.
      #
      lappend atts "n.creation_user as modifying_user"
    
      $object set item_id $item_id
    
      $object db_1row [:qn fetch_from_view_item_id] " select [join $atts ,], i.parent_id  from   ${:table_name}i n, cr_items i, acs_objects o  where  i.item_id = :item_id  and    n.${:id_column} = coalesce(i.live_revision, i.latest_revision)  and    o.object_id = i.item_id"
    }
    #
    # The method "db_1row" treats all newly created variables as
    # instance variables, so we can see vars like "__db_sql",
    # "__db_lst" that we do not want to keep.
    #
    foreach v [$object info vars __db_*] {
      $object unset $v
    }
    
    #
    # Deactivate compatibility with versions before OpenACS 5.2
    # (2005), since this is a busy code, but leave it here for easy
    # reactivating in legacy applications.
    #
    #if {[apm_version_names_compare [ad_acs_version] 5.2] <= -1} {
    #  set parent_id [$object set parent_id]
    #  $object set package_id [::xo::dc get_value get_pid {
    #    select package_id from cr_folders where folder_id = :parent_id
    #  }
    #}
    
    # :log "--AFTER FETCH\n[$object serialize]"
    if {$initialize} {$object initialize_loaded_object}
    return $object
  • folder_type (scripted, public)

     <instance of xo::db::CrClass[i]> folder_type \
        [ -include_subtypes include_subtypes ] [ -folder_id folder_id ] \
        operation

    register the current object type for folder_id. If folder_id is not specified, use the instvar of the class instead.

    Switches:
    -include_subtypes (optional, defaults to "t")
    Boolean value (t/f) to flag whether the operation should be applied on subtypes as well
    -folder_id (optional)
    Parameters:
    operation (required)

    Testcases:
    xotcl_core_tutorial_4
    if {$operation ne "register" && $operation ne "unregister"} {
      error "[self] operation for folder_type must be 'register' or 'unregister'"
    }
    if {![info exists folder_id]} {
      set folder_id ${:folder_id}
    }
    ::acs::dc call content_folder ${operation}_content_type  -folder_id $folder_id  -content_type ${:object_type}  -include_subtypes $include_subtypes
  • folder_type_unregister_all (scripted, public)

     <instance of xo::db::CrClass[i]> folder_type_unregister_all \
        [ -include_subtypes include_subtypes ]

    Unregister the object type from all folders on the system

    Switches:
    -include_subtypes (optional, defaults to "t")
    Boolean value (t/f) to flag whether the operation should be applied on subtypes as well

    Testcases:
    No testcase defined.
    set object_type ${:object_type}
    xo::dc foreach all_folders {
      select folder_id from cr_folder_type_map
      where content_type = :object_type
    } {
      ::acs::dc call content_folder unregister_content_type  -folder_id $folder_id  -content_type $object_type  -include_subtypes $include_subtypes
    }
  • get_instance_from_db (scripted, public)

     <instance of xo::db::CrClass[i]> get_instance_from_db \
        [ -item_id item_id ] [ -revision_id revision_id ] \
        [ -initialize on|off ]

    Retrieve either the live revision or a specified revision of a content item with all attributes into a newly created object. The retrieved attributes are stored in the instance variables in class representing the object_type. The XOTcl object is destroyed automatically on cleanup (end of a connection request)

    Switches:
    -item_id (optional, defaults to "0")
    id of the item to be retrieved.
    -revision_id (optional, defaults to "0")
    revision-id of the item to be retrieved.
    -initialize (optional, boolean, defaults to "true")
    Returns:
    fully qualified object

    Testcases:
    xotcl_core_tutorial_4, test_cr_items, create_folder_with_page, xowiki_test_cases, create_form_with_form_instance
    set object ::[expr {$revision_id ? $revision_id : $item_id}]
    if {![nsf::is object $object]} {
      :fetch_object -object $object  -item_id $item_id -revision_id $revision_id  -initialize $initialize
      $object destroy_on_cleanup
    }
    return $object
  • get_instances_from_db (scripted, public)

     <instance of xo::db::CrClass[i]> get_instances_from_db \
        [ -select_attributes select_attributes ] \
        [ -from_clause from_clause ] [ -where_clause where_clause ] \
        [ -orderby orderby ] [ -with_subtypes on|off ] \
        [ -folder_id folder_id ] [ -page_size page_size ] \
        [ -page_number page_number ] [ -base_table base_table ] \
        [ -initialize initialize ]

    Returns a set (ordered composite) of the answer tuples of an 'instance_select_query' with the same attributes. The tuples are instances of the class, on which the method was called.

    Switches:
    -select_attributes (optional)
    -from_clause (optional)
    -where_clause (optional)
    -orderby (optional)
    -with_subtypes (optional, boolean, defaults to "true")
    -folder_id (optional)
    -page_size (optional, defaults to "20")
    -page_number (optional)
    -base_table (optional, defaults to "cr_revisions")
    -initialize (optional, defaults to "true")

    Testcases:
    xotcl_core_tutorial_4, create_form_with_form_instance
    if {![info exists folder_id]} {
      set folder_id ${:folder_id}
    }
    set s [:instantiate_objects -sql  [:instance_select_query  -select_attributes $select_attributes  -from_clause $from_clause  -where_clause $where_clause  -orderby $orderby  -with_subtypes $with_subtypes  -folder_id $folder_id  -page_size $page_size  -page_number $page_number  -base_table $base_table  ]  -initialize $initialize]
    return $s
  • instance_select_query (scripted, public)

     <instance of xo::db::CrClass[i]> instance_select_query \
        [ -select_attributes select_attributes ] [ -orderby orderby ] \
        [ -where_clause where_clause ] [ -from_clause from_clause ] \
        [ -with_subtypes on|off ] [ -with_children on|off ] \
        [ -publish_status publish_status ] [ -count on|off ] \
        [ -folder_id folder_id ] [ -parent_id parent_id ] \
        [ -page_size page_size ] [ -page_number page_number ] \
        [ -base_table base_table ]

    returns the SQL-query to select the CrItems of the specified object_type

    Switches:
    -select_attributes (optional)
    attributes for the SQL query to be retrieved, in addition to item_id, name, publish_status, object_type, and package_id which are always returned
    -orderby (optional)
    for ordering the solution set
    -where_clause (optional)
    clause for restricting the answer set
    -from_clause (optional)
    -with_subtypes (optional, boolean, defaults to "true")
    return subtypes as well
    -with_children (optional, boolean, defaults to "false")
    return immediate child objects of all objects as well
    -publish_status (optional)
    one of 'live', 'ready', or 'production'
    -count (optional, boolean, defaults to "false")
    return the query for counting the solutions
    -folder_id (optional)
    parent_id
    -parent_id (optional)
    -page_size (optional, defaults to "20")
    -page_number (optional)
    -base_table (optional, defaults to "cr_revisions")
    typically automatic view, must contain title and revision_id
    Returns:
    SQL query

    Testcases:
    xowiki_test_cases
    if {![info exists folder_id]} {set folder_id ${:folder_id}}
    if {![info exists parent_id]} {set parent_id $folder_id}
    
    if {$base_table eq "cr_revisions"} {
      set attributes [list ci.item_id ci.name ci.publish_status acs_objects.object_type acs_objects.package_id]
    } else {
      set attributes [list bt.item_id ci.name ci.publish_status bt.object_type "bt.object_package_id as package_id"]
    }
    foreach a $select_attributes {
      if {$a eq "title"} {set a bt.title}
      lappend attributes $a
    }
    set type_selection_clause [:type_selection_clause -base_table $base_table -with_subtypes $with_subtypes]
    # :log "type_selection_clause -with_subtypes $with_subtypes returns $type_selection_clause"
    if {$count} {
      set attribute_selection "count(*)"
      set orderby ""      ;# no need to order when we count
      set page_number  ""      ;# no pagination when count is used
    } else {
      set attribute_selection [join $attributes ,]
    }
    
    set cond [list]
    if {$type_selection_clause ne ""} {lappend cond $type_selection_clause}
    if {$where_clause ne ""}          {lappend cond $where_clause}
    if {[info exists publish_status]} {lappend cond "ci.publish_status = :publish_status"}
    if {$base_table eq "cr_revisions"} {
      lappend cond "acs_objects.object_id = bt.revision_id"
      set acs_objects_table "acs_objects, "
    } else {
      lappend cond "ci.item_id = bt.item_id"
      set acs_objects_table ""
    }
    lappend cond "coalesce(ci.live_revision,ci.latest_revision) = bt.revision_id"
    if {$parent_id ne ""} {
      if {$with_children} {
        append from_clause ", (select $parent_id as item_id from dual union  select item_id from cr_items where parent_id = $parent_id) children"
        lappend cond "ci.parent_id = children.item_id"
      } else {
        lappend cond "ci.parent_id = $parent_id"
      }
    }
    
    if {$page_number ne ""} {
      set limit $page_size
      set offset [expr {$page_size*($page_number-1)}]
    } else {
      set limit ""
      set offset ""
    }
    
    if {!$count} {
      #
      # In case the query is not explicitly referring to a context_id,
      # return the context_id of the item. The problem are queries
      # using "*" in the attribute list, which should be deprecated.
      # Before that we should walk through the common call patterns of
      # this function to check, if this is feasible.
      #
      # This local hack was necessary to deal with a recent fix that
      # honors now correctly changes in the context_id. Before this
      # change, e.g. "get_all_children" was returning due to the
      # nature of the call the context_id of the revision (not of the
      # item), although it was returning items. A following bug-fix
      # actually triggered this change.
      # https://cvs.openacs.org/changelog/OpenACS?cs=oacs-5-10%3Agustafn%3A20210308161117
      #
      # TODO: remove me, when not necessary anymore.
      #
      if {[lsearch -glob $attributes *context_id*] == -1} {
        append attribute_selection {,(select context_id from acs_objects where object_id = ci.item_id)}
      }
    }
    
    set sql [::xo::dc select  -vars $attribute_selection  -from "$acs_objects_table cr_items ci, $base_table bt $from_clause"  -where [join $cond " and "]  -orderby $orderby  -limit $limit -offset $offset]
    #:log "--sql=$sql"
    return $sql
  • new_persistent_object (scripted, public)

     <instance of xo::db::CrClass[i]> new_persistent_object \
        [ -package_id package_id ] [ -creation_user creation_user ] \
        [ -creation_ip creation_ip ] args [ args... ]

    Create a new content item of the actual class, configure it with the given arguments and insert it into the database. The XOTcl object is destroyed automatically on cleanup (end of a connection request).

    Switches:
    -package_id (optional)
    -creation_user (optional)
    -creation_ip (optional)
    Parameters:
    args (required)
    Returns:
    fully qualified object

    Testcases:
    xotcl_core_tutorial_4
    :get_context package_id creation_user creation_ip
    # :log "ID [self] create $args"
    ad_try {
      :create ::0 {*}$args
    } on error {errorMsg} {
      ad_log error "CrClass create raises: $errorMsg"
    }
    # :log "ID [::0 serialize]"
    set item_id [::0 save_new  -package_id $package_id  -creation_user $creation_user  -creation_ip $creation_ip]
    ::0 move ::$item_id
    ::$item_id destroy_on_cleanup
    return ::$item_id