%3 ::xo::db::CrItem ::xo::db::CrItem changed_redirect_url current_user_id delete fix_content initialize_loaded_object is_cached_object is_package_root_folder privilege=creator rename save save_new set_live_revision update update_attribute_from_slot update_content update_item_index update_revision www-revisions ::xo::db::Object ::xo::db::Object ::xo::db::CrItem->::xo::db::Object ::xo::db::CrCache::Item ::xo::db::CrCache::Item ::xo::db::CrItem->::xo::db::CrCache::Item instmixin ::xowiki::Page ::xowiki::Page → container_already_rendered → find_slot → get_tags → import → quoted_html_content → save_tags __debug_known_field_names add_computed_instance_attributes adp_subst anchor anchor_parent_id build_instance_attribute_map build_name can_be_linked can_contain can_link can_save category_export category_import changed_redirect_url check_adp_include_path check_unresolved_references condition=is_folder_page condition=match condition=regexp content_header_append content_header_get copy_content_vars create_form_field create_form_fields create_form_fields_from_form_constraints create_form_page_instance create_link create_raw_form_field css_class_name default_instance_attributes demarshall detail_link div edit_set_default_values edit_set_file_selector_folder error_during_render error_in_includelet evaluate_form_field_condition exists_form_parameter exists_query_parameter field_names find_slot footer form_field_exists form_field_flush_cache form_field_index form_parameter get_anchor_and_query get_content get_description get_folder get_form_data get_html_from_content get_ids_for_bulk_actions get_instance_attributes get_nls_language_from_lang get_parent_object get_property_from_link_page get_query_parameter_return_url get_rich_text_spec get_target_from_link_page htmlFooter include include_content initialize_loaded_object instantiate_includelet is_folder_page is_form is_link_page is_new_entry is_unprefixed item_ref lang lookup_cached_form_field lookup_form_field map_categories map_party marshall mutual_overwrite_occurred new_link normalize_internal_link_name notification_detail_link notification_notify notification_render notification_subject physical_item_id physical_package_id physical_parent_id pretty_link pretty_name query_parameter record_last_visited references references_update regsub_eval rename render render_content render_icon render_includelet reset_resolve_context resolve_included_page_name return_redirect_without_params reverse_map_party reverse_map_party_attribute save save_data save_new search_render self_link_ids serialize_relocatable set_content set_resolve_context show_fields stats_record_count stats_record_detail substitute_markup translate unescape unset_temporary_instance_variables update update_publish_status validate=form_constraints validate=form_input_fields validate=name validate=page_order www-autosave-attribute www-bulk-delete www-clipboard-add www-clipboard-clear www-clipboard-content www-clipboard-copy www-clipboard-export www-create-new www-create-or-use www-csv-dump www-delete www-delete-revision www-diff www-duplicate www-edit www-list www-make-live-revision www-popular-tags www-revisions www-save-attributes www-save-tags www-toggle-publish-status www-validate-attribute www-view ::xowiki::Page->::xo::db::CrItem ::xowiki::File ::xowiki::File build_name demarshall full_file_name html_content marshall pretty_name render_content render_icon search_render www-download ::xowiki::File->::xowiki::Page ::xowiki::PageTemplate ::xowiki::PageTemplate → count_usages count_usages update www-delete ::xowiki::PageTemplate->::xowiki::Page ::xowiki::PageInstance ::xowiki::PageInstance → get_list_from_form_constraints → get_short_spec_from_form_constraints adp_subst count_usages create_raw_form_field demarshall field_names_from_form_constraints get_field_label get_field_type get_form get_form_constraints get_from_template get_short_spec get_template_object render_content template_vars update widget_spec_from_folder_object www-use-template ::xowiki::PageInstance->::xowiki::Page ::xowiki::PlainPage ::xowiki::PlainPage render_content set_content substitute_markup unescape ::xowiki::PlainPage->::xowiki::Page ::xowiki::PodcastItem ::xowiki::PodcastItem render_content update ::xowiki::PodcastItem->::xowiki::File ::xowiki::Form ::xowiki::Form → add_dom_attribute_value → disable_input_fields → dom_disable_input_fields → requireFormCSS demarshall footer get_form_constraints is_form marshall render_content update ::xowiki::Form->::xowiki::PageTemplate ::xowiki::FormPage ::xowiki::FormPage → compute_filter_clauses → fetch_object → filter_expression → get_all_children → get_folder_children → get_form_entries → get_super_folders → get_table_form_fields → sql_value action_url adp_subst combine_data_and_form_field_default compute_link_properties condition=in_state condition=is_true configure_page=regression_test create_category_fields create_form_field create_form_fields create_form_fields_from_names demarshall exists_property extra_html_fields field_names field_names_from_form footer form_field_as_html form_fields_sanity_check get_anon_instances get_form_constraints get_form_value get_parameter get_property get_value group_assign group_require hstore_attributes include_header_info initialize initialize_loaded_object is_folder_page is_form is_link_page langstring lappend_property load_values_into_form_fields map_value map_values marshall new_link notification_render post_process_dom_tree post_process_form_fields pretty_name property render_content render_form_action_buttons render_icon render_thumbnails reverse_map_value reverse_map_values setCSSDefaults set_content set_form_data set_form_value set_live_revision set_property set_publish_status update update_attribute_from_slot update_item_index update_langstring_property www-edit www-file-upload www-toggle-modebutton ::xowiki::FormPage->::xowiki::PageInstance ::xowiki::Object ::xowiki::Object get_payload initialize_loaded_object render_content set_payload ::xowiki::Object->::xowiki::PlainPage

Class ::xowiki::Page

::xowiki::Page[i] create ... \
           [ -absolute_links (default "0") ] \
           [ -do_substitutions (default "1") ] \
           [ -render_adp (default "1") ]

Class Relations

  • class: ::xo::db::CrClass[i]
  • superclass: ::xo::db::CrItem[i]
  • subclass: ::xowiki::File[i], ::xowiki::PageTemplate[i], ::xowiki::PageInstance[i], ::xowiki::PlainPage[i]
::xo::db::CrClass create ::xowiki::Page \
     -superclass ::xo::db::CrItem

Methods (to be applied on the object)

  • container_already_rendered (scripted)

    if {![info exists ::xowiki_page_item_id_rendered]} {
      return ""
    }
    #:log "--OMIT and not $field in ([ns_dbquotelist $::xowiki_page_item_id_rendered])"
    return "and not $field in ([ns_dbquotelist $::xowiki_page_item_id_rendered])"
  • find_slot (scripted)

     xowiki::Page[i] find_slot

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_slot_interactions slot_interactions (test ) xowiki::Page proc find_slot xowiki::Page proc find_slot test_slot_interactions->xowiki::Page proc find_slot test_xowiki xowiki (test ) test_xowiki->xowiki::Page proc find_slot

    Testcases:
    slot_interactions, xowiki
    foreach cl [list $start_class {*}[$start_class info heritage]] {
      set slotobj ${cl}::slot::$name
      if {[nsf::is object $slotobj]} {
        #:msg $slotobj
        return $slotobj
      }
    }
    return ""
  • get_tags (scripted)

    if {[info exists item_id]} {
      if {[info exists user_id]} {
        # tags for item and user
        set tags [::xo::dc list -prepare integer,integer,integer get_tags {
          SELECT distinct tag from xowiki_tags
          where user_id = :user_id and item_id = :item_id and package_id = :package_id
        }]
      } else {
        #
        # All tags for this item
        #
        set tags [::xo::dc list -prepare integer,integer get_tags {
          SELECT distinct tag from xowiki_tags
          where item_id = :item_id and package_id = :package_id
        }]
      }
    } else {
      if {[info exists user_id]} {
        #
        # All tags for this user
        #
        set tags [::xo::dc list -prepare integer,integer get_tags {
          SELECT distinct tag from xowiki_tags
          where user_id = :user_id and package_id :package_id
        }]
      } else {
        #
        # All tags for the package instance
        #
        set tags [::xo::dc list -prepare integer get_tags {
          SELECT distinct tag from xowiki_tags
          where package_id = :package_id
        }]
      }
    }
    return $tags
  • import (scripted)

    ad_log_deprecated "::xowiki::Page proc" "import" "::$package_id import ..."
    
    if {![info exists package_id]}  {
      set package_id  [::xo::cc package_id]
    }
    set cmd [list ::$package_id import -replace $replace]
    
    if {[info exists user_id]} {lappend cmd -user_id $user_id}
    if {[info exists objects]} {lappend cmd -objects $objects}
    {*}$cmd
  • quoted_html_content (scripted)

    list [ad_text_to_html -- $text] text/html
  • save_tags (scripted)

    ::xo::dc dml delete_tags  "delete from xowiki_tags where item_id = :item_id and user_id = :user_id"
    
    #
    # Map funny characters in tags to white-space. Tags are just
    # single words, no quotes are allowed. The resulting tags must be
    # compatible with "Package->validate_tag".
    #
    regsub -all {[^\w.-]+} $tags " " tags
    
    foreach tag [split $tags " "] {
      if {$tag ne ""} {
        ::xo::dc dml insert_tag  "insert into xowiki_tags (item_id,package_id, user_id, tag, time)  values (:item_id, :package_id, :user_id, :tag, CURRENT_TIMESTAMP)"
      }
    }
    search::queue -object_id $revision_id -event UPDATE

