Class ::xo::PackageMgr

::xo::PackageMgr[i] create ... \
           [ -default_package_parameter_page_info (default "") ] \
           [ -default_package_parameters (default "") ] \
           [ -package_key package_key ] \
           [ -site_wide_package_parameter_page_info (default "") ] \
           [ -site_wide_package_parameters (default "") ] \
           [ -site_wide_pages (default "") ]

Class Relations

  • class: ::xotcl::Class[i]
  • superclass: ::xo::db::Class[i]
::xotcl::Class create ::xo::PackageMgr \
     -superclass ::xo::db::Class

Methods (to be applied on the object)

Methods (to be applied on instances)

  • first_instance (scripted, public)

     <instance of xo::PackageMgr[i]> first_instance \
        [ -privilege privilege ] [ -party_id party_id ]

    Returns the first mounted instance of this Package. When a privilege and a party are specified, will return the first instance where the party has such privilege.

    Switches:
    -privilege
    (optional)
    -party_id
    (optional)
    the party we are checking the privilege for
    Returns:
    integer package_id, empty string when none is found

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_create_form_with_form_instance create_form_with_form_instance (test xowiki) xo::PackageMgr instproc first_instance xo::PackageMgr instproc first_instance test_create_form_with_form_instance->xo::PackageMgr instproc first_instance test_xowiki_test_cases xowiki_test_cases (test xowiki) test_xowiki_test_cases->xo::PackageMgr instproc first_instance db_driverkey db_driverkey (public) xo::PackageMgr instproc first_instance->db_driverkey

    Testcases:
    xowiki_test_cases, create_form_with_form_instance
    set package_key ${:package_key}
    if {![info exists privilege]} {
      return [::xo::dc get_value -prepare text get_first_package_id {
        select min(package_id)
          from apm_packages, site_nodes s
         where package_key = :package_key
           and s.object_id = package_id
      }]
    } elseif {[db_driverkey ""] eq "postgresql"} {
      # On Postgres we can use a recursive database function to check
      # for permissions on many objects more efficiently.
      set sql {
        select min(orig_object_id)
          from acs_permission.permission_p_recursive_array(array(
                select package_id
                  from apm_packages, site_nodes s
                 where package_key = :package_key
                   and s.object_id = package_id
                ), :party_id, :privilege)
      }
    } else {
      set sql {
        select min(package_id)
        from apm_packages, site_nodes s
       where package_key = :package_key
         and s.object_id = package_id
         and acs_permission.permission_p(package_id, :party_id, :privilege) = 't'
      }
    }
    
    return [::xo::dc get_value -prepare {text integer text} get_first_package_id_with_privilege $sql]
  • import_prototype_page (scripted, public)

     <instance of xo::PackageMgr[i]> import_prototype_page \
        [ -package_key package_key ] -name name  -parent_id parent_id  \
        -package_id package_id  [ -lang lang ] [ -add_revision on|off ]

    Import a named page from the prototypes folder of the package, i.e. under www/prototypes/*.page of the package.

    Switches:
    -package_key
    (optional)
    when provided, the package_key used to locate the page. When not provided, use the package_key of the class, on which this function is called.
    -name
    (required)
    name of the page to be loaded (not including the language prefix)
    -parent_id
    (required)
    place to where the page should be loaded
    -package_id
    (required)
    package instance to which the page should be loaded
    -lang
    (defaults to "en") (optional)
    -add_revision
    (boolean) (defaults to "true") (optional)
    When the page to be loaded exists already, add a new revision. When the page exists already, and the flag is not set, no change happens.

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_xowiki_test_cases xowiki_test_cases (test xowiki) xo::PackageMgr instproc import_prototype_page xo::PackageMgr instproc import_prototype_page test_xowiki_test_cases->xo::PackageMgr instproc import_prototype_page ad_file ad_file (public) xo::PackageMgr instproc import_prototype_page->ad_file

    Testcases:
    xowiki_test_cases
    if {![info exists package_key] && [info exists :package_key]} {
      set package_key ${:package_key}
    }
    set page ""
    set fn [:prototype_page_file_name -name $name -package_key $package_key]
    #:log "--W check $fn"
    if {![ad_file readable $fn]} {
      ns_log notice "no such prototype page $fn"
      return ""
    }
    #
    # We have the file of the prototype page. We try to create
    # either a new item or a revision from definition in the file
    # system.
    #
    if {[regexp {^(..):(.*)$} $name _ lang local_name]} {
      set fullName $name
    } else {
      set fullName en:$name
    }
    :log "--sourcing page definition $fn, using name '$fullName'"
    set page [source $fn]
    $page configure  -name $fullName  -parent_id $parent_id  -package_id $package_id
    #
    # xowiki::File has a different interface for build-name to
    # derive the "name" from a file-name. This is not important for
    # prototype pages, so we skip it
    #
    if {![$page istype ::xowiki::File]} {
      set nls_language [:get_nls_language_from_lang $lang]
      $page name [$page build_name -nls_language $nls_language]
      #:log "--altering name of page $page to '[$page name]'"
      set fullName [$page name]
    }
    if {![$page exists title]} {
      $page set title $object
    }
    $page destroy_on_cleanup
    $page set_content [string trim [$page text] " \n"]
    $page initialize_loaded_object
    
    xo::Package require $package_id
    set p [::$package_id get_page_from_name  -name $fullName  -assume_folder [$page is_folder_page]  -parent_id $parent_id]
    #:log "--get_page_from_name '$fullName' -parent_id $parent_id --> '$p'"
    if {$p eq ""} {
      #
      # We have to create the page new. The page is completed with
      # missing vars on save_new.
      #
      #:log "--save_new of $page class [$page info class]"
      $page save_new
    } else {
      #:log "--save revision $add_revision"
      if {$add_revision} {
        #
        # An old page exists already, create a revision.  Update the
        # existing page with all scalar variables from the prototype
        # page (which does not have always all instance variables set)
        #
        foreach v [$page info vars] {
          if {[$page array exists $v]} continue ;# don't copy arrays
          $p set $v [$page set $v]
        }
        #:log "--save of $p [$p name] class [$p info class]"
        $p save
      }
      set page $p
    }
    if {$page ne ""} {
      #
      # We want to be able to address the page after this call via the
      # canonical name ::$item_id
      #
      set page [::xo::db::CrClass get_instance_from_db -item_id [$page item_id]]
    }
    return $page
  • initialize (scripted, public)

     <instance of xo::PackageMgr[i]> initialize [ -ad_doc ad_doc ] \
        [ -parameter parameter ] [ -package_id package_id ] [ -url url ] \
        [ -user_id user_id ] [ -actual_query actual_query ] \
        [ -original_url_and_query original_url_and_query ] \
        [ -init_url init_url ] [ -keep_cc keep_cc ] \
        [ -form_parameter form_parameter ] [ -export_vars export_vars ]

    Create the connection context ::xo::cc and a package object if these are none defined yet. The connection context ::xo::cc and the package object will be destroyed on cleanup, when the global variables are reclaimed. As a side effect this method sets in the calling context the query parameters and package_id as variables, using the "defaults" for default values. init_url false requires the package_id to be specified and a call to Package instproc set_url to complete initialization. keep_cc true means that the original connection context is preserved (i.e. not altered) in case it exists already.

    Switches:
    -ad_doc
    (optional)
    -parameter
    (optional)
    -package_id
    (defaults to "0") (optional)
    -url
    (optional)
    -user_id
    (defaults to "-1") (optional)
    -actual_query
    (defaults to " ") (optional)
    -original_url_and_query
    (optional)
    -init_url
    (defaults to "true") (optional)
    -keep_cc
    (defaults to "false") (optional)
    -form_parameter
    (optional)
    -export_vars
    (defaults to "true") (optional)

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_create_form_with_form_instance create_form_with_form_instance (test xowiki) xo::PackageMgr instproc initialize xo::PackageMgr instproc initialize test_create_form_with_form_instance->xo::PackageMgr instproc initialize test_includelet_childresources includelet_childresources (test xowiki) test_includelet_childresources->xo::PackageMgr instproc initialize test_includelet_toc includelet_toc (test xowiki) test_includelet_toc->xo::PackageMgr instproc initialize test_link_tests link_tests (test xowiki) test_link_tests->xo::PackageMgr instproc initialize test_package_normalize_path package_normalize_path (test xowiki) test_package_normalize_path->xo::PackageMgr instproc initialize acs::root_of_host acs::root_of_host (public) xo::PackageMgr instproc initialize->acs::root_of_host ad_host ad_host (public) xo::PackageMgr instproc initialize->ad_host ad_parse_documentation_string ad_parse_documentation_string xo::PackageMgr instproc initialize->ad_parse_documentation_string api_page_documentation_mode_p api_page_documentation_mode_p (public) xo::PackageMgr instproc initialize->api_page_documentation_mode_p auth::require_login auth::require_login (public) xo::PackageMgr instproc initialize->auth::require_login

    Testcases:
    package_normalize_path, includelet_toc, includelet_childresources, xowiki_test_cases, link_tests, slot_interactions, path_resolve, create_form_with_form_instance
    #:msg "--i [self args], URL=$url, init_url=$init_url"
    
    if {[info exists ad_doc] && [api_page_documentation_mode_p]} {
      ad_parse_documentation_string $ad_doc doc_elements
      set doc_elements(query) $parameter
      error [array get doc_elements] "ad_page_contract documentation"
    }
    
    if {$url eq "" && $init_url} {
      if {[ns_conn isconnected]} {
        set url [acs::root_of_host [ad_host]][ns_conn url]
      } else {
        #
        # In case, we are not connected and no URL path is provided,
        # we do a best effort job to set the "url" variable to a path
        # belonging to the right package. The is no way to provide
        # here a better approximation. Note that if e.g. a batch job
        # needs a more precise (object_specific) url, this has to be
        # generated on the caller side with [$object_id pretty_link]
        # or similar means.
        #
        set url [lindex [site_node::get_url_from_object_id -object_id $package_id] 0]
        ns_log warning "PackageMgr initialize sets best-effort URL <$url>"
      }
      #:log "--CONN ns_conn url -> $url"
    }
    
    #
    # Get package_id from url in case it is not known. When the
    # package_id is already known, this is a noop.
    #
    set package_id [ConnectionContext require_package_id_from_url  -package_id $package_id $url]
    #
    # Require connection context if needed
    #
    ConnectionContext require  -keep_cc $keep_cc  -package_id $package_id -user_id $user_id  -parameter $parameter -url $url -actual_query $actual_query
    
    if {[info exists original_url_and_query]} {
      ::xo::cc original_url_and_query $original_url_and_query
    }
    
    if {[info exists form_parameter]} {
      ::xo::cc array set form_parameter $form_parameter
    }
    
    #
    # Create package object instance if necessary.
    #
    if {$keep_cc} {
      :require $package_id
    } else {
      :require -url $url $package_id
    }
    
    #
    # In case the login expired, we can force an early login to
    # prevent later login redirects, which can cause problems
    # from within catch operations. The package can decide, if
    # it want to force a refresh of the login, even if some pages
    # might not require the real user_id.
    #
    #:msg "force [::$package_id force_refresh_login] && #    [::xo::cc set untrusted_user_id] != [::xo::cc user_id]"
    if {[::$package_id force_refresh_login]
        && [::xo::cc set untrusted_user_id] != [::xo::cc user_id]} {
      auth::require_login
    }
    
    if {$export_vars} {
      ::xo::cc export_vars -level 2
    }
    return $package_id
  • instances (scripted, public)

     <instance of xo::PackageMgr[i]> instances \
        [ -include_unmounted include_unmounted ] [ -closure closure ]
    Switches:
    -include_unmounted
    (defaults to "false") (optional)
    include unmounted package instances
    -closure
    (defaults to "false") (optional)
    include instances of subclasses of the package
    Returns:
    list of package_ids of xowiki instances

    Partial Call Graph (max 5 caller/called nodes):
    %3

    Testcases:
    No testcase defined.
    set package_key ${:package_key}
    if {$include_unmounted} {
      set result [::xo::dc list get_xowiki_packages {select package_id  from apm_packages where package_key = :package_key}]
    } else {
      set result [::xo::dc list get_mounted_packages {select package_id  from apm_packages p, site_nodes s  where package_key = :package_key  and s.object_id = p.package_id}]
    }
    if {$closure} {
      foreach subclass [:info subclass] {
        foreach id [$subclass instances -include_unmounted $include_unmounted -closure true] {
          lappend result $id
        }
      }
    }
    return [lsort -integer $result]
  • require (scripted, public)

     <instance of xo::PackageMgr[i]> require [ -url url ] package_id

    Create package object if needed.

    Switches:
    -url
    (optional)
    Parameters:
    package_id

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_create_folder_with_page create_folder_with_page (test xowf) xo::PackageMgr instproc require xo::PackageMgr instproc require test_create_folder_with_page->xo::PackageMgr instproc require test_create_form_with_form_instance create_form_with_form_instance (test xowiki) test_create_form_with_form_instance->xo::PackageMgr instproc require test_xowiki_test_cases xowiki_test_cases (test xowiki) test_xowiki_test_cases->xo::PackageMgr instproc require apm_package_key_from_id apm_package_key_from_id (public) xo::PackageMgr instproc require->apm_package_key_from_id

    Testcases:
    create_folder_with_page, xowiki_test_cases, create_form_with_form_instance
    if {$package_id eq ""} {
      #::xo::show_stack
      error "package_id must not be empty"
    }
    
    #:log "--R $package_id exists? [nsf::is object ::$package_id] url='$url'"
    
    if {![nsf::is object ::$package_id]} {
      #:log "--R we have to create ::$package_id //url='$url'"
      #
      # To make initialization code generic, we obtain from the
      # package_id the class of the package.
      #
      set package_key [apm_package_key_from_id $package_id]
      set package_class [[self class] get_package_class_from_package_key $package_key]
      if {$package_class eq ""} {
        #
        # For some unknown reason, we did not find the key.  We want
        # to be conservative, behave like in older versions that did
        # not provide a package_key, but required for this call to be
        # invoked on the actual class of the package. We provide
        # compatibility, but complain in ns_log.
        #
        # (E.g. hypermail2xowiki uses this)
        ns_log warning "Could not find ::xo::Package with key $package_key ($package_id)"
        set package_class [self]
      }
    
      if {$url ne ""} {
        $package_class create ::$package_id -destroy_on_cleanup -id $package_id -url $url
      } else {
        $package_class create ::$package_id -destroy_on_cleanup -id $package_id
      }
    } else {
      if {$url ne ""} {
        ::$package_id set_url -url $url
      }
    }
  • require_site_wide_pages (scripted, public)

     <instance of xo::PackageMgr[i]> require_site_wide_pages \
        [ -refetch on|off ] [ -refetch_if_modified on|off ] \
        [ -pages pages ]

    Load site-wide pages from the prototype page directory. The pages are loaded into to site-wide instance. When a page to be loaded exists already, a new revision is added. If no pages are provided, use the list of pages as defined for the package.

    Switches:
    -refetch
    (boolean) (defaults to "false") (optional)
    force fresh loading of prototype pages
    -refetch_if_modified
    (boolean) (defaults to "false") (optional)
    refetch when modification date of file is new than the version in the content repository
    -pages
    (optional)
    list of pages (without languages prefix) or empty to refer to per-package definition.

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_xowiki_test_cases xowiki_test_cases (test xowiki) xo::PackageMgr instproc require_site_wide_pages xo::PackageMgr instproc require_site_wide_pages test_xowiki_test_cases->xo::PackageMgr instproc require_site_wide_pages ad_file ad_file (public) xo::PackageMgr instproc require_site_wide_pages->ad_file xo::db::tcl_date xo::db::tcl_date (public) xo::PackageMgr instproc require_site_wide_pages->xo::db::tcl_date

    Testcases:
    xowiki_test_cases
    #
    # When no pages are provided, take the default of the definition of
    # the package class.
    #
    if {$pages eq ""} {
      set pages ${:site_wide_pages}
    }
    set info [:require_site_wide_info]
    foreach n $pages {
      set item_id [::xo::db::CrClass lookup -name en:$n -parent_id [dict get $info folder_id]]
      #:log "lookup en:$n => $item_id"
      if {$item_id == 0} {
        #:log "require_site_wide_pages lookup for 'en:$n' failed"
    
        #
        # Try to refetch without prefix to support loading of
        # prefix-less pages.
        #
        set item_id [::xo::db::CrClass lookup -name $n -parent_id [dict get $info folder_id]]
        if {$item_id != 0} {
          :log "Page $n was already loaded without a prefix"
        }
      }
      set refetch_this_page $refetch
    
      #
      # Check, if we have to refetch the page, since it was changed in
      # the meantime in the file-system.
      #
      if {!$refetch_this_page && $item_id != 0 && $refetch_if_modified} {
        set existing_page [::xo::db::CrClass get_instance_from_db -item_id $item_id]
        set fn [:prototype_page_file_name -name $n -package_key ${:package_key}]
        set time [clock scan [::xo::db::tcl_date [$existing_page publish_date] tz_var]]
        if {[ad_file mtime $fn] > $time} {
          set refetch_this_page true
        }
        if {$refetch_this_page} {
          ns_log notice "page $n: refetch newer-than-installed prototype file"
        }
      }
      if {$item_id == 0 || $refetch_this_page} {
        :log "require_site_wide_pages tries to load en:$n"
        set page [:import_prototype_page  -name $n  -parent_id [dict get $info folder_id]  -package_id [dict get $info instance_id] ]
        :log "Page en:$n loaded as '$page'"
      }
    }