Methods (to be applied on instances)

  • __debug_known_field_names (scripted)

    set fields {}
    foreach name [lsort [array names ::_form_field_names]] {
      set f $::_form_field_names($name)
      append fields "  $name\t[$f info class]\t [$f spec]\n"
    }
    append fields "Repeat container:\n"
    foreach f [::xowiki::formfield::repeatContainer info instances] {
      append fields "$f\t[$f name]\t [$f spec]\n"
      foreach component [$f components] {
        append fields "... [$component name]\t[$component info class]\t [$component spec]\n"
        if {[$component istype ::xowiki::formfield::CompoundField]} {
          foreach c [$component components] {
            append fields "..... [$c name]\t[$c info class]\t [$c spec]\n"
          }
        }
      }
    }
    #ns_log notice "dynamic repeat field $msg: fields & specs:\n$fields"
  • absolute_links (setter)

  • add_computed_instance_attributes (scripted)

    #
    # Provide a hook to add computed instances attributes e.g. from a
    # workflow. This method is used e.g. in form-usages for displaying
    # instance attributes in a sortable table or via csv.
    #
  • adp_subst (scripted)

    #
    # The provided content and the returned result are strings
    # containing HTML.
    #
    #:msg "--adp_subst in ${:name} vars=[:info vars]"
    foreach __v [:info vars] {
      if {[info exists $__v]} continue
      #ns_log notice "import instvar $__v into current scope"
      :instvar $__v
    }
    #
    # The following block imports variables from the class to the
    # instance. Not sure, why we want this. TODO: Comment or remove.
    #
    #set __ignorelist {RE __defaults name_method object_type_key db_slot}
    #set __my_class [:info class]
    #foreach __v [$__my_class info vars] {
    #  if {[info exists $__v] || $__v in $__ignorelist} continue
    #  ns_log notice "import from $__my_class var $__v into [self]"
    #  $__my_class instvar $__v
    #}
    set __ignorelist {
      __v __vars __l __ignorelist __varlist __references __my_class
      __last_includelet text item_id content lang_links
    }
    
    # set variables current_* to ease personalization
    set current_user [::xo::cc set untrusted_user_id]
    set current_url [::xo::cc url]
    
    set __vars [info vars]
    regsub -all -- [template::adp_variable_regexp$content {\1@\2;noquote@} content_noquote
    #:log "--adp before adp_eval '[template::adp_level]'"
    
    set __l [string length $content]
    try {
      set __bufsize [ns_adp_ctl bufsize]
    } on error {errorMsg} {
      #
      # The adp buffer has limited size. For large pages, it might happen
      # that the buffer overflows. In AOLserver 4.5, we can increase the
      # buffer size. In 4.0.10, we are out of luck.
      #
      set __bufsize 0
    }
    if {$__bufsize > 0 && $__l > $__bufsize} {
      #
      # We have AOLserver 4.5 or NaviServer , we can increase the
      # bufsize
      #
      ns_adp_ctl bufsize [expr {$__l + 1024}]
    }
    set template_code [template::adp_compile -string $content_noquote]
    set my_parse_level [template::adp_level]
    ad_try {
      set template_value [template::adp_eval template_code]
    } on error {__errMsg} {
      #
      # Something went wrong during substitution; prepare a
      # user-friendly error message containing a listing of the
      # available variables.
      #
      # compute list of possible variables
      set __varlist [list]
      set __template_variables__ "<ul>\n"
      foreach __v [lsort $__vars] {
        if {[array exists $__v]} continue ;# don't report  arrays
        if {$__v in $__ignorelist} continue
        lappend __varlist $__v
        append __template_variables__ "<li><b>$__v:</b> '[set $__v]'\n"
      }
      append __template_variables__ "</ul>\n"
      set ::template::parse_level $my_parse_level
      #:log "--adp after adp_eval '[template::adp_level]' mpl=$my_parse_level"
      set template_value "<div class='errorMsg'>Error in Page $name: $__errMsg</div>$content<p>Possible values are$__template_variables__"
    }
    return $template_value
  • anchor (scripted)

     <instance of xowiki::Page[i]> anchor

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_nested_self_references nested_self_references (test ) xowiki::Page instproc anchor xowiki::Page instproc anchor test_nested_self_references->xowiki::Page instproc anchor test_xowiki xowiki (test ) test_xowiki->xowiki::Page instproc anchor ad_try ad_try (public) xowiki::Page instproc anchor->ad_try

    Testcases:
    nested_self_references, xowiki
    ad_try {
      set l [:create_link $arg]
    } on error {errorMsg} {
      return "<div class='errorMsg'>Error during processing of anchor ${arg}:<blockquote>$errorMsg</blockquote></div>"
    }
    if {$l eq ""} {
      return ""
    }
    
    if {[info exists :__RESOLVE_LOCAL] && [$l exists is_self_link] && [$l is_self_link]} {
      :set_resolve_context -package_id [:physical_package_id] -parent_id [:physical_parent_id]
      $l parent_id [:anchor_parent_id]
      set html [$l render]
      :reset_resolve_context
    } else {
      set html [$l render]
    }
    
    if {[info commands $l] ne ""} {
      $l destroy
    } else {
      ns_log notice "link object already destroyed. This might be due to a recursive inclusion"
    }
    return $html
  • anchor_parent_id (scripted)

    #
    # This method returns the parent_id used for rendering links
    # between double square brackets [[...]]. It can be overloaded for
    # more complex embedding situations
    #
    return ${:item_id}
  • build_instance_attribute_map (scripted)

    #
    # Build the data structure for mapping internal values (IDs) into
    # string representations and vice versa. In particular, it builds
    # and maintains the __instance_attribute_map, which is an
    # associative list (attribute/value pairs) for form-field attributes.
    #
    #foreach f $form_fields {lappend fns [list [$f name] [$f info class]]}
    #:msg "page ${:name} build_instance_attribute_map $fns"
    if {![info exists :__instance_attribute_map]} {
      set :__instance_attribute_map [dict create]
    }
    foreach f $form_fields {
      set multiple [expr {[$f exists multiple] ? [$f set multiple] : 0}]
      #:msg "$f [$f name] cat_tree [$f exists category_tree] is fc: [$f exists is_category_field]"
      if {[$f exists category_tree] && [$f exists is_category_field]} {
        #:msg "page ${:name} field [$f name] is a category_id from [$f category_tree]"
        dict set :__instance_attribute_map [$f name] [list category [$f category_tree] $multiple]
        :category_export [$f category_tree]
      } elseif {[$f exists is_party_id]} {
        #:msg "page ${:name} field [$f name] is a party_id"
        dict set :__instance_attribute_map [$f name] [list party_id $multiple]
      } elseif {[$f istype "::xowiki::formfield::file"]} {
        dict set :__instance_attribute_map [$f name] [list file 0]
      }
    }
    return ${:__instance_attribute_map}
  • build_name (scripted)

    #
    # Build the name of the page, based on the provided nls_language
    # This method strips existing language-prefixes and uses the
    # provided nls_language or the instance variable for the new name.
    # It handles as well anonymous pages, which are never equipped
    # with language prefixes. ::xowiki::File has its own method.
    #
    set name ${:name}
    set stripped_name $name
    regexp {^..:(.*)$} $name _ stripped_name
    
    #:log "$name / '$stripped_name'"
    # prepend the language prefix only, if the entry is not empty
    if {$stripped_name ne ""} {
      if {[:is_folder_page] || [:is_link_page]} {
        #
        # Do not add a language prefix to folder pages
        #
        set name $stripped_name
      } else {
        if {$nls_language ne ""} {
          set :nls_language $nls_language
        }
        set name [:lang]:$stripped_name
      }
    }
    return $name
  • can_be_linked (scripted)

    return 1
  • can_contain (scripted)

    #
    # This is a stub which can / should be refined in applications,
    # which want to disallow pages (e.g. folders) to be parent of some
    # kind of content. The function should return 0 if some content is
    # not allowed.
    #
    return 1
  • can_link (scripted)

    #
    # This is a stub which can / should be refined in applications,
    # which want to disallow links to other pages, in the sense, that
    # the links are not shown at all. A sample implementation might
    # look like the following.
    #
    # if {$item_id ne 0} {
    #   set obj [::xo::db::CrClass get_instance_from_db -item_id $item_id]
    #   return [$obj can_be_linked]
    # }
    #
    return 1
  • can_save (scripted)

    #
    # Determine the parent object of the page to be saved. If the
    # parent object is a page as well, then call can_contain. The
    # function is just determining a Boolean value such it can be used
    # for testing insertability as well.
    #
    set parent [:get_parent_object]
    if {$parent ne "" && [$parent istype ::xowiki::Page]} {
      return [$parent can_contain [self]]
    }
    return 1
  • category_export (scripted)

    #
    # Build a command to rebuild the category tree on imports
    # (__map_command). In addition this method builds and maintains a
    # category map, which maps internal IDs into symbolic values
    # (__category_map).
    #
    # Ignore locale in get_id for now, since it seems broken
    set tree_ids [::xowiki::Category get_mapped_trees -object_id ${:package_id}  -names [list $tree_name] -output tree_id]
    # Make sure to have only one tree_id, in case multiple trees are
    # mapped with the same name.
    set tree_id [lindex $tree_ids 0]
    set tree_description [dict get [category_tree::get_data $tree_id] description]
    set categories [list]
    if {![info exists :__category_map]} {
      set :__category_map [dict create]
    }
    foreach category [::xowiki::Category get_category_infos -tree_id $tree_id] {
      lassign $category category_id category_name deprecated_p level
      lappend categories $level $category_name
      set names($level$category_name
      set node_name $tree_name
      for {set l 1} {$l <= $level} {incr l} {append node_name /$names($l)}
      dict set :__category_map $category_id $node_name
    }
    set cmd [list :category_import  -name $tree_name -description $tree_description  -locale [lang::system::site_wide_locale]  -categories $categories]
    if {![info exists :__map_command] || [string first $cmd ${:__map_command}] == -1} {
      append :__map_command \n $cmd
    }
    return ${:__category_map}
    #:log "cmd=$cmd"
  • category_import (scripted)

    # Execute the category import for every tree name only once per request
    set key ::__xowiki_category_import($name)
    if {[info exists $key]} return
    
    # ok, it is the first request
    #:msg "... catetegoy_import [self args]"
    
    # Do we have a tree with the specified named mapped?
    set tree_ids [::xowiki::Category get_mapped_trees -object_id ${:package_id} -locale $locale  -names [list $name] -output tree_id]
    set tree_id [lindex $tree_ids 0]; # handle multiple mapped trees with same name
    if {$tree_id eq ""} {
      # The tree is not mapped, we import the category tree
      :log "...importing category tree $name"
      set tree_id [category_tree::import -name $name -description $description  -locale $locale -categories $categories]
      category_tree::map -tree_id $tree_id -object_id ${:package_id}
    }
    
    #
    # build reverse category_map
    foreach category [::xowiki::Category get_category_infos -tree_id $tree_id] {
      lassign $category category_id category_name deprecated_p level
      lappend categories $level $category_name
      set names($level$category_name
      set node_name $name
      for {set l 1} {$l <= $level} {incr l} {append node_name /$names($l)}
      set ::__xowiki_reverse_category_map($node_name$category_id
    }
    #:msg "... catetegoy_import reverse map [array names ::__xowiki_reverse_category_map]"
    # mark the tree with this name as already imported
    set $key 1
  • changed_redirect_url (scripted)

    if {[::${:package_id} exists_query_parameter "return_url"]} {
      return ""
    }
    return [:pretty_link]
  • check_adp_include_path (scripted)

    #
    # For security reasons, don't allow arbitrary paths to different
    # packages.  All allowed includelets must be made available
    # under xowiki/www (preferable xowiki/lib/portlets/*). When the
    # provided path contains "admin/*", admin rights are required.
    #
    if {[string match "admin/*" $adp_fn]} {
      set allowed [::xo::cc permission  -object_id ${:package_id} -privilege admin  -party_id [::xo::cc user_id]]
      if {!$allowed} {
        return [list allowed $allowed msg "Page can only be included by an admin!" fn ""]
      }
    }
    if {[regexp {^/www/templates/(.*)$} $adp_fn . widget]} {
       return [list allowed 1 msg "" fn /packages/openacs-bootstrap3-theme/resources/widgets/$widget]
    }
    if {[string match "/*" $adp_fn] || [string match "../*" $adp_fn]} {
      # Never allow absolute paths.
      #
      # Alternatively, we could allow url-based includes, and then using
      # set node [site_node::get -url [ad_conn url]]
      # permission::require_permission -object_id $node(object_id) -privilege read
      # ... or admin/* based checks like in rp.
      #
      return [list allowed 0 msg "Invalid name for adp_include" fn ""]
    }
    return [list allowed 1 msg "" fn /packages/[::${:package_id} package_key]/lib/$adp_fn]
  • check_unresolved_references (scripted)

    #:log "check_unresolved_references: name ${:name} parent_id ${:parent_id}"
    set parent_id ${:parent_id}
    set name ${:name}
    foreach i [xo::dc list -prepare integer,text items_with_unresolved_references {
      SELECT page from xowiki_unresolved_references
      WHERE  parent_id = :parent_id
      AND    name = :name
    }] {
      set page [::xo::db::CrClass get_instance_from_db -item_id $i]
      #:log "==== check_unresolved_references found page [$page name] with a broken reference to the new page ${:name}"
      $page render -update_references all -with_footer false
    }
  • condition=is_folder_page (scripted)

    # query_context and value are ignored
    return [:is_folder_page]
  • condition=match (scripted)

    #
    # Condition for conditional checks in policy rules
    # The match condition is called with an attribute
    # name and a pattern like in
    #
    #  edit {
    #     {{match {name {*weblog}}} package_id admin}
    #     {package_id write}
    #  }
    #
    # This example specifies that for a page named
    # *weblog, the method "edit" is only allowed
    # for package admins.
    #
    #:msg "query_context='$query_context', value='$value'"
    if {[llength $value] != 2} {
      error "two arguments for match required, [llength $value] passed (arguments='$value')"
    }
    ad_try {
      set success [string match [lindex $value 1] [set :[lindex $value 0]]]
    } on error {errorMsg} {
      ns_log error "error during condition match: $errorMsg"
      set success 0
    }
    return $success
  • condition=regexp (scripted)

    #
    # Condition for conditional checks in policy rules
    # The match condition is called with an attribute
    # name and a pattern like in
    #
    #  edit               {
    #    {{regexp {name {(weblog|index)$}}} package_id admin}
    #    {package_id write}
    #  }
    #
    # This example specifies that for a page ending with
    # weblog or index, the method "edit" is only allowed
    # for package admins.
    #
    #:msg "query_context='$query_context', value='$value'"
    if {[llength $value] != 2} {
      error "two arguments for regexp required, [llength $value] passed (arguments='$value')"
    }
    ad_try {
      set success [regexp [lindex $value 1] [set :[lindex $value 0]]]
    } on error {errorMsg} {
      ns_log error "error during condition regexp: $errorMsg"
      set success 0
    }
    return $success
  • content_header_append (scripted)

    #
    # This function is to be called on pages that want to prepend
    # content prior to the main content. This is especially important
    # for HTML forms (e.g. produced by the xowiki::Form renderer),
    # where the form-body is not allowed to contain nested forms.
    #
    append ::__xowiki__content_header $text \n
  • content_header_get (scripted)

    if {[info exists ::__xowiki__content_header]} {
      return $::__xowiki__content_header
    }
  • copy_content_vars (scripted)

    array set excluded_var {
      folder_id 1 package_id 1 absolute_links 1 lang_links 1 modifying_user 1
      publish_status 1 item_id 1 revision_id 1 last_modified 1
      parent_id 1 context_id 1
    }
    foreach var [$from_object info vars] {
      # don't copy vars starting with "__"
      if {[string match "__*" $var]} continue
      if {![info exists excluded_var($var)]} {
        set :$var [$from_object set $var]
      }
    }
  • create_form_field (scripted)

    switch -glob -- $field_name {
      __* {}
      _* {
        set varname [string range $field_name 1 end]
        return [:create_raw_form_field -name $field_name  -spec $cr_field_spec  -slot [:find_slot $varname]]
      }
      default {
        return [:create_raw_form_field -name $field_name  -spec $field_spec  -slot [:find_slot $field_name]]
      }
    }
  • create_form_fields (scripted)

    set form_fields [:create_category_fields]
    foreach att $field_names {
      if {[string match "__*" $att]} continue
      lappend form_fields [:create_form_field $att]
    }
    return $form_fields
  • create_form_fields_from_form_constraints (scripted, public)

     <instance of xowiki::Page[i]> create_form_fields_from_form_constraints \
        [ -lookup ] form_constraints

    Create form-fields from form constraints. When "-lookup" is specified, the code reuses existing form-field instead of recreating it. Since create_raw_form_field uses destroy_on_cleanup, we do not have to care here about destroying the objects.

    Switches:
    -lookup
    (optional)
    Parameters:
    form_constraints
    Returns:
    potentially empty list of form-field objects

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_form_fields_from_form_constraints form_fields_from_form_constraints (test xowiki) xowiki::Page instproc create_form_fields_from_form_constraints xowiki::Page instproc create_form_fields_from_form_constraints test_form_fields_from_form_constraints->xowiki::Page instproc create_form_fields_from_form_constraints

    Testcases:
    form_fields_from_form_constraints
    set form_fields [list]
    foreach name_and_spec $form_constraints {
      regexp {^([^:]+):(.*)$} $name_and_spec _ spec_name short_spec
      if {[string match "@table*" $spec_name]
          || $spec_name in {@categories @cr_fields}
        } continue
      if {$lookup && [:form_field_exists $spec_name]} {
        #:msg "... found form_field for $spec_name"
        lappend form_fields [:lookup_form_field -name $spec_name {}]
      } else {
        #:msg "create '$spec_name' with spec '$short_spec'"
        lappend form_fields [:create_raw_form_field  -name $spec_name  -slot [:find_slot $spec_name]  -spec $short_spec  -form_constraints $form_constraints  ]
      }
    }
    return $form_fields
  • create_form_page_instance (scripted)

    set ia [dict merge [:default_instance_attributes] $instance_attributes]
    
    if {$nls_language eq ""} {
      set nls_language [:query_parameter nls_language:wordchar [:nls_language]]
    }
    #
    # Take the value of the instance variables package_id and
    # parent_id as default.
    #
    if {![info exists package_id]} {
      set package_id ${:package_id}
    }
    if {![info exists parent_id]}  {
      set parent_id ${:parent_id}
    }
    
    if {$creation_user eq ""} {
      #
      # When no creation_user is provided, take the current user_id,
      # but take care as well for situations, where no connections is
      # available.
      #
      set context [::$package_id context]
      if {![nsf::is object $context]} {
        ::xo::ConnectionContext require  -package_id $package_id  -url [::$package_id pretty_link ${:name}]
      }
      set creation_user [$context user_id]
    }
    
    set f [FormPage new -destroy_on_cleanup  -name $name  -text $text  -package_id $package_id  -parent_id $parent_id  -nls_language $nls_language  -publish_status $publish_status  -creation_user $creation_user  -state $state  -instance_attributes $ia  -page_template ${:item_id}]
    
    # Make sure to load the instance attributes
    #$f array set __ia [$f instance_attributes]
    
    #
    # Call the application specific initialization, when a FormPage is
    # initially created. This is used to control the life-cycle of
    # FormPages.
    #
    $f initialize
    
    #
    # If we copy an item, we use source_item_id to provide defaults.
    #
    if {$source_item_id ne ""} {
      set source [FormPage get_instance_from_db -item_id $source_item_id]
      $f copy_content_vars -from_object $source
      #set name "[::xowiki::autoname new -parent_id $source_item_id -name ${:name}]"
      #::$package_id get_lang_and_name -name $name lang name
      #$f set name $name
      #ns_log notice "FINAL NAME <$name>"
      #:msg nls=[$f nls_language],source-nls=[$source nls_language]
    }
    foreach {att value} $default_variables {
      $f set $att $value
    }
    
    #
    # Finally provide base for auto-titles.
    #
    $f set __title_prefix ${:title}
    
    return $f
  • create_link (scripted)

     <instance of xowiki::Page[i]> create_link

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_nested_self_references nested_self_references (test xowiki) xowiki::Page instproc create_link xowiki::Page instproc create_link test_nested_self_references->xowiki::Page instproc create_link test_path_resolve path_resolve (test ) test_path_resolve->xowiki::Page instproc create_link test_xowiki xowiki (test ) test_xowiki->xowiki::Page instproc create_link xowiki::guesstype xowiki::guesstype xowiki::Page instproc create_link->xowiki::guesstype

    Testcases:
    path_resolve, xowiki, nested_self_references
    #:msg [self args]
    set label $arg
    set link $arg
    set options ""
    regexp {^([^|]+)[|](.*)$} $arg _ link label
    regexp {^([^|]+)[|](.*)$} $label _ label options
    set options [:unescape $options]
    set link [string trim $link]
    
    # Get the package_id from the provided path, and - if found -
    # return the shortened link relative to it.
    set package_id [::${:package_id} resolve_package_path $link link]
    if {$package_id == 0} {
      # we treat all such links like external links
      if {[regsub {^//} $link / link]} {
        #
        # For local links (starting with //), we provide
        # a direct treatment. JavaScript and CSS files are
        # included, images are rendered directly.
        #
        switch -glob -- [::xowiki::guesstype $link] {
          text/css {
            ::xo::Page requireCSS $link
            return ""
          }
          application/x-javascript -
          application/javascript {
            ::xo::Page requireJS $link
            return ""
          }
          image/* {
            Link create [self]::link  -page [self]  -name ""  -type localimage  -label $label  -href $link
            [self]::link configure {*}$options
            return [self]::link
          }
        }
      }
      set l [ExternalLink new -label $label -href $link]
      $l configure {*}$options
      return $l
    }
    
    #
    # TODO missing: typed links
    #
    ## do we have a typed link? prefix has more than two chars...
    #  if {[regexp {^([^:/?][^:/?][^:/?]+):((..):)?(.+)$} $link _  # link_type _ lang  stripped_name]} {
    # set name file:$stripped_name
    #  }
    
    set link_info [:get_anchor_and_query $link]
    set parent_id [expr {$package_id == ${:package_id} ?
                         ${:parent_id} : [::$package_id folder_id]}]
    
    # we might consider make this configurable
    set use_package_path true
    set is_self_link false
    
    if {[regexp {^:(..):(.+)$} [dict get $link_info link] _ lang stripped_name]} {
      #
      # a language link (it starts with a ':')
      #
      set item_ref_info [::$package_id item_ref  -use_package_path $use_package_path  -default_lang [:lang]  -parent_id $parent_id  ${lang}:$stripped_name]
      dict set item_ref_info link_type language
    
    } elseif {[regexp {^[.]SELF[.]/(.*)$} [dict get $link_info link] _ link]} {
      #
      # Remove ".SELF./" from the path and search for the named
      # resource (e.g. the image name) under the current (physical)
      # item.
      #
      set self_link_ids [:self_link_ids]
      set parent_id  [dict get $self_link_ids parent_id]
      set package_id [dict get $self_link_ids package_id]
    
      #ns_log notice "SELF-LINK '[dict get $link_info link]' in TEXT resolve with parent $parent_id"
      set is_self_link true
      set item_ref_info [::$package_id item_ref  -use_package_path $use_package_path  -default_lang [:lang]  -parent_id $parent_id  $link]
      dict set link_info link $link
      #:log "SELF-LINK returns $item_ref_info"
    
    } else {
      #
      # A plain link, search relative to the parent.
      #
      #ns_log notice "PLAIN-LINK '[dict get $link_info link]' in TEXT resolve with parent $parent_id"
      set item_ref_info [::$package_id item_ref  -use_package_path $use_package_path  -default_lang [:lang]  -parent_id $parent_id  [dict get $link_info link]]
    }
    #ns_log notice "link_info $link_info"
    #ns_log notice "--L link <$arg> lang [:lang] CURRENT ${:name} nls_lang ${:nls_language} -> item_ref_info $item_ref_info"
    
    #:log "link '[dict get $link_info link]' package_id $package_id ${:package_id} => [array get {}]"
    
    if {$label eq $arg} {
      set label [dict get $link_info link]
    }
    
    set item_name [string trimleft [dict get $item_ref_info prefix]:[dict get $item_ref_info stripped_name] :]
    Link create [self]::link  -page [self]  -form [dict get $item_ref_info form]  -type [dict get $item_ref_info link_type]  -name $item_name  -lang [dict get $item_ref_info prefix]  -anchor [dict get $link_info anchor]  -query [dict get $link_info query]  -stripped_name [dict get $item_ref_info stripped_name]  -label $label  -parent_id [dict get $item_ref_info parent_id]  -item_id [dict get $item_ref_info item_id]  -package_id $package_id  -is_self_link $is_self_link
    
    # in case, we can't link, flush the href
    if {[:can_link [dict get $item_ref_info item_id]] == 0} {
      :references refused [dict get $item_ref_info item_id]
      if {[[self]::link exists href]} {
        [self]::link unset href
      }
    }
    
    [self]::link configure {*}$options
    set result [[self]::link]
    
    return $result
  • create_raw_form_field (scripted)

    #ns_log notice "... create_raw_form_field name $name spec '$spec'"
    set save_slot $slot
    if {$slot eq ""} {
      # We have no slot, so create a minimal slot. This should only happen for instance attributes
      set slot [::xo::Attribute new -pretty_name $name -datatype text -noinit]
      $slot destroy_on_cleanup
    }
    if {$nls_language eq ""} {
      set nls_language [:nls_language]
    }
    
    set spec_list [list]
    if {[$slot exists spec]} {lappend spec_list [$slot set spec]}
    if {$spec ne ""}         {lappend spec_list $spec}
    #:msg "[self args] spec_list $spec_list"
    #:msg "$name, spec_list = '[join $spec_list ,]'"
    
    if {[$slot exists pretty_name]} {
      set label [$slot set pretty_name]
    } else {
      set label $name
      :log "no pretty_name for variable $name in slot $slot"
    }
    
    if {[$slot exists default]} {
      #:msg "setting ff $name default = [$slot default]"
      set default [$slot default]
    } else {
      set default ""
    }
    #ns_log notice "... create $name with spec '[join $spec_list ,]'"
    set f [::xowiki::formfield::FormField new -name $name  -id        [::xowiki::Includelet html_id F.${:name}.$name]  -locale    $nls_language  -label     $label  -type      [expr {[$slot exists datatype]  ? [$slot set datatype]  : "text"}]  -help_text [expr {[$slot exists help_text] ? [$slot set help_text] : ""}]  -validator [expr {[$slot exists validator] ? [$slot set validator] : ""}]  -required  [expr {[$slot exists required]  ? [$slot set required]  : "false"}]  -default   $default  -spec      [join $spec_list ,]  -object    [self]  -slot      $save_slot  ]
    
    $f destroy_on_cleanup
    $f configure {*}$configuration
    return $f
  • creation_user (setter)

  • creator (setter)

  • css_class_name (scripted)

    #
    # Determine the CSS class name for xowiki forms
    #
    set name ""
    if {$margin_form} {
      set css [::xowiki::CSS class margin-form]
      if {$css ne ""} {
        set name "$css "
      }
    }
    return [append name [::xowiki::utility formCSSclass ${:name}]]
  • default_instance_attributes (scripted)

    #
    # Provide the default list of instance attributes to derived
    # FormPages.
    #
    # We want to be able to create FormPages from all pages.
    # by defining this method, we allow derived applications
    # to provide their own set of instance attributes.
    return [list]
  • demarshall (scripted)

    # this method is the counterpart of marshall
    # Unset the context_id, which would otherwise come from the
    # original object and persisted. By default, will be set to the
    # new object's parent_id
    unset -nocomplain -- :context_id
    set :parent_id $parent_id
    set :package_id $package_id
    :reverse_map_party_attribute -attribute creation_user   -default_party $creation_user -create_user_ids $create_user_ids
    :reverse_map_party_attribute -attribute modifying_user  -default_party $creation_user -create_user_ids $create_user_ids
    # If we import from an old database without page_order, provide a
    # default value
    if {![info exists :page_order]} {set :page_order ""}
    set is_folder_page [:is_folder_page]
    #:msg "is-folder-page ${:name} => $is_folder_page"
    if {$is_folder_page} {
      # reset names if necessary (e.g. import from old releases)
      set :name [:build_name]
    } else {
      # Check, if nls_language and lang are aligned.
      if {[regexp {^(..):} ${:name} _ lang]} {
        if {[string range [:nls_language] 0 1] ne $lang} {
          set old_nls_language [:nls_language]
          :nls_language [:get_nls_language_from_lang $lang]
          ns_log notice "nls_language for item ${:name} set from $old_nls_language to [:nls_language]"
        }
      }
    }
    # in the general case, no more actions required
    #:msg "demarshall ${:name} DONE"
  • description (setter)

  • detail_link (scripted)

    if {[info exists :instance_attributes]} {
      if {[dict exists ${:instance_attributes} detail_link]
          && [dict get ${:instance_attributes} detail_link] ne ""} {
        return [dict get ${:instance_attributes} detail_link]
      }
    }
    return [:pretty_link]
  • div (scripted)

    if {$arg eq "content"} {
      return "<div id='content' class='column'>"
    } elseif {[string match "left-col*" $arg]  || [string match "right-col*" $arg]  || $arg eq "sidebar"} {
      return "<div id='[ns_quotehtml $arg]' class='column'>"
    } elseif {$arg eq "box"} {
      return "<div class='box'>"
    } elseif {$arg eq ""} {
      return "</div>"
    } else {
      return ""
    }
  • do_substitutions (setter)

  • edit_set_default_values (scripted)

    # set some default values if they are provided
    foreach param {name title page_order:graph last_page_id:int32 nls_language:wordchar} {
      regexp {^([^:]+):?} $param . key
      if {[::${:package_id} exists_query_parameter $key]} {
        #:log "setting [self] set $key [::${:package_id} query_parameter $key]"
        set :$key [::${:package_id} query_parameter $param]
      }
    }
  • edit_set_file_selector_folder (scripted)

    #
    # setting up folder id for file selector (use community folder if available)
    #
    if {[info commands ::dotlrn_fs::get_community_shared_folder] ne ""} {
      # ... we have dotlrn installed
      set cid [::dotlrn_community::get_community_id]
      if {$cid ne ""} {
        # ... we are inside of a community, use the community folder
        return [::dotlrn_fs::get_community_shared_folder -community_id $cid]
      }
    }
    return ""
  • error_during_render (scripted)

    return "<div class='errorMsg'>$msg</div>"
  • error_in_includelet (scripted)

    return [:error_during_render "[_ xowiki.error_in_includelet [list arg $arg name ${:name}]]<br >\n$msg"]
  • evaluate_form_field_condition (scripted)

    #
    # Can be refined
    #
    return 0
  • exists_form_parameter (forward)

  • exists_query_parameter (forward)

  • field_names (scripted)

    array set dont_modify {item_id 1 revision_id 1 object_id 1 object_title 1 page_id 1 name 1}
    set field_names [list]
    foreach field_name [[:info class] array names db_slot] {
      if {[info exists dont_modify($field_name)]} {
        continue
      }
      lappend field_names _$field_name
    }
    #:msg field_names=$field_names
    return $field_names
  • find_slot (scripted)

     <instance of xowiki::Page[i]> find_slot

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_slot_interactions slot_interactions (test ) xowiki::Page instproc find_slot xowiki::Page instproc find_slot test_slot_interactions->xowiki::Page instproc find_slot test_xowiki xowiki (test ) test_xowiki->xowiki::Page instproc find_slot

    Testcases:
    slot_interactions, xowiki
    if {![info exists start_class]} {
      set start_class [:info class]
    }
    return [::xowiki::Page find_slot -start_class $start_class $name]
  • footer (scripted)

    return ""
  • form_field_exists (scripted)

    return [info exists ::_form_field_names($name)]
  • form_field_flush_cache (scripted)

    #
    # Flush all cached form_field_names.
    #
    array unset ::_form_field_names
  • form_field_index (scripted)

    set marker ::__computed_form_field_names($form_field_objs)
    if {[info exists $marker]} return
    
    foreach form_field_obj $form_field_objs {
      if {![$form_field_obj istype ::xowiki::formfield::FormField]} continue
      set ::_form_field_names([$form_field_obj name]) $form_field_obj
      :form_field_index [$form_field_obj info children]
    }
    set $marker 1
  • form_parameter (forward)

  • get_anchor_and_query (scripted)

    #
    # strip anchor and query from link name
    #
    set anchor ""
    set query ""
    # remove anchor
    regexp {^([^#]*)(\#|%23)(.*)$} $link _ link . anchor
    # remove query part
    regexp {^(.*)[?]([^?]+)$} $link _ link query
    return [list link $link anchor $anchor query $query]
  • get_content (scripted)

    return [:render -with_footer false -update_references never]
  • get_description (scripted)

    set revision_id ${:revision_id}
    set description ${:description}
    if {$description eq "" && $content ne ""} {
      set description [ad_html_text_convert -from text/html -to text/plain -- $content]
    }
    if {$description eq "" && $revision_id > 0} {
      set body [::xo::dc get_value -prepare integer get_description_from_syndication  "select body from syndication where object_id = :revision_id"  -default ""]
      set description [ad_html_text_convert -from text/html -to text/plain -- $body]
    }
    if {[info exists nr_chars] && [string length $description] > $nr_chars} {
      set description [string range $description 0 $nr_chars]...
    }
    return $description
  • get_folder (scripted)

    set page [self]
    while {1} {
      if {[$page istype ::xowiki::FormPage]} {
        if {[$page is_folder_page]} break
    
        #     set page_template [$page page_template]
        #     set page_template_name [$page_template name]
        #         # search the page_template in the list of form_ids
        #         if {$page_template in $folder_form_ids} {
        #           break
        #     } elseif {$page_template_name eq "en:folder.form"} {
        #       # safety belt, in case we have in different directories
        #       # different en:folder.form
        #       break
        #     } elseif {$page_template_name eq "en:link.form"} {
        #       set fp [:is_folder_page]
        #       :msg fp=$fp
        #       break
        #         }
      }
      set page [::xo::db::CrClass get_instance_from_db -item_id [$page parent_id]]
    }
    return $page
  • get_form_data (scripted, public)

     <instance of xowiki::Page[i]> get_form_data \
        [ -field_names field_names ] form_fields

    Get the values from the form and store it in the form fields and finally as instance attributes. If the field names are not specified, all form parameters are used.

    Switches:
    -field_names
    (optional)
    Parameters:
    form_fields

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_create_form_with_form_instance create_form_with_form_instance (test xowiki) xowiki::Page instproc get_form_data xowiki::Page instproc get_form_data test_create_form_with_form_instance->xowiki::Page instproc get_form_data _ _ (public) xowiki::Page instproc get_form_data->_ ad_log ad_log (public) xowiki::Page instproc get_form_data->ad_log

    Testcases:
    create_form_with_form_instance
    #:log "===== Page get_form_data"
    
    set validation_errors 0
    set category_ids [list]
    array set containers [list]
    set cc [::${:package_id} context]
    
    if {![info exists field_names]} {
      #
      # Field names might come directly from the POST request payload
      # and need to be validated: enforce that field names are made
      # only by alphanumeric characters and dots, with the exception
      # of file related fields, where either .tmpfile or .content-type
      # will be appended.
      #
      #:log "===== Page get_form_data RAW field_names from form data: [$cc array names form_parameter *_.*]"
    
      set field_names [list]
      foreach att [$cc array names form_parameter] {
        if {[regexp {^[\w.]+(\.(tmpfile|content-type))?$} $att]} {
          lappend field_names $att
        } else {
          #
          # We might decide to return a 403 here instead...
          #
          ad_log warning "Page get_form_data: field name '$att' was skipped. Received field names: [$cc array names form_parameter]"
        }
      }
    }
    
    #:msg "fields $field_names // $form_fields"
    #foreach f $form_fields { :msg "... $f [$f name]" }
    #
    # We have the form data and get all form_parameters into the
    # form-field objects.
    #
    foreach att $field_names {
      #:msg "getting att=$att"
      set processed($att) 1
      switch -glob -- $att {
        __category_* {
          set f [:lookup_form_field -name $att $form_fields]
          if {![$f is_disabled]} {
            set value [$f value [$cc form_parameter $att]]
            foreach v $value {lappend category_ids $v}
          }
        }
        __* {
          #
          # Other internal variables (like __object_name) are ignored
          #
        }
        _* {
          #
          # CR fields
          #
          set f [:lookup_form_field -name $att $form_fields]
          if {![$f is_disabled]} {
            set value [$f value [string trim [$cc form_parameter $att]]]
            set varname [string range $att 1 end]
            if {[string first . $att] == -1} {
              set :$varname $value
            }
          }
        }
        default {
          #
          # Application form content fields.
          #
          if {[regexp {^(.+)[.](tmpfile|content-type)} $att _ file field]} {
            #
            # File related fields.
            #
            set f [:lookup_form_field -name $file $form_fields]
            if {![$f is_disabled]} {
              $f $field [string trim [$cc form_parameter $att]]
            }
            #:msg "[$f name]: [list $f $field [string trim [$cc form_parameter $att]]]"
    
          } else {
            #
            # Fields related to instance variables.
            #
            #:log "===== Page get_form_data calls lookup_form_field -name $att"
            set f [:lookup_form_field -name $att $form_fields]
            if {![$f is_disabled]} {
              set value [$f value [string trim [$cc form_parameter $att]]]
              #:log "===== Page get_form_data calls lookup_form_field -name $att -> $f -> '$value'"              
              if {[string first . $att] == -1} {
                #
                # If the field is not a compound field, put the received
                # value into the instance attributes. The containerized
                # input values from compound fields are processed below.
                #
                dict set :instance_attributes $att $value
              }
              if {[$f exists is_category_field]} {
                foreach v $value {
                  lappend category_ids $v
                }
              }
            }
          }
        }
      }
      if {[string first . $att] > -1} {
        lassign [split $att .] container component
        lappend containers($container$component
      }
    }
    
    #
    # The first round was a processing based on the transmitted input
    # fields of the forms. Now we use the formfields to complete the
    # data and to validate it.
    #
    set leaf_components {}
    set container_fields {}
    foreach f $form_fields {
      if {[$f istype ::xowiki::formfield::CompoundField]} {
        #ns_log notice "TOP call leaf_components for [$f info class]"
        lappend leaf_components {*}[$f leaf_components]
        lappend container_fields $f
        set processed([$f name]) 1
      }
    }
    
    #ns_log notice "PROCESSED <[lsort [array names processed]]>"
    #ns_log notice "LEAF COMPONENTS <[lsort [lmap f $leaf_components {$f name}]]>"
    #ns_log notice "FORM_FIELDS [lsort [lmap f $form_fields {$f name}]]"
    #ns_log notice "CONTAINER   [lsort [array names containers]] + [lsort [lmap f $container_fields {$f name}]]"
    
    #
    # Certain HTML form field types are not transmitted by the browser
    # (e.g. unchecked checkboxes). Therefore, we have not processed
    # these fields above and have to do it now.
    #
    foreach f [concat $form_fields $leaf_components] {
      #:log "check processed $f [$f name] [info exists processed([$f name])] disabled=[$f is_disabled]"
      set att [$f name]
    
      if {![info exists processed($att)]
          && ![$f exists is_repeat_template]
          && ![$f is_disabled]
        } {
        #ns_log notice "==== form field $att [$f info class] not yet processed"
    
        switch -glob -- $att {
          __* {
            # other internal variables (like __object_name) are ignored
          }
          _* {
            # instance attribute fields
            set varname [string range $att 1 end]
            set default ""
            if {[info exists :$varname]} {set default [set :$varname]}
            set v [$f value_if_nothing_is_returned_from_form $default]
            #ns_log notice "===== value_if_nothing_is_returned_from_form [$f name] '$default' => '$v' (type=[$f info class])"
            set value [$f value $v]
            if {$v ne $default} {
              if {[string first . $att] == -1} {
                set :$varname $value
              }
            }
          }
          default {
            # user form content fields
            set default ""
            #
            # The reason, why we set in the next line the default to
            # the old value is due to "show-solution" in the qti
            # use-case. Maybe one should alter this use-case to
            # simplify the semantics here.
            #
            if {[dict exists ${:instance_attributes} $att]} {
              set default [dict get ${:instance_attributes} $att]
            }
            set v [$f value_if_nothing_is_returned_from_form $default]
            #ns_log notice "===== value_if_nothing_is_returned_from_form [$f name] '$default' => '$v' (type=[$f info class])"
    
            set value [$f value $v]
            if {[string first . $att] == -1} {
              dict set :instance_attributes $att $value
            }
          }
        }
      }
    }
    
    #
    # In the third iteration, combine the values from the components
    # of a container to the value of the container.
    #
    foreach f $container_fields {
      set name [$f name]
      #:log "container $name: compute value for [$f info class]"
      if {![$f is_disabled]} {
        dict set :instance_attributes $name [$f value]
        #:log "container $name: is set to '[dict get ${:instance_attributes} $name]'"
      } elseif {[dict exists ${:instance_attributes} $name]} {
        $f value [dict get ${:instance_attributes} $name]
      }
    }
    
    #
    # Finally run the validator on the top-level fields
    #
    foreach f [concat $form_fields] {
      #
      # Run validator on every field
      #
      #:log "validate [$f name] ([$f info class]) with value '[$f value]'"
      set validation_error [$f validate [self]]
      if {$validation_error ne ""} {
        #:log "validation of $f [$f name] with value '[$f value]' returns '$validation_error'"
        $f error_msg $validation_error
        incr validation_errors
      }
    }
    
    #:msg "validation returns $validation_errors errors"
    set current_revision_id [$cc form_parameter __current_revision_id ""]
    if {$validation_errors == 0
        && $current_revision_id ne ""
        && $current_revision_id != ${:revision_id}
      } {
      set validation_errors [:mutual_overwrite_occurred]
      ad_log warning "mutual_overwrite occurred, current_revision_id <$current_revision_id> my ${:revision_id}"
    }
    
    if {[:validate=form_input_fields $form_fields] == 0} {
      incr validation_errors
      #:log "validation error due validate=form_input_fields"
    }
    
    if {$validation_errors == 0} {
      #
      # Postprocess based on form fields based on form-fields methods.
      #
      foreach f $form_fields {
        if {![$f is_disabled]} {
          $f convert_to_internal
        }
      }
    } else {
      :log validation_errors=$validation_errors
      #
      # There were validation errors. Reset the value of form-fields
      # which have to be reset on validation errors due to browser
      # semantics.
      #
      foreach f $form_fields {
        $f reset_on_validation_error
      }
    }
    
    #:log "=== get_form_data has validation_errors $validation_errors, instance_attributes: ${:instance_attributes}"
    
    return [list $validation_errors [lsort -unique $category_ids]]
  • get_html_from_content (scripted)

    # Check, whether we got the content through a classic 2-element
    # OpenACS templating widget or directly.  If the list is not
    # well-formed, it must be contained directly.
    if {![catch {set l [llength $content]}]
        && $l == 2
        && [string match "text/*" [lindex $content 1]]} {
      return [lindex $content 0]
    }
    return $content
  • get_ids_for_bulk_actions (forward)

  • get_instance_attributes (scripted)

    if {[info exists :instance_attributes]} {
      return ${:instance_attributes}
    }
    return ""
  • get_nls_language_from_lang (forward)

  • get_parent_object (scripted)

    #
    # Obtain the parent object for a page. If the parent page is a
    # dummy entry or not an object, return empty.
    #
    set parent_id ${:parent_id}
    if {$parent_id > 0} {
      if {! [nsf::is object ::$parent_id] } {
        ::xo::db::CrClass get_instance_from_db -item_id $parent_id
      }
      return ::$parent_id
    }
    return ""
  • get_property_from_link_page (scripted)

    
    if {![:is_link_page]} {
      return $default
    }
    
    set item_ref [:property link]
    ::xo::db::CrClass get_instance_from_db -item_id ${:item_id}
    set props [::xo::cc cache [list ::${:item_id} compute_link_properties $item_ref]]
    
    if {[dict exists $props $property]} {
      #${:item_id} msg "prop $property ==> [dict get $props $property]"
      return [dict get $props $property]
    }
    return $default
  • get_query_parameter_return_url (scripted)

    #
    # Get the return_url from query parameters and check, if this is
    # local.
    #
    set return_url [:query_parameter return_url:localurl $default]
    #if {[util::external_url_p $return_url]} {
    #  ns_log warning "return_url $return_url is apparently an external URL"
    #  ad_return_complaint 1 "Page <b>'${:name}'</b> non-local return_url was specified"
    #  ad_script_abort
    #}
    return $return_url
  • get_rich_text_spec (scripted)

    set package_id ${:package_id}
    set spec ""
    #:msg WidgetSpecs=[::$package_id get_parameter -check_query_parameter false WidgetSpecs]
    foreach {s widget_spec} [::$package_id get_parameter -check_query_parameter false WidgetSpecs] {
      lassign [split $s ,] page_name var_name
      # in case we have no name (edit new page) we use the first value or the default.
      set name [expr {[info exists :name] ? ${:name} : $page_name}]
      #:msg "--w T.name = '$name' var=$page_name ([string match $page_name $name]), $var_name $field_name ([string match $var_name $field_name])"
      if {[string match $page_name $name] &&
          [string match $var_name $field_name]} {
        set spec $widget_spec
        #:msg "setting spec to $spec"
        break
      }
    }
    if {$spec eq ""} {
      return $default
    }
    return $field_name:$spec
  • get_target_from_link_page (scripted)

    #
    # Dereference link and return target object of the
    # link. Dereferencing happens up to a maximal depth to avoid loop
    # in circular link structures. If this method is called with e.g.
    # {-depth 1} and the link (actual object) points to some link2,
    # the link2 is returned.
    #
    # @param depth maximal dereferencing depth
    # @return target object or empty
    #
    set item_id [:get_property_from_link_page item_id 0]
    if {$item_id == 0} {return ""}
    set target [::xo::db::CrClass get_instance_from_db -item_id $item_id]
    set target_package_id [$target package_id]
    if {$target_package_id != ${:package_id}} {
      ::xowiki::Package require $target_package_id
      #::xowiki::Package initialize -package_id $target_package_id -init_url false -keep_cc true
    }
    if {$depth > 1 && [$target is_link_page]} {
      set target [:get_target_from_link_page -count [expr {$depth - 1}]]
    }
    return $target
  • htmlFooter (scripted)

    if {[info exists :__no_footer]} {return ""}
    set package_id ${:package_id}
    set footer ""
    
    if {[ns_conn isconnected]} {
      set url         "[ns_conn location][::xo::cc url]"
      set package_url "[ns_conn location][::$package_id package_url]"
    }
    
    set tags ""
    if {[::$package_id get_parameter with_tags:boolean 1] &&
        ![:exists_query_parameter no_tags] &&
        [::xo::cc user_id] != 0
      } {
      set tag_content [:include my-tags]
      set tag_includelet ${:__last_includelet}
      if {[$tag_includelet exists tags]} {
        set tags [$tag_includelet set tags]
      }
    } else {
      set tag_content ""
    }
    
    if {[::$package_id get_parameter with_digg:boolean 0] && [info exists url]} {
      if {![info exists description]} {set description [:get_description $content]}
      append footer "<div style='float: right'>"  [:include [list digg -description $description -url $url]] "</div>\n"
    }
    
    if {[::$package_id get_parameter with_delicious:boolean 0] && [info exists url]} {
      if {![info exists description]} {set description [:get_description $content]}
      append footer "<div style='float: right; padding-right: 10px;'>"  [:include [list delicious -description $description -url $url -tags $tags]]  "</div>\n"
    }
    
    if {[::$package_id get_parameter with_yahoo_publisher:boolean 0] && [info exists package_url]} {
      set publisher [::$package_id get_parameter "my_yahoo_publisher"  [::xo::get_user_name [::xo::cc user_id]]]
      append footer  "<div style='float: right; padding-right: 10px;'>"  [:include [list my-yahoo-publisher  -publisher $publisher  -rssurl "$package_url?rss"]]  "</div>\n"
    }
    
    if {[::$package_id get_parameter show_page_references:boolean 1]} {
      append footer [:include my-references]
    }
    
    if {[::$package_id get_parameter show_per_object_categories:boolean 1]} {
      set html [:include my-categories]
      if {$html ne ""} {
        append footer $html <br>
      }
      set categories_includelet ${:__last_includelet}
    }
    
    append footer $tag_content
    
    if {[::$package_id get_parameter with_general_comments:boolean 0] &&
        ![:exists_query_parameter no_gc]} {
      append footer [:include my-general-comments]
    }
    
    if {$footer ne ""} {
      # make sure, the
      append footer "<div class='visual-clear'><!-- --></div>"
    }
    
    return  "<div class='item-footer'>$footer</div>\n"
  • include (scripted, public)

     <instance of xowiki::Page[i]> include [ -configure configure ] arg

    Include the html of the includelet. The method generates an includelet object (might be another xowiki page) and renders it and returns either html or an error message.

    Switches:
    -configure
    (optional)
    Parameters:
    arg

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_create_form_with_form_instance create_form_with_form_instance (test xowiki) xowiki::Page instproc include xowiki::Page instproc include test_create_form_with_form_instance->xowiki::Page instproc include test_includelet_childresources includelet_childresources (test xowiki) test_includelet_childresources->xowiki::Page instproc include test_includelet_toc includelet_toc (test xowiki) test_includelet_toc->xowiki::Page instproc include _ _ (public) xowiki::Page instproc include->_

    Testcases:
    includelet_toc, includelet_childresources, create_form_with_form_instance
    set page [:instantiate_includelet $arg]
    if {$page eq "" || ![nsf::is object $page]} {
      # The variable 'page_name' is required by the message key
      set page_name [ns_quotehtml $arg]
      return [:error_during_render [_ xowiki.error-includelet-unknown]]
    }
    if {[$page istype ::xowiki::Page]} {
      set package_id [$page package_id]
      set allowed [[::$package_id set policy] check_permissions  -package_id $package_id  -user_id [::xo::cc set untrusted_user_id]  $page view]
      if {!$allowed} {
        return "<div class='errorMsg'>Insufficient privileges to view content of [$page name].</div>"
      }
    }
    if {[info exists configure]} {
      $page configure {*}$configure
    }
    return [:render_includelet $page]
  • include_content (scripted)

    #
    # Recursion depth is a global variable to ease the deletion etc.
    #
    if {[incr ::xowiki_inclusion_depth] > 10} {
      return [:error_in_includelet $arg [_ xowiki.error-includelet-nesting_to_deep]]$ch2
    }
    if {[regexp {^adp (.*)$} $arg _ adp]} {
      try {
        lindex $adp 0
      } on error {errMsg} {
        # there is something syntactically wrong
        incr ::xowiki_inclusion_depth -1
        return [:error_in_includelet $arg [_ xowiki.error-includelet-adp_syntax_invalid]]$ch2
      }
      set adp [string map {&nbsp; " "$adp]
    
      #
      # Check the provided name of the adp file
      #
      set path_info [:check_adp_include_path [lindex $adp 0]]
      #:log "path_info returned $path_info"
      if {![dict get $path_info allowed]} {
        incr ::xowiki_inclusion_depth -1
        return [:error_in_includelet $arg [dict get $path_info msg]]$ch2
      }
    
      set adp_fn [dict get $path_info fn]
      #
      # check the provided arguments
      #
      set adp_args [lindex $adp 1]
      if {[llength $adp_args] % 2 == 1} {
        incr ::xowiki_inclusion_depth -1
        set adp $adp_args
        incr ::xowiki_inclusion_depth -1
        return [:error_in_includelet $arg [_ xowiki.error-includelet-adp_syntax_invalid]]$ch2
      }
    
      lappend adp_args __including_page [self]
      set including_page_level [template::adp_level]
      ad_try {
        set page [template::adp_include $adp_fn $adp_args]
      } on error {errorMsg} {
        ad_log error "$errorMsg\n$::errorInfo"
        # in case of error, reset the adp_level to the previous value
        set ::template::parse_level $including_page_level
        return [:error_in_includelet $arg  [_ xowiki.error-includelet-error_during_adp_evaluation]]$ch2
      } finally {
        incr ::xowiki_inclusion_depth -1
      }
    
      return $page$ch2
    } else {
      # we have a direct (adp-less include)
      set html [:include [:unescape $arg]]
      #:log "--include includelet returns $html"
      incr ::xowiki_inclusion_depth -1
      return $html$ch2
    }
  • initialize_loaded_object (scripted)

    if {[info exists :title] && ${:title} eq ""} {set :title ${:name}}
    next
  • instantiate_includelet (scripted)

    # we want to use package_id as proc-local variable, since the
    # cross package reference might alter it locally
    set package_id ${:package_id}
    
    # do we have a wellformed list?
    ::try {
      set page_name [lindex $arg 0]
    } on error {errMsg} {
      # there must be something syntactically wrong
      return [:error_in_includelet $arg [_ xowiki.error-includelet-dash_syntax_invalid]]
    }
    #:msg "includelet: [lindex $arg 0], caller parameters ? '[lrange $arg 1 end]'"
    
    # the include is either a includelet class, or a wiki page
    if {[:isclass ::xowiki::includelet::$page_name]} {
      # direct call, without page, not tailorable
      set page [::xowiki::includelet::$page_name new  -package_id $package_id  -name $page_name  -locale [::xo::cc locale]  -actual_query [::xo::cc actual_query]]
    } else {
      #
      # Include a wiki page, tailorable.
      #
      #set page [:resolve_included_page_name $page_name]
      set page [::$package_id get_page_from_item_ref  -use_package_path true  -use_site_wide_pages true  -use_prototype_pages true  -default_lang [:lang]  -parent_id ${:parent_id} $page_name]
    
      if {$page ne "" && ![$page exists __decoration]} {
        #
        # we use as default decoration for included pages
        # the "portlet" decoration
        #
        $page set __decoration [::$package_id get_parameter default-portlet-decoration:graph portlet]
      }
    }
    
    if {$page ne ""} {
      $page set __caller_parameters [lrange $arg 1 end]
      $page destroy_on_cleanup
      set :__last_includelet $page
      $page set __including_page [self]
      if {[$page istype ::xowiki::Includelet]} {
        $page initialize
      }
    }
    return $page
  • is_folder_page (scripted, public)

     <instance of xowiki::Page[i]> is_folder_page \
        [ -include_folder_links include_folder_links ]

    Check, if page is a folder. This function is typically overlaaded by specializations. Plain xowiki::Pages are never folders.

    Switches:
    -include_folder_links
    (defaults to "true") (optional)
    return true, if the current page is a link to a folder.
    Returns:
    boolean

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_xowiki_test_cases xowiki_test_cases (test xowiki) xowiki::Page instproc is_folder_page xowiki::Page instproc is_folder_page test_xowiki_test_cases->xowiki::Page instproc is_folder_page

    Testcases:
    xowiki_test_cases
    return 0
  • is_form (scripted)

    return 0
  • is_link_page (scripted)

    return 0
  • is_new_entry (scripted)

    return [expr {${:publish_status} eq "production" && $old_name eq ${:revision_id}}]
  • is_unprefixed (scripted)

    #
    # Pages which should not get an extra language prefix.  In case,
    # your package has further such requirements, extend this proc in
    # you package.
    #
    return [expr {[:is_folder_page]
                  || [:is_link_page]
                  || ${:name} eq ${:revision_id}
                }]
  • item_ref (forward)

  • lang (scripted)

    return [string range ${:nls_language} 0 1]
  • last_modified (setter)

  • lookup_cached_form_field (scripted)

    set key ::_form_field_names($name)
    #:msg "FOUND($name)=[info exists $key]"
    if {[info exists $key]} {
      return [set $key]
    }
    error "No form field with name $name found"
  • lookup_form_field (scripted)

    :form_field_index $form_fields
    #ns_log notice "lookup_form_field <$name>"
    
    set key ::_form_field_names($name)
    if {[info exists $key]} {
      return [set $key]
    }
    #
    # We have here a non-existing form-field. Maybe the entry in the
    # form was dynamically created, so we create it here on the fly...
    #
    # For forms with variable numbers of entries, we allow wild-cards
    # in the field-names of the form constraints.
    #
    foreach name_and_spec [:get_form_constraints] {
      regexp {^([^:]+):(.*)$} $name_and_spec _ spec_name short_spec
    
      if {[string match $spec_name $name]} {
        set f [:create_form_fields_from_form_constraints [list $name:$short_spec]]
        set $key $f
        return $f
      }
    }
    
    #
    # Maybe, this was a repeat field, and we have to create the nth
    # component dynamically.
    #
    set components [split $name .]
    set path [lindex $components 0]
    #ns_log notice "dynamic repeat field name '$name' -> components <$components>"
    
    foreach c [lrange $components 1 end] {
      if {[string is integer -strict $c]} {
        # this looks like a repeat component
        #ns_log notice "dynamic repeat field root <$path> number $c exists? [info exists ::_form_field_names($path)]"
    
        if {[info exists ::_form_field_names($path)]} {
          #
          # The root field exists, so add the component.
          #
          set repeatField [set ::_form_field_names($path)]
          #
          # Add all components from 1 to specified number to the list,
          # unless restricted by the max value. This frees us from
          # potential problems, when the browser sends the form fields
          # in an unexpected order. The resulting components will be
          # always in the numbered order.
          #
          set max [$repeatField max]
          if {$max > $c} {
            set max $c
          }
          for {set i 1} {$i <= $max} {incr i} {
            if {![info exists ::_form_field_names($path.$i)]} {
              set f [$repeatField require_component $i]
              #ns_log notice "dynamic repeat field created $path.$i -> $f"
              :form_field_index $f
            }
          }
        } else {
          :__debug_known_field_names "<$path> needed to create <$path.$c>"
        }
      }
      append path . $c
    }
    #
    # We might have created in the loop above the required
    # formfield. If so, return it.
    #
    if {[info exists $key]} {
      #ns_log notice "dynamic repeat 2nd lookup for $key succeeds"
      return [set $key]
    }
    
    if {$name ni {langmarks fontname fontsize formatblock} && ![string match *__locale $name]} {
      set names [list]
      #xo::show_stack
      foreach f $form_fields {lappend names [$f name]}
      :msg "No form field with name '$name' found (available fields: [lsort [array names ::_form_field_names]])"
      ns_log warning "====== lookup_form_field: No form field with name '$name' found"  "(available fields: [lsort [array names ::_form_field_names]])"
    }
    set f [:create_form_fields_from_form_constraints [list $name:text]]
    set $key $f
    return $f
  • map_categories (scripted)

    #
    # Could be optimized, if we do not want to have categories (form
    # constraints?)
    #
    #:log "--category::map_object -remove_old -object_id ${:item_id} <$category_ids>"
    category::map_object -remove_old -object_id ${:item_id} $category_ids
  • map_party (scripted)

    if {$party_id eq "" || $party_id == 0} {
      return $party_id
    }
    ad_try {
      acs_user::get -user_id $party_id -array info
      set result [list]
      foreach a {username email first_names last_name screen_name url} {
        lappend result $a $info($a)
      }
      :log "--    map_party $party_id: $result"
      return $result
    } on error {errorMsg} {
      # swallow errors; there should be a better way to check if user
      # and or group info exists
    }
    ad_try {
      group::get -group_id $party_id -array info
      :log "got group info: [array get info]"
      set result [array get info]
      set members {}
      foreach member_id [group::get_members -group_id $party_id] {
        lappend members [:map_party -property $property $member_id]
      }
      lappend result members $members
      ns_log notice "--    map_party $party_id: $result"
      return $result
    } on error {errorMsg} {
      # swallow errors; there should be a better way to check if user
      # and or group info exists
    }
    ns_log warning "Cannot map party_id $party_id, probably not a user; property $property lost during export"
    return {}
  • marshall (scripted)

    :unset_temporary_instance_variables
    
    set old_creation_user  [:creation_user]
    set old_modifying_user ${:modifying_user}
    set :creation_user   [:map_party -property creation_user $old_creation_user]
    set :modifying_user  [:map_party -property modifying_user $old_modifying_user]
    if {$mode eq "export" && [:is_new_entry ${:name}]} {
      #
      # For anonymous entries, names might clash in the target
      # instance. If we create on the target site for anonymous
      # entries always new instances, we end up with duplicates.
      # Therefore, we rename anonymous entries during export to
      #    ip_address:port/item_id
      #
      set server [ns_info server]
      set port [ns_config ns/server/${server}/module/nssock port]
      set new_name [ns_info address]:${port}-${:item_id}
    }
    
    if {[info exists new_name]} {
      #
      # We have a new name, so patch this locally to get it into the
      # serialized content.
      #
      set old_name ${:name}
      set :name $new_name
      set content [:serialize_relocatable]
      set :name $old_name
    } else {
      set content [:serialize_relocatable]
    }
    set :creation_user  $old_creation_user
    set :modifying_user $old_modifying_user
    
    return $content
  • mime_type (setter)

  • mutual_overwrite_occurred (scripted)

    util_user_message -html  -message "[_ xowiki.User] <em>[::xo::get_user_name ${:modifying_user}]</em> [_ xowiki.has_modified_this_page].  [_ xowiki.Please_open] <a href='[ns_quotehtml [::xo::cc url]]' target='_blank'>[_ xowiki.modified_page]</a> [_ xowiki.new_window_or_OK]."
    # return 1 to flag validation error, 0 to ignore this fact
    return 1
  • name (setter)

  • new_link (scripted)

    if {[info exists parent_id] && $parent_id eq ""} {
      unset parent_id
    }
    return [::$page_package_id make_link $page_package_id  edit-new object_type name title nls_language return_url parent_id autoname]
  • nls_language (setter)

  • normalize_internal_link_name (scripted)

    #
    # strip anchor and query from link name
    #
    set anchor ""
    set query ""
    # remove anchor
    regexp {^([^#]*)(\#|%23)(.*)$} $stripped_name _ stripped_name . anchor
    # remove query part
    regexp {^(.*)[?]([^?]+)$} $stripped_name _ stripped_name query
    
    # if we have an empty stripped name, it is a link to the current
    # page, maybe in a different language
    if {$stripped_name eq ""} {
      regexp {:([^:]+)$} $name _ stripped_name
    }
    
    set normalized_name [::${:package_id} normalize_name $stripped_name]
    #:msg "input: [self args] - lang=[:lang], [:nls_language]"
    if {$lang  eq ""}   {set lang [:lang]}
    if {$name  eq ""}   {set name $lang:$normalized_name}
    #:msg result=[list name $name lang $lang normalized_name $normalized_name anchor $anchor]
    return [list name $name lang $lang normalized_name $normalized_name anchor $anchor query $query]
  • notification_detail_link (scripted)

    set link [:pretty_link -absolute 1]
    append html "<p>For more details, see <a href='[ns_quotehtml $link]'>[ns_quotehtml ${:title}]</a></p>"
    append text "\nFor more details, see $link ...\n"
    return [list html $html text $text]
  • notification_notify (scripted)

    ::xowiki::notification::do_notifications -page [self]
  • notification_render (scripted)

    return [:render]
  • notification_subject (scripted)

    if {$category_label eq ""} {
      return "\[$instance_name\]: ${:title} ($state)"
    } else {
      return "\[$instance_name\] $category_label: ${:title} ($state)"
    }
  • page_id (setter)

  • page_order (setter)

  • physical_item_id (scripted)

    if {[info exists :physical_item_id]} {
      return ${:physical_item_id}
    } else {
      return ${:item_id}
    }
  • physical_package_id (scripted)

    if {[info exists :physical_package_id]} {
      return ${:physical_package_id}
    } else {
      return ${:package_id}
    }
  • physical_parent_id (scripted)

    if {[info exists :physical_parent_id]} {
      return ${:physical_parent_id}
    } else {
      return ${:parent_id}
    }
  • pretty_link (scripted, public)

     <instance of xowiki::Page[i]> pretty_link [ -anchor anchor ] \
        [ -query query ] [ -absolute on|off ] [ -siteurl siteurl ] \
        [ -lang lang ] [ -download download ] [ -path_encode on|off ]

    This method is a convenience stub for Package->pretty_link and can be overloaded for different pages types. Note that it is necessary to initialize the package before this method can be used.

    Switches:
    -anchor
    (optional)
    anchor to be added to the link
    -query
    (optional)
    query parameters to be added literally to the resulting URL
    -absolute
    (boolean) (defaults to "false") (optional)
    make an absolute link (including protocol and host)
    -siteurl
    (optional)
    -lang
    (optional)
    use the specified 2 character language code (rather than computing the value)
    -download
    (defaults to "false") (optional)
    create download link (without m=download)
    -path_encode
    (boolean) (defaults to "true") (optional)
    control encoding of the url path. Returns the URL path urlencoded, unless path_encode is set to false.
    Returns:
    the pretty_link for the current page
    See Also:

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_nested_self_references nested_self_references (test xowiki) xowiki::Page instproc pretty_link xowiki::Page instproc pretty_link test_nested_self_references->xowiki::Page instproc pretty_link test_xowiki_test_cases xowiki_test_cases (test xowiki) test_xowiki_test_cases->xowiki::Page instproc pretty_link ad_log ad_log (public) xowiki::Page instproc pretty_link->ad_log

    Testcases:
    xowiki_test_cases, nested_self_references
    if {${:parent_id} eq "0"} {
      set msg "you must not call pretty_link on a page with parent_id 0"
      ad_log error $msg
      error $msg
    }
    ${:package_id} pretty_link -parent_id ${:parent_id}  -anchor $anchor -query $query -absolute $absolute -siteurl $siteurl  -lang $lang -download $download -page [self]  -path_encode $path_encode  ${:name}
  • pretty_name (scripted)

    return ${:name}
  • publish_date (setter)

  • query_parameter (forward)

  • record_last_visited (scripted)

    set item_id ${:item_id}
    set package_id ${:package_id}
    if {![info exists user_id]} {set user_id [::xo::cc set untrusted_user_id]}
    if {$user_id > 0} {
      # only record information for authenticated users
      ::xo::dc transaction {
        set rows [xo::dc dml -prepare integer,integer update_last_visisted {
          update xowiki_last_visited set time = CURRENT_TIMESTAMP, count = count + 1
          where page_id = :item_id and user_id = :user_id
        }]
        if {$rows ne "" && $rows < 1} {
          ::xo::dc dml insert_last_visisted  "insert into xowiki_last_visited (page_id, package_id, user_id, count, time)  values (:item_id, :package_id, :user_id, 1, CURRENT_TIMESTAMP)"
        }
      }
    }
  • references (scripted)

    #ns_log notice "---- ${:name} references $submethod $args"
    switch -- $submethod {
      clear {
        set :__references { unresolved {} resolved {} refused {} }
      }
      unresolved -
      refused -
      resolved {
        return [dict lappend :__references $submethod [lindex $args 0]]
      }
      get { return [dict get ${:__references} [lindex $args 0]] }
      all { return ${:__references} }
      default {error "unknown submethod: $submethod"}
    }
  • references_update (scripted)

    #:log "references_update resolved '$resolved' unresolved '$unresolved'"
    set item_id ${:item_id}
    ::xo::dc dml -prepare integer delete_references  "delete from xowiki_references where page = :item_id"
    ::xo::dc dml -prepare integer delete_unresolved_references  "delete from xowiki_unresolved_references where page = :item_id"
    foreach ref $resolved {
      lassign $ref r link_type
      ::xo::dc dml insert_reference  "insert into xowiki_references (reference, link_type, page)  values (:r,:link_type,:item_id)"
    }
    foreach ref $unresolved {
      dict with ref {
        ::xo::dc dml insert_unresolved_reference  "insert into xowiki_unresolved_references (page, parent_id, name, link_type)  values (:item_id,:parent_id,:name,:link_type)"
      }
    }
  • regsub_eval (scripted)

    if {$noquote} {
      set map { \[ \\[ \] \\] \$ \\$ \\ \\\\}
    } else {
      set map { \" \\\" \[ \\[ \] \\] \$ \\$ \\ \\\\}
    }
    uplevel [list subst [regsub -all -- $re [string map $map $string"\[$cmd\]"]]
  • rename (scripted)

    ${:package_id} flush_name_cache -name $old_name -parent_id ${:parent_id}
    next
    :log "----- rename <$old_name> to <$new_name>"
    #ns_log notice [:serialize]
  • render (scripted, public)

     <instance of xowiki::Page[i]> render \
        [ -update_references update_references ] [ -with_footer on|off ]

    Render a wiki page with some optional features, such as including a footer or updating references for this page.

    Switches:
    -update_references
    (defaults to "unresolved") (optional)
    might be "all", "unresolved" or "never"
    -with_footer
    (boolean) (defaults to "true") (optional)
    boolean value
    Returns:
    rendered HTML content.

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_link_tests link_tests (test xowiki) xowiki::Page instproc render xowiki::Page instproc render test_link_tests->xowiki::Page instproc render test_xowiki_test_cases xowiki_test_cases (test xowiki) test_xowiki_test_cases->xowiki::Page instproc render

    Testcases:
    xowiki_test_cases, link_tests
    #
    # prepare language links
    #
    array set :lang_links {found "" undefined ""}
    #
    # prepare references management
    #
    :references clear
    if {[info exists :__extra_references]} {
      #
      # xowiki content-flow uses extra references, e.g. to forms.
      # TODO: provide a better interface for providing these kind of
      # non-link references.
      #
      foreach ref ${:__extra_references} {
        :references resolved $ref
      }
      unset :__extra_references
    }
    #
    # Get page content and care about reference management.
    #
    set content [:render_content]
    #
    # Clear old reference and record new ones in cases updating
    # references is activated "always" or just for unresolved
    # references.
    #
    set unresolved_references [:references get unresolved]
    
    if {$update_references eq "all"
        || ($update_references eq "unresolved" && [llength $unresolved_references] > 0)
      } {
      :references_update  [lsort -unique [:references get resolved]]  [lsort -unique $unresolved_references]
    }
    #
    #:log "Page ${:name} render with_footer $with_footer - [ns_conn isconnected] - [catch {ns_conn content}]"
    #
    # handle footer
    #
    if {$with_footer && [::xo::cc get_parameter content-type:graph text/html] eq "text/html"} {
      append content "<DIV class='content-chunk-footer'>"
      if {![info exists :__no_footer] && ![::xo::cc get_parameter __no_footer:boolean 0]} {
        append content [:footer]
      }
      append content "</DIV>\n"
    }
    return $content
  • render_adp (setter)

  • render_content (scripted)

    #:log "-- '${:text}'"
    lassign ${:text} html mime
    if {[:render_adp]} {
      set html [:adp_subst $html]
    }
    return [:substitute_markup $html]
  • render_icon (scripted)

    return [list text [namespace tail [:info class]] is_richtext false]
  • render_includelet (scripted)

    #
    # The passed includelet is either an instance of ::xowiki::Page or
    # of ::xowiki::Includelet
    #
    foreach {att value} [$includelet set __caller_parameters] {
      switch -- $att {
        -decoration {$includelet set __decoration $value}
        -title {$includelet set title $value}
        -id {$includelet set id $value}
      }
    }
    if {[$includelet exists __decoration] && [$includelet set __decoration] ne "none"} {
      $includelet mixin add ::xowiki::includelet::decoration=[$includelet set __decoration]
    }
    
    set includeletClass [$includelet info class]
    if {[$includeletClass exists cacheable] && [$includeletClass set cacheable]} {
      $includelet mixin add ::xowiki::includelet::page_fragment_cache
    }
    
    if {[$includelet istype ::xowiki::Includelet]} {
      # call this always
      $includelet include_head_entries
    }
    
    # "render" might be cached
    set html ""
    ad_try {
      set html [$includelet render]
    } on error {errorMsg} {
      set errorCode $::errorCode
      set errorInfo $::errorInfo
      if {[string match "*for parameter*" $errorMsg]} {
        ad_return_complaint 1 [ns_quotehtml $errorMsg]
        ad_script_abort
      } else {
        ad_log error "render_includelet $includeletClass led to: $errorMsg ($errorCode)\n$errorInfo"
        set page_name [$includelet name]
        set ::errorInfo [::xowiki::Includelet html_encode $errorInfo]
        set html [:error_during_render [_ xowiki.error-includelet-error_during_render]]
      }
    }
    #:log "--include includelet returns $html"
    return $html
  • reset_resolve_context (scripted)

    #
    # Pop the last values from the stack
    #
    if {![info exists :resolve_context_stack] || [llength ${:resolve_context_stack}] < 1} {
      error "set_resolve_context and reset_resolve_context calls not balanced"
    }
    set entry [lindex ${:resolve_context_stack} end]
    set :resolve_context_stack [lrange ${:resolve_context_stack} 0 end-1]
    :configure {*}$entry
    #
    # When the stack is empty, remove the stack and the "physical*" attributes
    #
    if {[llength ${:resolve_context_stack}] == 0} {
      unset :resolve_context_stack
      foreach att {item package parent} {
        set name physical_${att}_id
        if {[info exists :$name]} {
          unset :$name
        }
      }
    }
  • resolve_included_page_name (scripted, public)

     <instance of xowiki::Page[i]> resolve_included_page_name page_name

    Determine the page object for the specified page name. The specified page name might have the form //some_other_instance/page_name, in which case the page is resolved from some other package instance. If the page_name does not contain a language prefix, the language prefix of the including page is used.

    Parameters:
    page_name

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

    Testcases:
    No testcase defined.
    if {$page_name ne ""} {
      set page [::${:package_id} resolve_page_name_and_init_context -lang [:lang] $page_name]
      if {$page eq ""} {
        error "Cannot find page '$page_name' to be included in page '${:name}'"
      }
    } else {
      set page [self]
    }
    return $page
  • return_redirect_without_params (scripted)

    #
    # Return to [xo::cc url], the current URL without query
    # parameters.
    #
    ::${:package_id} returnredirect  [:query_parameter return_url:localurl [ad_urlencode_folder_path [::xo::cc url]]]
  • reverse_map_party (scripted)

    # So far, we just handle users, but we should support parties in
    # the future as well.http://localhost:8003/nimawf/admin/export
    
    set email [expr {[dict exists $entry email] ? [dict get $entry email] : ""}]
    if {$email ne ""} {
      set id [party::get_by_email -email $email]
      if {$id ne ""} { return $id }
    }
    set username [expr {[dict exists $entry username] ? [dict get $entry username] : ""}]
    if {$username ne ""} {
      set id [acs_user::get_by_username -username $username]
      if {$id ne ""} { return $id }
    }
    set group_name [expr {[dict exists $entry group_name] ? [dict get $entry group_name] : ""}]
    if {$group_name ne ""} {
      set id [group::get_id -group_name $group_name]
      if {$id ne ""} { return $id }
    }
    
    if {$create_user_ids} {
      if {$group_name ne ""} {
        :log "+++ create a new group group_name=${group_name}"
        set group_id [group::new -group_name $group_name]
        group::update -group_id $group_id [list join_policy [dict get $entry join_policy]]
        ns_log notice "+++ reverse_party_map: we could add members [dict get $entry members] - but we don't"
        return $group_id
      } else {
        :log "+++ create a new user username=${username}, email=${email}"
        set status [auth::create_user -username $username -email $email  -first_names [dict get $entry first_names]  -last_name [dict get $entry last_name]  -screen_name [dict get $entry screen_name]  -url [dict get $entry url] -nologin]
        set creation_status [dict get $status creation_status]
        if {$creation_status eq "ok"} {
          return [dict get $status user_id]
        }
        :log "+++ create user username=${username}, email=${email} failed, reason=${creation_status}"
      }
    }
    return $default_party
  • reverse_map_party_attribute (scripted)

    if {![info exists :$attribute]} {
      set :$attribute $default_party
    } elseif {[llength [set :$attribute]] < 2} {
      set :$attribute $default_party
    } else {
      set :$attribute [:reverse_map_party  -entry [set :$attribute]  -default_party $default_party  -create_user_ids $create_user_ids]
    }
  • save (scripted)

    if {![:can_save]} {error "can't save this page under this parent"}
    ${:package_id} flush_page_fragment_cache
    set id [next]
    :check_unresolved_references
    return $id
  • save_data (scripted)

    #:log "-- [self args]"
    :unset_temporary_instance_variables
    set package_id ${:package_id}
    
    ::xo::dc transaction {
      #
      # If the newly created item was in production mode, but ordinary entries
      # are not, change on the first save the status to ready
      #
      #ns_log notice "----- save_data: old_name $old_name, is_new_entry [:is_new_entry $old_name] name <${:name}>"
      if {[:is_new_entry $old_name]} {
        if {![::$package_id get_parameter production_mode:boolean 0]} {
          set :publish_status "ready"
        }
      }
      :map_categories $category_ids
    
      #
      # Handle now further database operations that should be saved in
      # a transaction. Examples are calendar-items defined in a
      # FormPage, which should show up also in the calendar.
      #
      # Probably, categories should also be moved into the
      # transaction queue.
      #
      set queue ::__xowiki__transaction_queue(${:item_id})
      if {[info exists $queue]} {
        foreach cmd [set $queue] {
          #ns_log notice ".... executing transaction command: $cmd"
          {*}$cmd
        }
      }
    
      :save -use_given_publish_date $use_given_publish_date
      if {$old_name ne ${:name}} {
        :rename -old_name $old_name -new_name ${:name}
      }
      :notification_notify
    }
    return ${:item_id}
  • save_new (scripted)

     <instance of xowiki::Page[i]> save_new

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_nested_self_references nested_self_references (test ) xowiki::Page instproc save_new xowiki::Page instproc save_new test_nested_self_references->xowiki::Page instproc save_new test_xowiki xowiki (test ) test_xowiki->xowiki::Page instproc save_new

    Testcases:
    nested_self_references, xowiki
    if {![:can_save]} {error "can't save this page under this parent"}
    ${:package_id} flush_page_fragment_cache
    set id [next]
    :check_unresolved_references
    return $id
  • search_render (scripted)

    set :__no_form_page_footer 1
    set html [:render -update_references none]
    unset :__no_form_page_footer
    
    foreach tag {h1 h2 h3 h4 h5 b strong} {
      foreach {match words} [regexp -all -inline "<$tag>(\[^<\]+)</$tag>" $html] {
        foreach w [split $words] {
          if {$w eq ""} continue
          set word($w) 1
        }
      }
    }
    foreach tag [::xowiki::Page get_tags -package_id ${:package_id} -item_id ${:item_id}] {
      set word($tag) 1
    }
    #:log [list html $html keywords [array names word]]
    return [list mime text/html html $html keywords [array names word] text ""]
  • self_link_ids (scripted)

    set parent_id [expr {[info exists :__ignore_self_in_links] ? ${:parent_id} : [:physical_item_id]}]
    return [list package_id [:physical_package_id] parent_id $parent_id]
  • serialize_relocatable (scripted)

    if {[::package vcompare [package require xotcl::serializer] 2.1] > -1} {
      #
      # nsf 2.1 has support for specifying the target as argument of
      # the serialize method.
      #
      set content [:serialize -target [string trimleft [self] :]]
    } else {
      #
      # Since we serialize nx and XOTcl objects, make objects the
      # old-fashioned way relocatable. This is dangerous, since it
      # might substitute as well content.
      #
      set content [:serialize]
      #
      # The following statement drops the leading colons from the object
      # names such that the imported objects are inserted into the
      # current (rather than the global) namespace. Rather than the
      # global namespace. The approach is cruel, but backward compatible
      # and avoids potential name clashes with pre-existing objects.
      #
      # Replace the first occurrence of the object name (in the alloc/create
      # statement):
      #
      regsub { ::([0-9]+) } $content { \1 } content
    
      #
      # Replace leading occurrences of the object name (when e.g. procs
      # are as well exported as separate statements)
      #
      regsub -all -- {\n::([0-9]+) } $content "\n\\1 " content
    }
    return $content
  • set_content (scripted)

    :text [list [string map [list >> "\n&gt;&gt;" << "&lt;&lt;\n"]  [string trim $text " \n"]] text/html]
  • set_resolve_context (scripted)

    #
    # Push the last values to the stack
    #
    set stack_entry [list -package_id ${:package_id} -parent_id ${:parent_id} -item_id ${:item_id}]
    lappend :resolve_context_stack $stack_entry
    
    #
    # Reset the current values with the specified ones
    #
    if {${:parent_id} != $parent_id} {
      if {![info exists :physical_parent_id]} {
        set :physical_parent_id ${:parent_id}
      }
      set :parent_id $parent_id
    }
    if {${:package_id} != $package_id} {
      if {![info exists :physical_package_id]} {
        set :physical_package_id ${:package_id}
      }
      set :package_id $package_id
      #:msg "doing extra require on ${:physical_package_id}"
      #::xowiki::Package require ${:physical_package_id}
    }
    if {[info exists item_id] && ${:item_id} != $item_id} {
      if {![info exists :physical_item_id]} {
        set :physical_item_id ${:item_id}
      }
      set :item_id $item_id
    }
  • show_fields (scripted)

    # this method is for debugging only
    foreach f $form_fields { append msg "[$f name] [namespace tail [$f info class]], " }
    :msg $msg
    :log "form_fields: $msg"
  • stats_record_count (scripted)

    # This is a stub which can / should be overloaded in applications,
    # collecting statistics about certain usage pattern (e.g. exam
    # workflows).  This method is overloaded in xowf, and is here just
    # for safety reasons to avoid hard errors.
    ns_log error "the method Page->stats_record_count should not be called"
  • stats_record_detail (scripted)

    # This is a stub which can / should be overloaded in applications,
    # collecting statistics about certain usage pattern (e.g. exam
    # workflows).  This method is overloaded in xowf, and is here just
    # for safety reasons to avoid hard errors.
    ns_log error "the method Page->stats_record_detail should not be called"
  • substitute_markup (scripted)

     <instance of xowiki::Page[i]> substitute_markup

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_nested_self_references nested_self_references (test ) xowiki::Page instproc substitute_markup xowiki::Page instproc substitute_markup test_nested_self_references->xowiki::Page instproc substitute_markup test_xowiki xowiki (test ) test_xowiki->xowiki::Page instproc substitute_markup ad_enhanced_text_to_html ad_enhanced_text_to_html (public) xowiki::Page instproc substitute_markup->ad_enhanced_text_to_html ad_try ad_try (public) xowiki::Page instproc substitute_markup->ad_try

    Testcases:
    nested_self_references, xowiki
    if {${:mime_type} eq "text/enhanced"} {
      set content [ad_enhanced_text_to_html $content]
    }
    if {!${:do_substitutions}} {
      return $content
    }
    #
    # The provided content and the returned result are strings
    # containing HTML (unless we have other rich-text encodings).
    #
    # First get the potentially class specific regular expression
    # definitions.
    #
    set baseclass [expr {[[:info class] exists RE] ? [:info class] : [self class]}]
    $baseclass instvar RE markupmap
    #:log "-- baseclass for RE = $baseclass"
    
    #
    # Secondly, iterate line-wise over the text.
    #
    set output ""
    set l ""
    
    ad_try {
      if {$context_obj ne ""} {
        :set_resolve_context  -package_id [$context_obj package_id] -parent_id [$context_obj item_id]
        set :__ignore_self_in_links 1
      }
    
      foreach l0 [split $content \n] {
        append l [string map $markupmap(escape) $l0]
        if {[string first \{\{ $l] > -1 && [string first \}\} $l] == -1} {append l " "continue}
        set l [:regsub_eval $RE(anchor)  $l {:anchor  "\1""1"]
        set l [:regsub_eval $RE(div)     $l {:div     "\1"}]
        set l [:regsub_eval $RE(include) $l {:include_content "\1" "\2"}]
        #regsub -all -- $RE(clean) $l {\1} l
        regsub -all -- $RE(clean2) $l { \1} l
        set l [string map $markupmap(unescape) $l]
        append output $l \n
        set l ""
      }
    } on error {errorMsg} {
      error $errorMsg
    } finally {
      if {$context_obj ne ""} {
        unset :__ignore_self_in_links
        :reset_resolve_context
      }
    }
    #:log "--substitute_markup returns $output"
    return $output
  • text (setter)

  • title (setter)

  • translate (scripted)

    set langpair $from|$to
    set url http://translate.google.com/#$from/$to/$text
    set request [util::http::get -url $url]
    set status [dict get $request status]
    set data [expr {[dict exists $request page] ? [dict get $request page] : ""}]
    
    #:msg status=[$r set status]
    if {$status == 200} {
      #:msg data=$data
      dom parse -html -simple $data doc
      $doc documentElement root
      set n [$root selectNodes {//*[@id="result_box"]}]
      :msg "$text $from=>$to node '$n'"
      if {$n ne ""} {return [$n asText]}
    }
    util_user_message -message "Could not translate text,  status=$status"
    return "untranslated: $text"
  • unescape (scripted)

    # Some browsers change {{cmd -flag "..."}} into {{cmd -flag &quot;...&quot;}}
    # We have to change this back
    return [string map [list "&gt;" > "&lt;" < "&quot;" \" "&amp;" & "&semicolon;" {;} ] $string]
  • unset_temporary_instance_variables (scripted)

    #
    # Don't marshall/save/cache the following vars:
    #
    # array unset :__ia
    unset -nocomplain :__form_fields :__field_in_form  :__field_needed
  • update (scripted)

    ::xo::dc transaction {
      next
      :instvar object_id creator page_order publish_date description text title
      ::xo::dc dml update_xowiki_page {update xowiki_page
        set creator = :creator,page_order = :page_order,publish_date = :publish_date,description = :description,text = :text,title = :title where page_id = :object_id
      }
    }
  • update_publish_status (scripted)

    #
    # The publish_status of xowiki is used for "advertising"
    # pages. When the publish_status is e.g. in "production" or
    # "expired", users can access this object when they know obout its
    # existence (e.g. workflow assignments), but it is excluded from
    # listings, which contain - per default - only elements in
    # publish_status "ready".
    #
    # This proc can be used to change the publish status of a page and
    # handle visibility via syndication.
    #
    if {$new_publish_status ni {production ready live expired}} {
      error "update_publish_status receives invalid publish status '$new_publish_status'"
    }
    
    if {$new_publish_status ne ${:publish_status}} {
      :set_live_revision  -revision_id ${:revision_id}  -publish_status $new_publish_status
    
      ::xo::xotcl_object_cache flush ${:revision_id}
      ::xo::xotcl_object_cache flush ${:item_id}
    
      if {$new_publish_status eq "ready"} {
        ::xowiki::notification::do_notifications -revision_id ${:revision_id}
        ::xowiki::datasource -nocleanup ${:revision_id}
      } else {
        set revision_id ${:revision_id}
        ::xo::dc dml -prepare integer flush_syndication {delete from syndication where object_id = :revision_id}
      }
    }
  • validate=form_constraints (scripted)

    #
    # First check for invalid meta characters for security reasons.
    #
    #if {[regexp {[\[\]]} $form_constraints]} {
    #  :uplevel [list set errorMsg [_ xowiki.error-form_constraint-invalid_characters]]
    #  return 0
    #}
    #
    # Create from fields from all specs and report, if there are any errors
    #
    ad_try {
      :create_form_fields_from_form_constraints $form_constraints
    } on error {errorMsg} {
      ad_log error "error during form_constraints validator: $errorMsg"
      :uplevel [list set errorMsg $errorMsg]
      #:log "ERROR: invalid spec '$short_spec' for form field '$spec_name' -- $errorMsg"
      return 0
    }
    return 1
  • validate=form_input_fields (scripted)

    #
    # This is the form-level validator, which might be used to perform
    # validation based on e.g. multiple depending formfields.  The
    # validator can be used to test inter-dependencies between
    # form-fields and should set the error fields of the reporting
    # form field(s) via
    #
    #   $f error_msg "some error...."
    #
    # This method can be refined by e.g. a workflow.
    #
    return 1
  • validate=name (scripted)

    #:log "---- validate=name $name is called"
    upvar nls_language nls_language
    set success [::xowiki::validate_name [self]]
    if {$success} {
      set actual_length [string length $name]
      set max_length 400
      if {$actual_length > $max_length} {
        set errorMsg [_ acs-tcl.lt_name_is_too_long__Ple  [list name $name max_length $max_length actual_length $actual_length]]
        set success 0
      }
    } else {
      #
      # The plain form validation has signaled, that the name is not
      # ok. Try to provide a more detailed error message.
      #
      if {![:istype ::xowiki::File] && [regexp {^[a-zA-Z][a-zA-Z]:$} $name]} {
        set errorMsg [_ xowiki.Page-validate_name-invalid_name [list value $name]]
      } else {
        set errorMsg [_ xowiki.Page-validate_name-duplicate_item [list value $name]]
      }
    }
    
    if {$success} {
      #
      # Set the instance variable with a potentially prefixed
      # name. The classical validators (like xowiki::validate_name) do
      # just an upvar. Therefore, the "name" value is already
      # normalized and prefixed.
      #
      set :name $name
    } else {
      uplevel [list set errorMsg $errorMsg]
    }
    return $success
  • validate=page_order (scripted)

    if {[info exists :page_order]} {
      set page_order [string trim $value " ."]
      set :page_order $page_order
      # :log "validate=page_order '$value' -> '$page_order'"
      return [expr {![regexp {[^0-9a-zA-Z_.]} $page_order]}]
    }
    return 1
  • www-autosave-attribute (scripted, public)

     <instance of xowiki::Page[i]> www-autosave-attribute

    The web-callable method which is a simplified version of save-attributes, but which does NOT perform input validation, which might be a problem in case of partial input.

    Partial Call Graph (max 5 caller/called nodes):
    %3 ad_script_abort ad_script_abort (public) xowiki::Page instproc www-autosave-attribute xowiki::Page instproc www-autosave-attribute xowiki::Page instproc www-autosave-attribute->ad_script_abort

    Testcases:
    No testcase defined.
    
    set field_names [:field_names]
    #ns_log notice "[self] autosave-attribute called field-names: $field_names"
    set provided_form_parameters [xo::cc get_all_form_parameter]
    set keys [dict keys $provided_form_parameters]
    
    if {[llength $keys] == 1} {
      set key   [lindex $keys 0]
      set value [::xo::cc form_parameter $key]
      ns_log notice "[self] autosave-attribute save '$key' <$value>"
      set prefix ""
      regexp {^([^.]+)[.]} $key . prefix
    
      if {$prefix ne "" && $prefix in $field_names} {
        #
        # We are inside a compound field, which is saved in the instance
        # attributes.
        #
        #ns_log notice "SAVE old ia <${:instance_attributes}>"
        if {[dict exists ${:instance_attributes} $prefix]} {
          set innerDict [dict get ${:instance_attributes} $prefix]
        } else {
          set innerDict ""
        }
        dict set innerDict $key $value
        dict set :instance_attributes $prefix $innerDict
    
        #ns_log notice "SAVE new ia <${:instance_attributes}>"
        set s [:find_slot instance_attributes]
        :update_attribute_from_slot $s ${:instance_attributes}
        ns_return 200 text/plain ok
    
      } elseif {$prefix eq "" && $key in $field_names} {
        #
        # It is a plain attribute, either from the cr-attributes
        # (starting with an "_") or from the instance attributes.
        #
        if {[string match _* $key]} {
          set s [:find_slot [string range $key 1 end]]
          :update_attribute_from_slot $s $value
        } else {
          set s [:find_slot instance_attributes]
          dict set :instance_attributes $key $value
          :update_attribute_from_slot $s ${:instance_attributes}
        }
        ns_return 200 text/plain ok
    
      } else {
        ns_return 404 text/plain "not ok"
        ns_log error "autosave attribute: unexpected field name <$key>"  "(prefix '$prefix'), not contained in <$field_names> "  "value [llength $value] bytes"
      }
    } else {
      ns_log warning "autosave attribute: expecting a single form parameter with a prefix keys <$keys>"
      ns_return 404 text/plain "not ok"
    }
    ns_log notice "SAVE-att DONE"
    ad_script_abort
  • www-bulk-delete (scripted, public)

     <instance of xowiki::Page[i]> www-bulk-delete

    This web-callable method performs a bulk delete based on the object names provided by the form-variable "objects" and refresh then the caller page. This method is e.g. called by the folder-procs. By passing the "instantiate_p" one can decide whether each item should be instantiated (useful when the delete logic from the whole item ancestry is required), or if we will rely on the cheaper deletion at the package level. The default is false.

    Partial Call Graph (max 5 caller/called nodes):
    %3 security::csrf::validate security::csrf::validate (public) xowiki::Page instproc www-bulk-delete xowiki::Page instproc www-bulk-delete xowiki::Page instproc www-bulk-delete->security::csrf::validate

    Testcases:
    No testcase defined.
    ::security::csrf::validate
    
    if {![:exists_form_parameter "objects"]} {
      :msg "nothing to delete"
    }
    
    set instantiate_p [:form_parameter instantiate_p:boolean false]
    
    set item_ids [:get_ids_for_bulk_actions [:form_parameter objects]]
    foreach item_id $item_ids {
      :log "bulk-delete: DELETE item_id $item_id"
      if {$instantiate_p} {
        set i [::xo::db::CrClass get_instance_from_db -item_id $item_id]
        $i www-delete
      } else {
        ${:package_id} www-delete -item_id $item_id
      }
    }
    :return_redirect_without_params
  • www-clipboard-add (scripted, public)

     <instance of xowiki::Page[i]> www-clipboard-add

    This web-callable method adds elements to the clipboard based on the names provided by the form variable "objects". The objects are resolved below the current object, which is treated as containing folder. After adding elements to the clipboard, redirect either to the return_url of the calling page.

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

    Testcases:
    No testcase defined.
    if {![:exists_form_parameter "objects"] && [ns_conn method] eq "POST"} {
      :msg "nothing to copy"
    }
    
    ::xowiki::clipboard add [:get_ids_for_bulk_actions [:form_parameter objects]]
    #
    # When called via AJAX, we have reason to make a redirect.
    #
    if {[ns_set iget [ns_conn headers] "X-Requested-With"] eq "XMLHttpRequest"} {
      ns_log notice "HEADERS: got X-Requested-With"
      return OK
    } else {
      #ns_log notice "HEADERS: no X-Requested-With"
      :return_redirect_without_params
    }
  • www-clipboard-clear (scripted, public)

     <instance of xowiki::Page[i]> www-clipboard-clear

    This web-callable method clears the clibpboard contents. Finally redirect either to the return_url of the calling page.

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

    Testcases:
    No testcase defined.
    ::xowiki::clipboard clear
    :return_redirect_without_params
  • www-clipboard-content (scripted, public)

     <instance of xowiki::Page[i]> www-clipboard-content

    This web-callable method displays the content of the clipboard. Finally redirect either to the return_url of the calling page.

    Partial Call Graph (max 5 caller/called nodes):
    %3 util_user_message util_user_message (public) xowiki::Page instproc www-clipboard-content xowiki::Page instproc www-clipboard-content xowiki::Page instproc www-clipboard-content->util_user_message

    Testcases:
    No testcase defined.
    set clipboard [::xowiki::clipboard get]
    if {$clipboard eq ""} {
      util_user_message -message "Clipboard empty"
    } else {
      foreach item_id $clipboard {
        if {[::xo::db::CrClass get_instance_from_db -item_id $item_id] ne ""} {
          util_user_message -message [::$item_id pretty_link]
        } else {
          util_user_message -message "item $item_id deleted"
        }
      }
    }
    :return_redirect_without_params
  • www-clipboard-copy (scripted, public)

     <instance of xowiki::Page[i]> www-clipboard-copy

    This web-callable method copies the content of the clipboard to the current folder. After copying the elements from the clipboard, redirect either to the return_url of the calling page.

    Partial Call Graph (max 5 caller/called nodes):
    %3 ad_try ad_try (public) util_user_message util_user_message (public) xowiki::Page instproc www-clipboard-copy xowiki::Page instproc www-clipboard-copy xowiki::Page instproc www-clipboard-copy->ad_try xowiki::Page instproc www-clipboard-copy->util_user_message

    Testcases:
    No testcase defined.
    set clipboard [::xowiki::clipboard get]
    set item_ids [::xowiki::exporter include_needed_objects $clipboard]
    set content [::xowiki::exporter marshall_all -mode copy $item_ids]
    
    ad_try {
      namespace eval ::xo::import $content
    } on error {errMsg} {
      :msg "Error: $errMsg\n$::errorInfo"
      return
    }
    set folder_id [expr {[:is_folder_page] ? ${:item_id} : ${:parent_id}}]
    set msg [::${:package_id} import -replace 0 -create_user_ids 1  -parent_id $folder_id -objects $item_ids]
    util_user_message -html -message $msg
    ::xowiki::clipboard clear
    :return_redirect_without_params
  • www-clipboard-export (scripted, public)

     <instance of xowiki::Page[i]> www-clipboard-export

    This web-callable method exports the content of the clipboard in form of an xowiki dump. Then clear the clipboard and stop the script.

    Partial Call Graph (max 5 caller/called nodes):
    %3 ad_script_abort ad_script_abort (public) xowiki::Page instproc www-clipboard-export xowiki::Page instproc www-clipboard-export xowiki::Page instproc www-clipboard-export->ad_script_abort

    Testcases:
    No testcase defined.
    set clipboard [::xowiki::clipboard get]
    ::xowiki::exporter export $clipboard
    ns_conn close
    ::xowiki::clipboard clear
    ad_script_abort
  • www-create-new (scripted, public)

     <instance of xowiki::Page[i]> www-create-new \
        [ -parent_id parent_id ] [ -view_method view_method ] \
        [ -name name ] [ -nls_language nls_language ] \
        [ -publish_status publish_status ]

    This web-callable method creates a new page, typically an instance of a form page. The method accesses several form variables such as "__form_redirect", "__text_to_html", "last_page_id", "name", "nls_language", "package_id", "package_instance", "page_order", "parent_id", "publish_status", "source_item_id", "title" The call redirects either to the "__form_redirect" or to the created page.

    Switches:
    -parent_id
    (defaults to "0") (optional)
    -view_method
    (defaults to "edit") (optional)
    -name
    (optional)
    -nls_language
    (optional)
    -publish_status
    (optional)

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_create_composite_test_item create_composite_test_item (test xowf) xowiki::Page instproc www-create-new xowiki::Page instproc www-create-new test_create_composite_test_item->xowiki::Page instproc www-create-new test_create_folder_and_configure create_folder_and_configure (test xowiki) test_create_folder_and_configure->xowiki::Page instproc www-create-new test_create_form_with_form_instance create_form_with_form_instance (test xowiki) test_create_form_with_form_instance->xowiki::Page instproc www-create-new test_create_form_with_numeric create_form_with_numeric (test xowiki) test_create_form_with_numeric->xowiki::Page instproc www-create-new test_create_test_items create_test_items (test xowf) test_create_test_items->xowiki::Page instproc www-create-new _ _ (public) xowiki::Page instproc www-create-new->_ ad_return_complaint ad_return_complaint (public) xowiki::Page instproc www-create-new->ad_return_complaint ad_script_abort ad_script_abort (public) xowiki::Page instproc www-create-new->ad_script_abort ad_text_to_html ad_text_to_html (public) xowiki::Page instproc www-create-new->ad_text_to_html ad_try ad_try (public) xowiki::Page instproc www-create-new->ad_try

    Testcases:
    create_test_items, create_composite_test_item, xowiki_test_cases, create_form_with_form_instance, create_form_with_numeric, form_validate, create_folder_and_configure
    set original_package_id ${:package_id}
    
    if {[:exists_query_parameter "package_instance"]} {
      set package_instance [:query_parameter package_instance:localurl]
      #
      # Initialize the target package and set the variable package_id.
      #
      ad_try {
        ::xowiki::Package initialize  -url $package_instance -user_id [::xo::cc user_id]  -actual_query ""
      } on error {errorMsg} {
        ns_log error "Package initialize: $errorMsg\n$::errorInfo"
        return [::$original_package_id error_msg  "Page <b>'${:name}'</b> invalid provided package instance=$package_instance<p>$errorMsg</p>"]
      }
    }
    
    #
    # Collect some default values from query parameters.
    #
    set default_variables [list]
    foreach param {name title page_order:graph last_page_id:int32 nls_language:wordchar} {
      regexp {^([^:]+):?} $param . key
      if {[:exists_query_parameter $key]} {
        lappend default_variables $key [:query_parameter $param]
      }
    }
    
    # TODO: the following calls are here temporarily for posting
    # content from manually added forms (e.g. linear forum). The
    # following should be done:
    #  - create an includelet to create the form markup automatically
    #  - validate and transform input as usual
    # We should probably allow as well controlling auto-naming and
    # and prohibit empty postings.
    
    set text_to_html [:form_parameter "__text_to_html" ""]
    foreach key {_text _name} {
      if {[:exists_form_parameter $key]} {
        set __value [:form_parameter $key]
        if {$key in $text_to_html} {
          set __value [ad_text_to_html -- $__value]
        }
        lappend default_variables [string range $key 1 end] $__value
        switch -- $key {
          _name {set name $__value}
        }
      }
    }
    
    # Load the instance attributes from the form parameters
    set instance_attributes [list]
    foreach {_att _value} [::xo::cc get_all_form_parameter] {
      if {[string match _* $_att]} continue
      lappend instance_attributes $_att $_value
    }
    
    #
    # To create form_pages in different places than the form, one can
    # provide parent_id and package_id.
    #
    # The following construct is more complex than necessary to
    # provide backward compatibility. Note that the passed-in
    # parent_id has priority over the other measures to obtain it.
    #
    if {$parent_id == 0} {
      if {![info exists :parent_id]} {
        set :parent_id [::${:package_id} folder_id]
      }
      set fp_parent_id [:form_parameter parent_id [:query_parameter parent_id:int32 ${:parent_id}]]
    } else {
      set fp_parent_id $parent_id
    }
    #
    # Allow only inserts to own package.
    #
    if {![::xo::db::CrClass id_belongs_to_package -item_id $fp_parent_id -package_id ${:package_id}]} {
      ad_return_complaint 1 "invalid parent_id"
      ad_script_abort
    }
    
    # In case the Form is inherited and package_id was not specified, we
    # use the actual package_id.
    set fp_package_id [:form_parameter package_id [:query_parameter package_id:int32 ${:package_id}]]
    
    #
    # Handling publish_status. When the publish_status is provided via
    # query parameter, this has the highest priority. Otherwise use
    # the publish_status according to the production_mode. We control
    # this here explicitly, since when "name" is provided via query
    # variable, the default production/ready selection fails, and we
    # have to set the publish_status manually (see issue #3380).
    #
    if {$publish_status eq ""} {
      set publish_status [:query_parameter publish_status:wordchar ""]
    }
    if {$publish_status eq "" && [:exists_query_parameter name]} {
      if {[::${:package_id} get_parameter production_mode:boolean 0]} {
        set publish_status "production"
      } else {
        set publish_status "ready"
      }
      :log "FINAL publish_status $publish_status"
    }
    
    #
    # Provide "p.source" hook to configure pages by copying variables
    # from other pages (e.g. sitewide pages)
    #
    set source_item_id 0
    if {[:exists_query_parameter p.source]} {
      set source_page [:query_parameter p.source:token]
      set source_item_id [::${:package_id} lookup -use_site_wide_pages true -name $source_page]
    }
    if {$source_item_id == 0} {
      set source_item_id [:query_parameter source_item_id:int32 ""]
    }
    
    ::xo::Package require $fp_package_id
    set f [:create_form_page_instance  -name $name  -nls_language $nls_language  -parent_id $fp_parent_id  -package_id $fp_package_id  -default_variables $default_variables  -instance_attributes $instance_attributes  -source_item_id $source_item_id]
    
    if {$publish_status ne ""
        && $publish_status in {"production" "ready" "live" "expired"}
      } {
      $f publish_status $publish_status
    }
    
    #
    # Provide "p.configure" hook to programmatically configure pages
    #
    if {[:exists_query_parameter p.configure]} {
      set configure_method [:query_parameter p.configure:wordchar]
      if {[$f procsearch configure_page=$configure_method] ne ""} {
        #ns_log notice "call [$f procsearch configure_page=$configure_method] // [$f info class]"
        $f configure_page=$configure_method $name
      } else {
        ns_log notice "cannot find configure_page=$configure_method on [$f info precedence]"
      }
    }
    
    if {$name eq ""} {
      $f save_new
    } else {
      set id [::$fp_package_id lookup -parent_id $fp_parent_id -name $name]
      if {$id == 0} {
        $f save_new
      } else {
        ::xowiki::FormPage get_instance_from_db -item_id $id
        $f copy_content_vars -from_object $id
        $f item_id $id
        $f save
      }
    }
    
    $f notification_notify
    
    foreach var {return_url:localurl template_file title detail_link:localurl text} {
      regexp {^([^:]+):?} $var . key
      if {[:exists_query_parameter $key]} {
        set $key [:query_parameter $var]
        :log "set instance var from query param '$key' -> '[set $key]'"
      }
    }
    
    if {[info exists template_file]} {
      #
      # strip the leading "/" added by ns_normalizepath.
      #
      # TODO: check use-cases, with the restricted case actually still
      # makes sense.
      #
      set template_file [$fp_package_id normalizepath $template_file]
    }
    set form_redirect [:form_parameter "__form_redirect" ""]
    if {$form_redirect eq ""} {
      set form_redirect [$f pretty_link -query [export_vars {
        {m $view_method} return_url template_file title detail_link text
      }]]
    }
    ${:package_id} returnredirect $form_redirect
    set :package_id $original_package_id
  • www-create-or-use (scripted, public)

     <instance of xowiki::Page[i]> www-create-or-use \
        [ -parent_id parent_id ] [ -view_method view_method ] \
        [ -name name ] [ -nls_language nls_language ]

    This web-callable method calls www-create-new, unless overloaded from some other package, as done e.g. by xowf.

    Switches:
    -parent_id
    (defaults to "0") (optional)
    -view_method
    (defaults to "edit") (optional)
    -name
    (optional)
    -nls_language
    (optional)

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_create_test_items create_test_items (test xowf) xowiki::Page instproc www-create-or-use xowiki::Page instproc www-create-or-use test_create_test_items->xowiki::Page instproc www-create-or-use

    Testcases:
    create_test_items
    # can be overloaded
    :www-create-new  -parent_id $parent_id -view_method $view_method  -name $name -nls_language $nls_language
  • www-csv-dump (scripted, public)

     <instance of xowiki::Page[i]> www-csv-dump

    This web-callable method produces a CSV dump based on the includelet "form-usages".

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

    Testcases:
    No testcase defined.
    if {![:is_form]} {
      error "not called on a form"
    }
    set form_item_id ${:item_id}
    set items [::xowiki::FormPage get_form_entries  -base_item_ids $form_item_id -form_fields "" -initialize false  -publish_status all -package_id ${:package_id}]
    # collect all instances attributes of all items
    foreach i [$items children] {array set vars [$i set instance_attributes]}
    array set vars [list _name 1 _last_modified 1 _creation_user 1]
    set attributes [lsort -dictionary [array names vars]]
    #
    # Make sure, we the includelet honors the CSV generation
    #
    set includelet_key name:form-usages,form_item_ids:$form_item_id,field_names:[join $attributes " "],
    ::xo::cc set queryparm(includelet_key) [ns_base64urlencode $includelet_key]
    # call the includelet
    :www-view [:include [list form-usages -field_names $attributes  -extra_form_constraints _creation_user:numeric,format=%d  -form_item_id ${:item_id} -generate csv]]
  • www-delete (scripted, public)

     <instance of xowiki::Page[i]> www-delete [ -return_url return_url ]

    This web-callable method deletes a page via the delete method of the package.

    Switches:
    -return_url
    (optional)

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_xowiki_test_cases xowiki_test_cases (test xowiki) xowiki::Page instproc www-delete xowiki::Page instproc www-delete test_xowiki_test_cases->xowiki::Page instproc www-delete

    Testcases:
    xowiki_test_cases
    set returnUrlOpt [expr {[info exists return_url] ? [list -return_url $return_url] : ""}]
    
    # delete always via package
    ${:package_id} www-delete -item_id ${:item_id} -name ${:name} {*}$returnUrlOpt
  • www-delete-revision (scripted, public)

     <instance of xowiki::Page[i]> www-delete-revision

    This web-callable method deletes a single revision of a Page, which is actually performed by the "delete_revision" method of the package, which is responsible for caching. Since we instantiate the Page based on the "revision_id" query parameter, it is sufficient to delete here just based on the current instance variable of the revision_id.

    Partial Call Graph (max 5 caller/called nodes):
    %3 export_vars export_vars (public) xowiki::url xowiki::url (private) xowiki::Page instproc www-delete-revision xowiki::Page instproc www-delete-revision xowiki::Page instproc www-delete-revision->export_vars xowiki::Page instproc www-delete-revision->xowiki::url

    Testcases:
    No testcase defined.
    set item_id ${:item_id}
    ::xo::dc 1row -prepare integer get_revision {
      select latest_revision,live_revision from cr_items where item_id = :item_id
    }
    
    # do real deletion via package
    ${:package_id} delete_revision -revision_id ${:revision_id} -item_id $item_id
    
    if {$live_revision == ${:revision_id}} {
      # latest revision might have changed by delete_revision, so we have to fetch here
      xo::dc 1row -prepare integer get_revision {select latest_revision from cr_items where item_id = :item_id}
      if {$latest_revision eq ""} {
        # we are out of luck, this was the final revision, delete the item
        ${:package_id} delete -name ${:name} -item_id $item_id
      } else {
        # Fetch fresh instance from db so that we have actual values
        # from the latest revision for e.g. the update of the
        # item_index.
        set page [::xo::db::CrClass get_instance_from_db -item_id $item_id -revision_id $latest_revision]
        $page set_live_revision -revision_id $latest_revision
      }
    }
    if {$latest_revision ne ""} {
      # otherwise, "delete" did already the redirect
      ${:package_id} returnredirect [:query_parameter return_url:localurl  [export_vars -base [${:package_id} url] {{m revisions}}]]
    }
  • www-diff (scripted, public)

     <instance of xowiki::Page[i]> www-diff

    This web-callable method produces a "diff" of two pages based on the current page and the revision_id provided as query parameter by "compare_revision_id". We can choose here between the more fancy "::util::html_diff" and a plain text diff. The latter is used, when the query variable "plain_text_diff" is provided, or when the fancy diff raises an exception.

    Partial Call Graph (max 5 caller/called nodes):
    %3 ad_html_text_convert ad_html_text_convert (public) ad_try ad_try (public) util::html_diff util::html_diff (public) xo::get_user_name xo::get_user_name xowiki::text_diff_in_html xowiki::text_diff_in_html (private) xowiki::Page instproc www-diff xowiki::Page instproc www-diff xowiki::Page instproc www-diff->ad_html_text_convert xowiki::Page instproc www-diff->ad_try xowiki::Page instproc www-diff->util::html_diff xowiki::Page instproc www-diff->xo::get_user_name xowiki::Page instproc www-diff->xowiki::text_diff_in_html

    Testcases:
    No testcase defined.
    
    set compare_id [:query_parameter compare_revision_id:int32 0]
    if {$compare_id == 0} {
      return ""
    }
    ::xo::Page requireCSS urn:ad:css:xowiki-[::xowiki::CSS toolkit]
    
    set my_page [::xowiki::Package instantiate_page_from_id -revision_id ${:revision_id}]
    
    ad_try {
      set html1 [$my_page render]
    } on error {errorMsg} {
      set html1 "Error rendering ${:revision_id}$errorMsg"
    }
    set user1 [::xo::get_user_name [$my_page set creation_user]]
    set time1 [$my_page set creation_date]
    set revision_id1 [$my_page set revision_id]
    regexp {^([^.]+)[.]} $time1 _ time1
    
    set other_page [::xowiki::Package instantiate_page_from_id -revision_id $compare_id]
    $other_page volatile
    #$other_page absolute_links 1
    
    ad_try {
      set html2 [$other_page render]
    } on error {errorMsg} {
      set html2 "Error rendering $compare_id: $errorMsg"
    }
    set user2 [::xo::get_user_name [$other_page set creation_user]]
    set time2 [$other_page set creation_date]
    set revision_id2 [$other_page set revision_id]
    regexp {^([^.]+)[.]} $time2 _ time2
    
    set title "Differences for ${:name}"
    set context [list $title]
    
    if {![:exists_query_parameter plain_text_diff]} {
      #
      # try util::html diff if it is available and works
      #
      ad_try {
        set content [::util::html_diff -old $html2 -new $html1 -show_old_p t]
      } on error {errMsg} {
        ns_log notice "::util::html_diff failed on comparing page ${:name}, revisions_id ${:revision_id} and $compare_id"
      }
    }
    
    if {![info exists content]} {
      #
      # If the fist attempt failed, or the plain text based diff was
      # desired, fall back to proven plain text based diff
      #
      set text1 [ad_html_text_convert -from text/html -to text/plain -- $html1]
      set text2 [ad_html_text_convert -from text/html -to text/plain -- $html2]
      set content [::xowiki::text_diff_in_html $text2 $text1]
    }
    
    ::xo::Page set_property doc title $title
    array set property_doc [::xo::Page get_property doc]
    ::xo::Page header_stuff
    
    ${:package_id} return_page -adp /packages/xowiki/www/diff -variables {
      content title context
      time1 time2 user1 user2 revision_id1 revision_id2 property_doc
    }
  • www-duplicate (scripted, public)

     <instance of xowiki::Page[i]> www-duplicate

    This web-callable method duplicated the current object. It uses the same mechanisms as the clipboard-copy operation. After adding elements to the clipboard, redirect either to the return_url of the calling page (as handled by www-clipboard-copy)

    Partial Call Graph (max 5 caller/called nodes):
    %3 ad_try ad_try (public) xowiki::Page instproc www-duplicate xowiki::Page instproc www-duplicate xowiki::Page instproc www-duplicate->ad_try

    Testcases:
    No testcase defined.
    ::xowiki::clipboard clear
    ::xowiki::clipboard add [list ${:item_id}]
    
    if {![regexp {^(.*[-]copy-)\d+} ${:name} . stem]} {
      set stem ${:name}-copy-
    }
    set new_name [::xowiki::autoname new -name $stem -parent_id ${:package_id}]
    set old_name ${:name}
    
    ad_try {
      set :name $new_name
      #
      # Call whatever clipboard-copy is doing....
      #
      :www-clipboard-copy
    
    } finally {
      #
      # Restore the actual object
      #
      set :name $old_name
      #
      # To be on the save side, flush the cache
      #
      ::xo::xotcl_object_cache flush ${:item_id}
    }
  • www-edit (scripted, public)

     <instance of xowiki::Page[i]> www-edit [ -new on|off ] \
        [ -autoname on|off ] [ -validation_errors validation_errors ]

    This web-callable method renders a page in "edit" mode (i.e. provide input fields). This is the old-style edit based on the old-style xowiki-form-procs. FormPages should be used when possible for better user experience.

    Switches:
    -new
    (boolean) (defaults to "false") (optional)
    is this an edit-new operation?
    -autoname
    (boolean) (defaults to "false") (optional)
    value to be passed to getFormClass
    -validation_errors
    (optional)
    ignored in this class, but used for compatibility with FormPage.www-edit

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_create_form_with_form_instance create_form_with_form_instance (test xowiki) xowiki::Page instproc www-edit xowiki::Page instproc www-edit test_create_form_with_form_instance->xowiki::Page instproc www-edit test_create_form_with_numeric create_form_with_numeric (test xowiki) test_create_form_with_numeric->xowiki::Page instproc www-edit test_form_validate form_validate (test xowiki) test_form_validate->xowiki::Page instproc www-edit test_xowiki_test_cases xowiki_test_cases (test xowiki) test_xowiki_test_cases->xowiki::Page instproc www-edit export_vars export_vars (public) xowiki::Page instproc www-edit->export_vars template::util::lpop template::util::lpop (public) xowiki::Page instproc www-edit->template::util::lpop

    Testcases:
    xowiki_test_cases, create_form_with_form_instance, create_form_with_numeric, form_validate
    #
    # We have to keep the instvar for "item_id" for the time being.
    #
    :instvar item_id
    
    #:log "--edit new=$new autoname=$autoname, validation_errors=$validation_errors, parent=${:parent_id}"
    :edit_set_default_values
    set fs_folder_id [:edit_set_file_selector_folder]
    
    if {[::${:package_id} exists_query_parameter "return_url"]} {
      set submit_link [:query_parameter return_url:localurl]
      set return_url $submit_link
    } else {
      #
      # Before we used "." as default submit link (resulting in a
      # "ad_returnredirect .").  However, this does not seem to work
      # in case we have folders in use....
      #
      #set submit_link "."
      set submit_link [:pretty_link]
    }
    #:log "--u submit_link=$submit_link qp=[:query_parameter return_url]"
    set object_type [:info class]
    
    # We have to do template mangling here; ad_form_template writes
    # form variables into the actual parse-level, so we have to be in
    # our own level in order to access and pass these.
    #
    lappend ::template::parse_level [info level]
    
    set action_vars [expr {$new ? "{edit-new 1} object_type return_url" : "{m edit} return_url"}]
    #:log "--formclass=[$object_type getFormClass -data [self]] object_type=$object_type"
    
    #
    # Determine the package_id of some mounted xowiki instance to find
    # the directory + URL, from where the scripts called from Xinha
    # can be used.
    #
    if {[::${:package_id} info class] eq "::xowiki::Package"} {
      #
      # The actual instance is a plain xowiki instance, we can use it.
      #
      set folder_spec [list script_dir [::${:package_id} package_url]]
    } else {
      #
      # The actual instance is not a plain xowiki instance, so, we try
      # to find one, where the current user has at least read
      # permissions.  This act is required for sub-packages, which
      # might not have the script dir.
      #
      set first_instance_id [::xowiki::Package first_instance  -party_id [::xo::cc user_id]  -privilege read]
      if {$first_instance_id ne ""} {
        ::xowiki::Package require $first_instance_id
        set folder_spec [list script_dir [::$first_instance_id package_url]]
      }
    }
    
    if {$fs_folder_id ne ""} {
      lappend folder_spec folder_id $fs_folder_id
    }
    
    [$object_type getFormClass -data [self]] create ::xowiki::f1 -volatile  -action  [export_vars -base [::${:package_id} url] $action_vars]  -data [self]  -folderspec $folder_spec  -submit_link $submit_link  -autoname $autoname
    #:log "form created"
    
    #
    # The variable "item_id" is hard-wired in the old-style "generate"
    # method.
    #
    if {[info exists return_url]} {
      ::xowiki::f1 generate -export [list [list return_url $return_url]]
    } else {
      ::xowiki::f1 generate
    }
    #:log "form rendered"
    ::xowiki::f1 instvar edit_form_page_title context formTemplate
    
    if {[info exists item_id]} {
      set rev_link  [::${:package_id} make_link [self] revisions]
      set view_link [::${:package_id} make_link [self] view]
    }
    if {[info exists last_page_id]} {
      set back_link [::${:package_id} url]
    }
    
    set index_link  [::${:package_id} make_link -privilege public ${:package_id}]
    ::xo::Page set_property doc title "[::${:package_id} instance_name] - $edit_form_page_title"
    
    array set property_doc [::xo::Page get_property doc]
    set edit_tmpl [::${:package_id} get_adp_template "edit"]
    
    set html [::${:package_id} return_page -adp $edit_tmpl  -form f1  -variables {
                    item_id {parent_id ${:parent_id}}
                    edit_form_page_title context formTemplate
                    view_link back_link rev_link index_link property_doc
                  }]
    template::util::lpop ::template::parse_level
    #:log "--edit html length [string length $html]"
    return $html
  • www-list (scripted, public)

     <instance of xowiki::Page[i]> www-list

    This web-callable method provides a listing of pages. When the query parameter "children" is used, it returns the children of this item via the "child-resources" includelet. Otherwise, when this method is called on any kind of Form, it returns the form instances via the "form-usages" includelet. Otherwise, when this method is called on any kind of folder pages, it returns the elements of this folder via the "child-resources" includelet. If the above fails, it redirects to the starting page.

    Partial Call Graph (max 5 caller/called nodes):
    %3 ad_return_url ad_return_url (public) xowiki::Page instproc www-list xowiki::Page instproc www-list xowiki::Page instproc www-list->ad_return_url

    Testcases:
    No testcase defined.
    if {[:is_form] && ![:exists_query_parameter children]} {
      #
      # The following line is here to provide a short description for
      # larger form-usages (a few MB) where otherwise
      # "ad_html_text_convert" in Page.get_description tend to use
      # forever (at least in Tcl 8.5)
      #
      set :description "form-usages for ${:name} ${:title}"
    
      return [:www-view [:include [list form-usages -form_item_id ${:item_id}]]]
    }
    if {[:is_folder_page] || [:exists_query_parameter children]} {
      return [:www-view [:include [list child-resources -publish_status all]]]
    }
    #:msg "method list undefined for this kind of object"
    ${:package_id} returnredirect [ad_return_url]
  • www-make-live-revision (scripted, public)

     <instance of xowiki::Page[i]> www-make-live-revision

    This web-callable method makes the revision specified by parameter "revision_id" the live revision, or when this is not available, the parameter "local_return_url".

    Partial Call Graph (max 5 caller/called nodes):
    %3 export_vars export_vars (public) xowiki::Page instproc www-make-live-revision xowiki::Page instproc www-make-live-revision xowiki::Page instproc www-make-live-revision->export_vars

    Testcases:
    No testcase defined.
    set page_id [:query_parameter revision_id]
    if {[string is integer -strict $page_id]} {
      set revision_id $page_id
    } else {
      set revision_id ${:revision_id}
    }
    #:log "--M set_live_revision $revision_id"
    :set_live_revision -revision_id $revision_id
    ${:package_id} returnredirect [${:package_id} query_parameter_return_url  [export_vars -base [::${:package_id} url] {{m revisions}}]]
  • www-popular-tags (scripted, public)

     <instance of xowiki::Page[i]> www-popular-tags

    AJAX called function, returns an HTML snippet with the popular tags.

    Partial Call Graph (max 5 caller/called nodes):
    %3 _ _ (public) ad_script_abort ad_script_abort (public) ad_urlencode ad_urlencode (public) xowiki::Page instproc www-popular-tags xowiki::Page instproc www-popular-tags xowiki::Page instproc www-popular-tags->_ xowiki::Page instproc www-popular-tags->ad_script_abort xowiki::Page instproc www-popular-tags->ad_urlencode

    Testcases:
    No testcase defined.
    set package     ::${:package_id}
    set limit       [:query_parameter limit:int32 20]
    set weblog_page [$package get_parameter weblog_page:graph weblog]
    set href        [$package pretty_link -parent_id [$package folder_id] $weblog_page]?summary=1
    
    set entries [list]
    xo::dc foreach get_popular_tags  [::xo::dc select  -vars "count(*) as nr, tag"  -from "xowiki_tags"  -where "item_id = [ns_dbquotevalue ${:item_id}]"  -groupby "tag"  -orderby "nr"  -limit $limit] {
               set label [ns_quotehtml "$tag ($nr)"]
               lappend entries "<a href='[ns_quotehtml $href&ptag=[ad_urlencode $tag]]'>$label</a>"
             }
    ns_return 200 text/html "<span class='popular-tags'>[_ xowiki.popular_tags_label]: [join $entries {, }]</span>"
    ad_script_abort
  • www-revisions (scripted, public)

     <instance of xowiki::Page[i]> www-revisions

    This web-callable method lists the revisions based. The rendering is actually performed in the cr-procs, but can overloaded per package.

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_xowiki_test_cases xowiki_test_cases (test xowiki) xowiki::Page instproc www-revisions xowiki::Page instproc www-revisions test_xowiki_test_cases->xowiki::Page instproc www-revisions

    Testcases:
    xowiki_test_cases
    #set context [list [list [::${:package_id} url] ${:name} ] [_ xotcl-core.revisions]]
    #set title "[_ xotcl-core.revision_title] '${:name}'"
    return [:www-view [next]]
  • www-save-attributes (scripted, public)

     <instance of xowiki::Page[i]> www-save-attributes

    The web-callable method save-attributes is typically callable over the REST interface. It allows one to save attributes of a page without adding a new revision.

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

    Testcases:
    No testcase defined.
    set field_names [:field_names]
    set form_fields [list]
    set query_field_names [list]
    
    set validation_errors 0
    foreach field_name $field_names {
      if {[::xo::cc exists_form_parameter $field_name]} {
        lappend form_fields [:create_form_field $field_name]
        lappend query_field_names $field_name
      }
    }
    #:show_fields $form_fields
    lassign [:get_form_data -field_names $query_field_names $form_fields] validation_errors category_ids
    
    if {$validation_errors == 0} {
      #
      # We have no validation errors, so we can save the content.
      #
      set update_without_revision [::${:package_id} query_parameter replace:boolean 0]
    
      foreach form_field $form_fields {
        #
        # Fix richtext content in accordance with oacs conventions.
        #
        if {[$form_field istype ::xowiki::formfield::richtext]} {
          $form_field value [list [$form_field value] text/html]
        }
      }
      if {$update_without_revision} {
        # field-wise update without revision
        set update_instance_attributes 0
        foreach form_field $form_fields {
          set s [$form_field slot]
          if {$s eq ""} {
            #
            # Empty slot means that we have an instance_attribute; we
            # save all in one statement below.
            #
            set update_instance_attributes 1
          } else {
            error "Not implemented yet"
            :update_attribute_from_slot $s [$form_field value]
          }
        }
        if {$update_instance_attributes} {
          set s [:find_slot instance_attributes]
          :update_attribute_from_slot $s [:instance_attributes]
        }
      } else {
        #
        # Perform standard update (with revision).
        #
        :save_data  -use_given_publish_date [expr {"_publish_date" in $field_names}]  [::xo::cc form_parameter __object_name:signed,convert ""$category_ids
      }
      ${:package_id} returnredirect  [:query_parameter return_url:localurl [:pretty_link]]
      return
    } else {
      # TODO: handle errors in a user friendly way
      ns_log warning "www-save-attributes: we have $validation_errors validation_errors"
    }
    ${:package_id} returnredirect  [:query_parameter return_url:localurl [:pretty_link]]
  • www-save-tags (scripted, public)

     <instance of xowiki::Page[i]> www-save-tags

    This web-callable method saves tags (provided via form parameter "new_tags").

    Partial Call Graph (max 5 caller/called nodes):
    %3 ad_return_url ad_return_url (public) xowiki::Page instproc www-save-tags xowiki::Page instproc www-save-tags xowiki::Page instproc www-save-tags->ad_return_url

    Testcases:
    No testcase defined.
    ::xowiki::Page save_tags  -user_id [::xo::cc user_id]  -item_id ${:item_id}  -revision_id ${:revision_id}  -package_id ${:package_id}  [:form_parameter new_tags]
    
    ::${:package_id} returnredirect  [:query_parameter return_url:localurl [ad_return_url]]
  • www-toggle-publish-status (scripted, public)

     <instance of xowiki::Page[i]> www-toggle-publish-status \
        [ -return_url return_url ]

    This web-callable method toggles from "production" to "ready", and from "ready" or "archived" to "production". The return_url can be passed in for cases, where some proc calls internally this function, since update_publish_status might have to initialize some related objects, which might modify the return_url as well (e.g., workflows with specialized return_url handling).

    Switches:
    -return_url
    (optional)

    Partial Call Graph (max 5 caller/called nodes):
    %3 ad_return_url ad_return_url (public) xowiki::Page instproc www-toggle-publish-status xowiki::Page instproc www-toggle-publish-status xowiki::Page instproc www-toggle-publish-status->ad_return_url

    Testcases:
    No testcase defined.
    if {![info exists return_url]} {
      set return_url [:query_parameter return_url:localurl [ad_return_url]]
    }
    if {${:publish_status} ne "ready"} {
      set new_publish_status "ready"
    } else {
      set new_publish_status "production"
    }
    :update_publish_status $new_publish_status
    ${:package_id} returnredirect $return_url
  • www-validate-attribute (scripted, public)

     <instance of xowiki::Page[i]> www-validate-attribute

    This web-callable method can be used to validate form attributes, typically called via AJAX.

    Partial Call Graph (max 5 caller/called nodes):
    %3 xo::localize xo::localize xowiki::Page instproc www-validate-attribute xowiki::Page instproc www-validate-attribute xowiki::Page instproc www-validate-attribute->xo::localize

    Testcases:
    No testcase defined.
    set field_names [:field_names]
    set validation_errors 0
    
    #
    # Get the first transmitted form field.
    #
    foreach field_name $field_names {
      if {[::xo::cc exists_form_parameter $field_name]} {
        set form_fields [:create_form_field $field_name]
        set query_field_names $field_name
        break
      }
    }
    lassign [:get_form_data -field_names $query_field_names $form_fields]  validation_errors category_ids
    set error ""
    if {$validation_errors == 0} {
      set status_code 200
    } else {
      set status_code 406
      foreach f $form_fields {
        if {[$f error_msg] ne ""} {
          set error [::xo::localize [$f error_msg] 1]
        }
      }
    }
    ns_return $status_code text/html $error
  • www-view (scripted, public)

     <instance of xowiki::Page[i]> www-view [ content ]

    This web-callable method is called when viewing wiki content. The method "view" is used primarily as web API call, when the xowiki page is viewed. It is not intended for e.g. embedded wiki pages (use includes), since it contains full framing, etc. In most cases, the argument "content" is not provided, and it is computed via the "render" method of the current object. It is as well possible to reuse the rendering logic of the method for other pages, where some HTML content is already computed, but it should be viewed exactly as in the page viewing cases.

    Parameters:
    content (optional)

    Partial Call Graph (max 5 caller/called nodes):
    %3 test_xowiki_test_cases xowiki_test_cases (test xowiki) xowiki::Page instproc www-view xowiki::Page instproc www-view test_xowiki_test_cases->xowiki::Page instproc www-view ad_return_complaint ad_return_complaint (public) xowiki::Page instproc www-view->ad_return_complaint ad_return_url ad_return_url (public) xowiki::Page instproc www-view->ad_return_url ad_script_abort ad_script_abort (public) xowiki::Page instproc www-view->ad_script_abort ad_try ad_try (public) xowiki::Page instproc www-view->ad_try export_vars export_vars (public) xowiki::Page instproc www-view->export_vars

    Testcases:
    xowiki_test_cases
    #ns_log notice "www-view <$content>"
    
    #
    # The recursion_count os maintained to avoid recursive includes
    # inside a page.
    #
    ::xowiki::Page set recursion_count 0
    set page_package_id    ${:package_id}
    set context_package_id [::xo::cc package_id]
    set folder_id [::$page_package_id folder_id]
    
    #:msg "page_package_id=$page_package_id, context_package_id=$context_package_id"
    
    set template_file [ns_normalizepath [:query_parameter "template_file"  [::$context_package_id get_parameter template_file:graph view-default]]]
    if {[nsf::is object ::xowiki::$template_file]} {
      $template_file before_render [self]
    }
    
    #
    # Set up template variables.
    #
    set object_type [::$page_package_id get_parameter object_type:graph [:info class]]
    set rev_link    [::$page_package_id make_link [self] revisions]
    
    if {[::$context_package_id query_parameter m:token ""] eq "edit"} {
      set view_link [::$page_package_id make_link [self] view return_url]
      set edit_link ""
    } else {
      set edit_link [::$page_package_id make_link [self] edit return_url]
      set view_link ""
    }
    
    set delete_link [::$page_package_id make_link [self] delete return_url]
    if {[info exists :__link(new)]} {
      set new_link [set :__link(new)]
    } else {
      set new_link [:new_link $page_package_id]
    }
    
    set admin_link  [::$context_package_id make_link -privilege admin -link admin/ ::$context_package_id]
    set index_link  [::$context_package_id make_link -privilege public ::$context_package_id]
    set view_link   [::$page_package_id make_link [self] view return_url]
    
    set notification_subscribe_link ""
    if {[::$context_package_id get_parameter with_notifications:boolean 1]} {
      if {[::xo::cc user_id] != 0} {
        #
        # Notifications are only be displayed for logged-in users.
        #
        set notifications_return_url [expr {[info exists return_url] ? $return_url : [ad_return_url]}]
        set notification_type [notification::type::get_type_id -short_name xowiki_notif]
        set notification_text "Subscribe to [::$context_package_id instance_name]"
        set notification_subscribe_link  [export_vars -base /notifications/request-new  {{return_url $notifications_return_url}
                   {pretty_name $notification_text}
                   {type_id $notification_type}
                   {object_id $context_package_id}}]
        set notification_image "<adp:icon name='envelope' title='[ns_quotehtml $notification_text]'>"
      }
    }
    
    #
    # The content may be passed by other methods (e.g. edit) to
    # make use of the same templating machinery below.
    #
    if {$content eq ""} {
      set content [:content_header_get][:render]
      #:msg "--after render"
    } else {
      set content [:content_header_get]$content
    }
    #set content [::xowiki::adp_parse_tags $content]
    
    #
    # These variables can be influenced via set-parameter.
    #
    set autoname [::$page_package_id get_parameter autoname:boolean 0]
    
    #
    # Setup top includeletes and footers.
    #
    
    set footer [:htmlFooter -content $content]
    set top_includelets ""
    set vp [string trim [::$context_package_id get_parameter top_includelet ""]]
    if {$vp ne "" && $vp ne "none"} {
      set top_includelets [:include $vp]
    }
    
    if {[::$context_package_id get_parameter MenuBar:boolean 0]} {
      #
      # When a "MenuBar" is used, it might contain folder-specific
      # content. Therefore, we have to compute the tree. The resulting
      # HTML code can be placed via adp templates differently (or it
      # can be ignored).
    
      set folderhtml [:include {folders -style folders}]
      ::xo::Page set_property body folderHTML $folderhtml
      # TODO: there should be no need to pass manually folderhtml,
      # use the property instead
    
      #
      # At this place, the menu should be complete, we can render it.
      #
      set mb [::xowiki::MenuBar info instances -closure]
      if {$mb ne ""} {
        set mbHTML [$mb render-preferred]
        ::xo::Page set_property body menubarHTML $mbHTML
      }
    }
    
    if {[::$context_package_id get_parameter with_user_tracking:boolean 1]} {
      :record_last_visited
    }
    
    #
    # Deal with the views package (many thanks to Malte for this
    # snippet!)
    #
    if {[::$context_package_id get_parameter with_views_package_if_available:boolean 1]
        && [info commands ::views::record_view] ne ""} {
      views::record_view -object_id ${:item_id} -viewer_id [::xo::cc user_id]
      array set views_data [views::get -object_id ${:item_id}]
    }
    
    if {[:exists_query_parameter return_url]} {
      set return_url [:query_parameter return_url:localurl]
    }
    
    #:log "--after notifications [info exists notification_image]"
    
    set master [::$context_package_id get_parameter master:boolean 1]
    if {![string is boolean -strict $master]} {
      ad_return_complaint 1 "value of master is not boolean"
      ad_script_abort
    }
    
    if {$master} {
      set context [list ${:title}]
      #:msg "$context_package_id title=[::$context_package_id instance_name] - ${:title}"
      #:msg "::xo::cc package_id = [::xo::cc package_id]  ::xo::cc url= [::xo::cc url] "
      ::xo::Page set_property doc title "[::$context_package_id instance_name] - ${:title}"
      ::xo::Page set_property body title ${:title}
    
      # We could offer a user to translate the current page to his preferred language
      #
      # set create_in_req_locale_link ""
      # if {[::$context_package_id get_parameter use_connection_locale:boolean 0]} {
      #  $context_package_id get_lang_and_name -path [::$context_package_id set object] req_lang req_local_name
      #  set default_lang [::$page_package_id default_language]
      #  if {$req_lang ne $default_lang} {
      #      set l [Link create new -destroy_on_cleanup  #             -page [self] -type language -stripped_name $req_local_name  #             -name ${default_lang}:$req_local_name -lang $default_lang  #             -label $req_local_name -parent_id ${:parent_id} -item_id 0  #                 -package_id $context_package_id -init  #             -return_only undefined]
      #      $l render
      #   }
      # }
    
      #:log "--after context delete_link=$delete_link "
      #set template [::$context_package_id get_parameter template ""]
      set template ""
      set page [self]
    
      foreach css [::$context_package_id get_parameter extra_css ""] {
        ::xo::Page requireCSS -order 10 $css
      }
    
      #
      # Refetch "template_file", since it might have been changed via
      # set-parameter. The cache-flush (next line) is not pretty here
      # and should be supported from xotcl-core.
      #
      ::xo::cc unset -nocomplain cache([list $context_package_id get_parameter template_file:graph])
      set template_file [:query_parameter "template_file"  [::$context_package_id get_parameter template_file:graph view-default]]
      #
      # If the template_file does not have a path, assume it in the
      # standard location.
      #
      if {[string range $template_file 0 0] eq "/"} {
        ns_log warning "ignore template as specified in parameter 'template_file'"  "on non-standard location: $template_file. The template should be"  " under\n/packages/[${:package_id} package_key]/resources/templates/..."
        set template_file [::$context_package_id get_parameter  -check_query_parameter false  -nocache  template_file view-default]
      }
      set validated_template_file [::${:package_id} get_adp_template $template_file]
      if {$validated_template_file eq ""} {
        ns_log error "invalid template specified in parameter 'template_file': '$template_file'"
      }
      set template_file $validated_template_file
    
      # Force xowiki*.css to be loaded first(ish), so we can override
      # its styling via the theme (e.g. different buttons...). This
      # uses the "template::head" API directly, since resources from
      # requireCSS are typically loaded later than those from the theme.
    
      template::head::add_css  -href urn:ad:css:xowiki-[::xowiki::CSS toolkit]  -order 0
    
      #
      # Popular tags handling (should probably go to includelets)
      #
      if {$footer ne ""} {
        template::add_body_script -script {
          function get_popular_tags(popular_tags_link, prefix) {
            var http = new XMLHttpRequest();
            http.open('GET', popular_tags_link, true);
            http.onreadystatechange = function() {
              if (http.readyState == 4) {
                if (http.status != 200) {
                  alert('Something wrong in HTTP request, status code = ' + http.status);
                } else {
                  var e = document.getElementById(prefix + '-popular_tags');
                  e.innerHTML = http.responseText;
                  e.style.display = 'block';
                }
              }
            };
            http.send(null);
          }
        }
      }
    
      #
      # The method header_stuff performs the required
      # template::head::add_script and template::head::add_css
      # etc. operations
      #
      ::xo::Page header_stuff
    
      if {![info exists :description]} {
        set :description [:get_description $content]
      }
    
      if {[info commands ::template::head::add_meta] ne ""} {
        #set meta(language) [:lang]
        ::xo::Page set_property doc title_lang [:lang]
        set meta(description) [:description]
        set meta(keywords) ""
        if {[:istype ::xowiki::FormPage]} {
          set meta(keywords) [string trim [:property keywords]]
          if {[:property html_title] ne ""} {
            ::xo::Page set_property doc title [:property html_title]
          }
        }
        if {$meta(keywords) eq ""} {
          set meta(keywords) [::$context_package_id get_parameter keywords ""]
        }
        foreach i [array names meta] {
          # don't set empty meta tags
          if {$meta($i) eq ""} continue
          template::head::add_meta -name $i -content $meta($i)
        }
      }
    
      #
      # Pass variables for properties doc and body.
      # Example: ::xo::Page set_property body class "yui-skin-sam"
      #
      array set body [::xo::Page get_property body]
      array set doc  [::xo::Page get_property doc]
    
      if {$page_package_id != $context_package_id} {
        set page_context [::$page_package_id instance_name]
      }
    
      if {$template ne ""} {
    
        #
        # Initialize and set the template variables, to be used by
        # a. "adp_compile" / "adp_eval"
        # b. "return_page" / "adp_include"
        #
    
        set __including_page $page
        set __adp_stub [::$context_package_id get_adp_template view-default]
    
        set template_code [template::adp_compile -string $template]
        #
        # Make sure that <master/> and <slave/> tags are processed
        #
        append template_code {
          if { [info exists __adp_master] } {
            set __adp_output [template::adp_parse $__adp_master   [concat [list __adp_slave $__adp_output]  [array get __adp_properties]]]
          }
        }
        ad_try {
          set content [template::adp_eval template_code]
          ns_return 200 text/html $content
        } on error {errMsg} {
          ns_return 200 text/html "Error in Page ${:name}$errMsg<br>$template"
        }
        ad_script_abort
      } else {
        #
        # Use adp file.
        #
        #:log "use adp content=$content"
        set package_id $context_package_id
        set title      ${:title}
        set name       ${:name}
        set item_id    ${:item_id}
        ::$context_package_id return_page -adp $template_file -variables {
          name title item_id context return_url
          content footer package_id page_package_id page_context
          rev_link edit_link delete_link new_link admin_link index_link view_link
          notification_subscribe_link notification_image
          top_includelets page views_data body doc
        }
      }
    } else {
      set :mime_type [::xo::cc get_parameter content-type:graph text/html]
      return $content
    }