Class ::xowiki::Page (public)

 ::xo::db::CrClass ::xowiki::Page[i]

Defined in

Testcases:
No testcase defined.
Source code:
namespace eval ::xowiki {}
::nsf::object::alloc ::xo::db::CrClass ::xowiki::Page {array set :RE {include {{{([^<]+?)}}([^\}\\]|$)} clean2 { <br */?> *(<div)} anchor {\\\[\\\[([^\]]+?)\\\]\\\]} div {&gt;&gt;([^&<]*?)&lt;&lt;([ \n]*)?} clean {[\\](\{\{|&gt;&gt;|\[\[)}}
   set :__default_metaclass ::xotcl::Class
   set :__default_superclass ::xotcl::Object
   set :abstract_p f
   set :auto_save false
   array set :db_constraints {page_order {{default {}} {default {}}}}
   array set :db_slot {page_order ::xowiki::Page::slot::page_order creator ::xowiki::Page::slot::creator page_id ::xowiki::Page::slot::page_id creation_date ::xo::db::Object::slot::creation_date creation_user ::xo::db::Object::slot::creation_user object_id ::xo::db::Object::slot::object_id description ::xowiki::Page::slot::description text ::xowiki::Page::slot::text creation_ip ::xo::db::Object::slot::creation_ip package_id ::xo::db::Object::slot::package_id object_title ::xo::db::Object::slot::object_title nls_language ::xo::db::CrItem::slot::nls_language security_inherit_p ::xo::db::Object::slot::security_inherit_p context_id ::xo::db::Object::slot::context_id mime_type ::xo::db::CrItem::slot::mime_type name ::xo::db::CrItem::slot::name title ::xowiki::Page::slot::title item_id ::xo::db::CrItem::slot::item_id revision_id ::xo::db::CrItem::slot::revision_id publish_date ::xowiki::Page::slot::publish_date modifying_user ::xo::db::Object::slot::modifying_user last_modified ::xo::db::Object::slot::last_modified modifying_ip ::xo::db::Object::slot::modifying_ip}
   set :folder_id -100
   set :form ::xowiki::WikiForm
   set :id_column page_id
   array set :markupmap {unescape { {[[}  \{\{  {&gt;&gt;}} escape {{\[[}  \\\{\{  {\&gt;&gt;} }}
   set :mime_type text/html
   set :name_method {}
   set :non_cached_instance_var_patterns __*
   set :object_type ::xowiki::Page
   set :object_type_key 000000000000111100011001
   set :pretty_name #xowiki.Page_pretty_name#
   set :pretty_plural #xowiki.Page_pretty_plural#
   set :recursion_count 0
   set :security_inherit_p t
   set :sql_package_name ::xowiki::Page
   set :storage_type text
   set :supertype content_revision
   set :table_name xowiki_page
   set :with_table true}
::xowiki::Page proc quoted_html_content text {
    list [ad_text_to_html -- $text] text/html
  }
::xowiki::Page proc container_already_rendered field {
    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])"
  }
::xowiki::Page proc save_tags {-package_id:required -item_id:required -revision_id:required -user_id:required tags} {
    ::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
  }
::xowiki::Page proc get_tags {-package_id:required -item_id -user_id} {
    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
  }
::xowiki::Page proc find_slot {-start_class:required name} {
    foreach cl [list $start_class {*}[$start_class info heritage]] {
      set slotobj ${cl}::slot::$name
      if {[nsf::is object $slotobj]} {
        #:msg $slotobj
        return $slotobj
      }
    }
    return ""
  }
::xowiki::Page proc import {-user_id -package_id -folder_id {-replace 0} -objects} {
    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
  }
::xowiki::Page instproc category_import {-name -description -locale -categories} {
    # 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
  }
::xowiki::Page instproc error_in_includelet {arg msg} {
    return [:error_during_render "[_ xowiki.error_in_includelet [list arg $arg name ${:name}]]<br >\n$msg"]
  }
::xowiki::Page instproc create_link arg {
    #: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
  }
::xowiki::Page instproc www-make-live-revision {} {
    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}}]]
  }
::xowiki::Page instproc www-toggle-publish-status -return_url {
    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
  }
::xowiki::Page instproc lang {} {
    return [string range ${:nls_language} 0 1]
  }
::xowiki::Page instproc get_content {} {
    return [:render -with_footer false -update_references never]
  }
::xowiki::Page instproc validate=form_constraints form_constraints {
    #
    # 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
  }
::xowiki::Page instproc www-clipboard-content {} {
    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
  }
::xowiki::Page instproc is_folder_page {{-include_folder_links true}} {
    return 0
  }
::xowiki::Page instproc get_description {-nr_chars content} {
    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
  }
::xowiki::Page instproc references_update {resolved {unresolved {}}} {
    #: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)"
      }
    }
  }
::xowiki::Page instproc www-save-attributes {} {
    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]]
  }
::xowiki::Page instproc www-delete -return_url {
    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
  }
::xowiki::Page instproc search_render {} {
    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 ""]
  }
::xowiki::Page instproc notification_render {} {
    return [:render]
  }
::xowiki::Page instproc show_fields {form_fields {msg {}}} {
    # 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"
  }
::xowiki::Page instproc lookup_cached_form_field -name:required {
    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"
  }
::xowiki::Page instproc can_link item_id {
    #
    # 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
  }
::xowiki::Page instproc category_export tree_name {
    #
    # 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"
  }
::xowiki::Page instproc is_form {} {
    return 0
  }
::xowiki::Page instproc www-edit {{-new:boolean false} {-autoname:boolean false} {-validation_errors ""}} {
    #
    # 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
  }
::xowiki::Page instproc initialize_loaded_object {} {
    if {[info exists :title] && ${:title} eq ""} {set :title ${:name}}
    next
  }
::xowiki::Page instproc www-delete-revision {} {
    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}}]]
    }
  }
::xowiki::Page instproc get_anchor_and_query link {
    #
    # 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]
  }
::xowiki::Page instproc condition=match {query_context value} {
    #
    # 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
  }
::xowiki::Page instproc create_form_field {{-cr_field_spec ""} {-field_spec ""} field_name} {
    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]]
      }
    }
  }
::xowiki::Page instproc www-clipboard-copy {} {
    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
  }
::xowiki::Page instproc references {submethod args} {
    #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"}
    }
  }
::xowiki::Page instproc physical_parent_id {} {
    if {[info exists :physical_parent_id]} {
      return ${:physical_parent_id}
    } else {
      return ${:parent_id}
    }
  }
::xowiki::Page instproc form_field_flush_cache {} {
    #
    # Flush all cached form_field_names.
    #
    unset -nocomplain ::_form_field_names
  }
::xowiki::Page instproc div arg {
    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 ""
    }
  }
::xowiki::Page instproc www-revisions {} {
    #set context [list [list [::${:package_id} url] ${:name} ] [_ xotcl-core.revisions]]
    #set title "[_ xotcl-core.revision_title] '${:name}'"
    return [:www-view [next]]
  }
::xowiki::Page instproc unescape string {
    # 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]
  }
::xowiki::Page instproc update_publish_status new_publish_status {
    #
    # 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}
      }
    }
  }
::xowiki::Page instproc create_form_fields field_names {
    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
  }
::xowiki::Page instproc www-create-new {{-parent_id 0} {-view_method edit} {-name ""} {-nls_language ""} {-publish_status ""}} {
    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 {}
    #
    # The value for "name" is validated later, and requires the type
    # of the object. Different names are allowed for files, folders
    # and other wiki pages.
    #
    foreach name_and_spec [list  name  title  page_order:graph  last_page_id:int32  nls_language:oneof,arg=[join [lang::system::get_locales] |]  ] {
      set p [string first : $name_and_spec]
      set key [expr {$p > -1 ? [string range $name_and_spec 0 $p-1] : $name_and_spec}]
      if {[:exists_query_parameter $key]} {
        lappend default_variables $key [:query_parameter $name_and_spec]
      }
    }

    # 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:0..n ""]
    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:int32 [: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:int32 [: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 name_and_spec {
      return_url:localurl
      template_file
      title
      detail_link:localurl
      text
    } {
      set p [string first : $name_and_spec]
      set key [expr {$p > -1 ? [string range $name_and_spec 0 $p-1] : $name_and_spec}]
      if {[:exists_query_parameter $key]} {
        set $key [:query_parameter $name_and_spec]
        :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:0..n ""]
    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
  }
::xowiki::Page instproc unset_temporary_instance_variables {} {
    #
    # Don't marshall/save/cache the following vars:
    #
    # array unset :__ia
    unset -nocomplain :__form_fields :__field_in_form  :__field_needed
  }
::xowiki::Page instproc adp_subst content {
    #
    # 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
  }
::xowiki::Page instproc pretty_link {{-anchor ""} {-query ""} {-absolute:boolean false} {-siteurl ""} {-lang ""} {-download false} {-path_encode:boolean true}} {
    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}
  }
::xowiki::Page instproc css_class_name {{-margin_form:boolean true}} {
    #
    # Determine the CSS class name for xowiki forms
    #
    set name ""
    if {$margin_form} {
      set css [::template::CSS class margin-form]
      if {$css ne ""} {
        set name "$css "
      }
    }
    return [append name [::xowiki::utility formCSSclass ${:name}]]
  }
::xowiki::Page instproc translate {-from -to text} {
    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 -- $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"
  }
::xowiki::Page instproc form_field_exists name {
    return [info exists ::_form_field_names($name)]
  }
::xowiki::Page instproc set_content text {
    :text [list [string map [list >> "\n&gt;&gt;" << "&lt;&lt;\n"]  [string trim $text " \n"]] text/html]
  }
::xowiki::Page instproc can_be_linked {} {
    return 1
  }
::xowiki::Page instproc condition=is_folder_page {query_context value} {
    # query_context and value are ignored
    return [:is_folder_page]
  }
::xowiki::Page instproc get_property_from_link_page {property {default {}}} {

    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
  }
::xowiki::Page instproc copy_content_vars {-from_object:required {-except {}}} {
    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 $except {
      dict set excluded_var $var 1
    }
    foreach var [$from_object info vars] {
      # don't copy vars starting with "__"
      if {[string match "__*" $var]} continue
      if {![dict exists $excluded_var $var]} {
        set :$var [$from_object set $var]
      }
    }
  }
::xowiki::Page instproc www-bulk-delete {} {
    ::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:int32,0..n]]
    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
  }
::xowiki::Page instproc render_icon {} {
    return [list text [namespace tail [:info class]] is_richtext false]
  }
::xowiki::Page instproc record_last_visited -user_id {
    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)"
        }
      }
    }
  }
::xowiki::Page instproc render_content {} {
    #:log "-- '${:text}'"
    lassign ${:text} html mime
    if {[:render_adp]} {
      set html [:adp_subst $html]
    }
    return [:substitute_markup $html]
  }
::xowiki::Page instproc resolve_included_page_name page_name {
    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
  }
::xowiki::Page instproc substitute_markup {{-context_obj ""} content} {
    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
  }
::xowiki::Page instproc get_rich_text_spec {field_name default} {
    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
  }
::xowiki::Page instproc mutual_overwrite_occurred {} {
    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
  }
::xowiki::Page instproc www-validate-attribute {} {
    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
  }
::xowiki::Page instproc self_link_ids {} {
    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]
  }
::xowiki::Page instproc is_new_entry old_name {
    return [expr {${:publish_status} eq "production" && $old_name eq ${:revision_id}}]
  }
::xowiki::Page instproc www-create-or-use {{-parent_id 0} {-view_method edit} {-name ""} {-nls_language ""}} {
    # can be overloaded
    :www-create-new  -parent_id $parent_id -view_method $view_method  -name $name -nls_language $nls_language
  }
::xowiki::Page instproc physical_item_id {} {
    if {[info exists :physical_item_id]} {
      return ${:physical_item_id}
    } else {
      return ${:item_id}
    }
  }
::xowiki::Page instproc anchor_parent_id {} {
    #
    # 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}
  }
::xowiki::Page instproc validate=form_input_fields form_fields {
    #
    # 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
  }
::xowiki::Page instproc get_html_from_content content {
    # 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
  }
::xowiki::Page instproc www-popular-tags {} {
    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
  }
::xowiki::Page instproc content_header_append text {
    #
    # 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
  }
::xowiki::Page instproc find_slot {-start_class name} {
    if {![info exists start_class]} {
      set start_class [:info class]
    }
    return [::xowiki::Page find_slot -start_class $start_class $name]
  }
::xowiki::Page instproc update {} {
      ::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
        }
      }
    }
::xowiki::Page instproc get_folder -folder_form_ids:required {
    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
  }
::xowiki::Page instproc evaluate_form_field_condition cond {
    #
    # Can be refined
    #
    return 0
  }
::xowiki::Page instproc www-diff {} {

    set compare_id [:query_parameter compare_revision_id:int32 0]
    if {$compare_id == 0} {
      return ""
    }
    ::xo::Page requireCSS urn:ad:css:xowiki-[::template::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
    }
  }
::xowiki::Page instproc create_raw_form_field {-name {-slot ""} {-spec ""} {-configuration ""} {-omit_field_name_spec:boolean false} {-nls_language ""} {-form_constraints ""}} {
    #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
  }
::xowiki::Page instproc instantiate_includelet arg {
    # 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
  }
::xowiki::Page instproc htmlFooter {{-content ""}} {
    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"
  }
::xowiki::Page instproc is_unprefixed {} {
    #
    # 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}
                }]
  }
::xowiki::Page instproc is_link_page {} {
    return 0
  }
::xowiki::Page instproc can_contain obj {
    #
    # 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
  }
::xowiki::Page instproc include_content {arg ch2} {
    #
    # 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
    }
  }
::xowiki::Page instproc create_form_page_instance {-name:required -package_id -parent_id {-text ""} {-instance_attributes ""} {-default_variables ""} {-nls_language ""} {-state initial} {-creation_user ""} {-publish_status production} {-source_item_id ""}} {
    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 sourceObj [FormPage get_instance_from_db -item_id $source_item_id]
      $f copy_content_vars -from_object $sourceObj -except name

      #
      # In case, we want manual autonaming, the following could be
      # used.
      #
      #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
  }
::xowiki::Page instproc return_redirect_without_params {} {
    #
    # 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]]]
  }
::xowiki::Page instproc build_name {{-nls_language ""}} {
    #
    # 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
  }
::xowiki::Page instproc reverse_map_party {-entry -default_party {-create_user_ids 0}} {
    # 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
  }
::xowiki::Page instproc lookup_form_field {-name:required form_fields} {
    :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
  }
::xowiki::Page instproc normalize_internal_link_name {name stripped_name lang} {
    #
    # 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]
  }
::xowiki::Page instproc content_header_get {} {
    if {[info exists ::__xowiki__content_header]} {
      return $::__xowiki__content_header
    }
  }
::xowiki::Page instproc anchor arg {
    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
  }
::xowiki::Page instproc map_categories category_ids {
    #
    # 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
  }
::xowiki::Page instproc physical_package_id {} {
    if {[info exists :physical_package_id]} {
      return ${:physical_package_id}
    } else {
      return ${:package_id}
    }
  }
::xowiki::Page instproc build_instance_attribute_map form_fields {
    #
    # 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}
  }
::xowiki::Page instproc demarshall {-parent_id -package_id -creation_user {-create_user_ids 0}} {
    # 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"
  }
::xowiki::Page instproc stats_record_count name {
    # 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"
  }
::xowiki::Page instproc www-save-tags {} {
    ::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:0..n]

    ::${:package_id} returnredirect  [:query_parameter return_url:localurl [ad_return_url]]
  }
::xowiki::Page instproc www-view {{content {}}} {
    #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:localurl ""] {
        ::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-[::template::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
    }
  }
::xowiki::Page instproc www-list {} {
    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]
  }
::xowiki::Page instproc notification_notify {} {
    ::xowiki::notification::do_notifications -page [self]
  }
::xowiki::Page instproc __debug_known_field_names msg {
    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"
  }
::xowiki::Page instproc field_names {{-form ""}} {
    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
  }
::xowiki::Page instproc render {{-update_references unresolved} {-with_footer:boolean true}} {
    #
    # 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
  }
::xowiki::Page instproc pretty_name {} {
    return ${:name}
  }
::xowiki::Page instproc get_target_from_link_page {{-depth 10}} {
    #
    # 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
  }
::xowiki::Page instproc create_form_fields_from_form_constraints {-lookup:switch 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
  }
::xowiki::Page instproc edit_set_default_values {} {
    # set some default values if they are provided
    foreach name_and_spec [list  name  title  page_order:graph  last_page_id:int32  nls_language:oneof,arg=[join [lang::system::get_locales] |]  ] {
      set p [string first : $name_and_spec]
      set key [expr {$p > -1 ? [string range $name_and_spec 0 $p-1] : $name_and_spec}]
      if {[::${:package_id} exists_query_parameter $key]} {
        #:log "setting [self] set $key [::${:package_id} query_parameter $key]"
        set :$key [::${:package_id} query_parameter $name_and_spec]
      }
    }
  }
::xowiki::Page instproc serialize_relocatable {} {
    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
  }
::xowiki::Page instproc check_unresolved_references {} {
    #: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
    }
  }
::xowiki::Page instproc notification_subject {-instance_name {-category_label ""} -state} {
    if {$category_label eq ""} {
      return "\[$instance_name\]: ${:title} ($state)"
    } else {
      return "\[$instance_name\] $category_label: ${:title} ($state)"
    }
  }
::xowiki::Page instproc www-clipboard-clear {} {
    ::xowiki::clipboard clear
    :return_redirect_without_params
  }
::xowiki::Page instproc set_resolve_context {-package_id:required -parent_id:required -item_id} {
    #
    # 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
    }
  }
::xowiki::Page instproc get_parent_object {} {
    #
    # 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 ""
  }
::xowiki::Page instproc new_link {-object_type -name -title -nls_language -return_url -parent_id page_package_id} {
    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]
  }
::xowiki::Page instproc footer {} {
    return ""
  }
::xowiki::Page instproc default_instance_attributes {} {
    #
    # 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]
  }
::xowiki::Page instproc can_save {} {
    #
    # 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
  }
::xowiki::Page instproc save_new args {
    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
  }
::xowiki::Page instproc detail_link {} {
    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]
  }
::xowiki::Page instproc validate=name name {
    #: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
  }
::xowiki::Page instproc rename {-old_name -new_name} {
    ${:package_id} flush_name_cache -name $old_name -parent_id ${:parent_id}
    next
    :log "----- rename <$old_name> to <$new_name>"
    #ns_log notice [:serialize]
  }
::xowiki::Page instproc error_during_render msg {
    return "<div class='errorMsg'>$msg</div>"
  }
::xowiki::Page instproc check_adp_include_path adp_fn {
    #
    # 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]
  }
::xowiki::Page instproc www-clipboard-export {} {
    set clipboard [::xowiki::clipboard get]
    ::xowiki::exporter export $clipboard
    ns_conn close
    ::xowiki::clipboard clear
    ad_script_abort
  }
::xowiki::Page instproc www-csv-dump {} {
    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]]
  }
::xowiki::Page instproc get_instance_attributes {} {
    if {[info exists :instance_attributes]} {
      return ${:instance_attributes}
    }
    return ""
  }
::xowiki::Page instproc edit_set_file_selector_folder {} {
    #
    # 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 ""
  }
::xowiki::Page instproc www-autosave-attribute {} {

    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
  }
::xowiki::Page instproc include {-configure arg} {
    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]
  }
::xowiki::Page instproc map_party {-property party_id} {
    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 {}
  }
::xowiki::Page instproc changed_redirect_url {} {
    if {[::${:package_id} exists_query_parameter "return_url"]} {
      return ""
    }
    return [:pretty_link]
  }
::xowiki::Page instproc get_query_parameter_return_url {{default {}}} {
    #
    # 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
  }
::xowiki::Page instproc save args {
    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
  }
::xowiki::Page instproc validate=page_order value {
    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
  }
::xowiki::Page instproc render_includelet includelet {
    #
    # 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
  }
::xowiki::Page instproc save_data {{-use_given_publish_date:boolean false} old_name category_ids} {
    #: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}
  }
::xowiki::Page instproc www-clipboard-add {} {
    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:int32,0..n]]
    #
    # 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
    }
  }
::xowiki::Page instproc reverse_map_party_attribute {-attribute {-default_party 0} {-create_user_ids 0}} {
    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]
    }
  }
::xowiki::Page instproc add_computed_instance_attributes {} {
    #
    # 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.
    #
  }
::xowiki::Page instproc marshall {{-mode export}} {
    :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
  }
::xowiki::Page instproc get_form_data {-field_names form_fields} {
    #: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]]
  }
::xowiki::Page instproc www-duplicate {} {
    ::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}
    }
  }
::xowiki::Page instproc reset_resolve_context {} {
    #
    # 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
        }
      }
    }
  }
::xowiki::Page instproc notification_detail_link {} {
    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]
  }
::xowiki::Page instproc form_field_index form_field_objs {
    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
  }
::xowiki::Page instproc condition=regexp {query_context value} {
    #
    # 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
  }
::xowiki::Page instproc stats_record_detail args {
    # 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"
  }
::xowiki::Page instproc regsub_eval {{-noquote:boolean false} re string cmd {prefix ""}} {
    if {$noquote} {
      set map { \[ \\[ \] \\] \$ \\$ \\ \\\\}
    } else {
      set map { \" \\\" \[ \\[ \] \\] \$ \\$ \\ \\\\}
    }
    uplevel [list subst [regsub -all -- $re [string map $map $string"\[$cmd\]"]]
  }
::xowiki::Page instforward get_nls_language_from_lang ::xowiki::Package %proc
::xowiki::Page instforward exists_form_parameter {%set :package_id} %proc
::xowiki::Page instforward item_ref {%my package_id} %proc
::xowiki::Page instforward form_parameter {%set :package_id} %proc
::xowiki::Page instforward exists_query_parameter {%set :package_id} %proc
::xowiki::Page instforward get_ids_for_bulk_actions {%my package_id} %proc
::xowiki::Page instforward query_parameter {%set :package_id} %proc
::xowiki::Page instparametercmd creator
::xowiki::Page instparametercmd page_id
::xowiki::Page instparametercmd creation_user
::xowiki::Page instparametercmd mime_type
::xowiki::Page instparametercmd publish_date
::xowiki::Page instparametercmd render_adp
::xowiki::Page instparametercmd absolute_links
::xowiki::Page instparametercmd title
::xowiki::Page instparametercmd page_order
::xowiki::Page instparametercmd description
::xowiki::Page instparametercmd name
::xowiki::Page instparametercmd last_modified
::xowiki::Page instparametercmd do_substitutions
::xowiki::Page instparametercmd text
::xowiki::Page instparametercmd nls_language
::nsf::relation::set ::xowiki::Page superclass ::xo::db::CrItem

::nx::slotObj -container slot ::xowiki::Page
::xowiki::Page::slot eval {set :__parameter {
        {render_adp 1}
        {do_substitutions 1}
        {absolute_links 0}
      }}

::nsf::object::alloc ::xo::db::CrAttribute ::xowiki::Page::slot::page_order {set :accessor public
   set :column_name page_order
   set :configurable true
   set :convert false
   set :create_acs_attribute true
   set :create_table_attribute true
   set :datatype text
   set :default {}
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xowiki::Page
   set :incremental false
   set :manager ::xowiki::Page::slot::page_order
   set :max_n_values 1
   set :methodname page_order
   set :min_n_values 1
   set :multiplicity 1..1
   set :name page_order
   set :parameterSpec {-page_order:substdefault {}}
   set :per-object false
   set :position 0
   set :pretty_name #xowiki.Page-page_order#
   set :pretty_plural {}
   set :references {}
   set :required false
   set :sqltype ltree
   set :substdefault 0b111
   set :trace none
   set :validator page_order
   : init}

::nsf::object::alloc ::xo::db::CrAttribute ::xowiki::Page::slot::creator {set :accessor public
   set :column_name creator
   set :configurable true
   set :convert false
   set :create_acs_attribute true
   set :create_table_attribute true
   set :datatype text
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xowiki::Page
   set :incremental false
   set :manager ::xowiki::Page::slot::creator
   set :max_n_values 1
   set :methodname creator
   set :min_n_values 1
   set :multiplicity 1..1
   set :name creator
   set :parameterSpec -creator
   set :per-object false
   set :position 0
   set :pretty_name #xowiki.Page-creator#
   set :pretty_plural {}
   set :references {}
   set :required false
   set :sqltype text
   set :trace none
   : init}

::nsf::object::alloc ::xo::db::Attribute ::xowiki::Page::slot::page_id {set :accessor public
   set :column_name page_id
   set :configurable true
   set :convert false
   set :create_acs_attribute false
   set :create_table_attribute true
   set :datatype integer
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xowiki::Page
   set :incremental false
   set :manager ::xowiki::Page::slot::page_id
   set :max_n_values 1
   set :methodname page_id
   set :min_n_values 1
   set :multiplicity 1..1
   set :name page_id
   set :parameterSpec -page_id
   set :per-object false
   set :position 0
   set :pretty_name ID
   set :pretty_plural {}
   set :references {}
   set :required false
   set :sqltype integer
   set :trace none
   : init}

::nsf::object::alloc ::xotcl::Attribute ::xowiki::Page::slot::do_substitutions {set :accessor public
   set :configurable true
   set :convert false
   set :default 1
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xowiki::Page
   set :incremental 0
   set :manager ::xowiki::Page::slot::do_substitutions
   set :methodname do_substitutions
   set :multiplicity 1..1
   set :name do_substitutions
   set :parameterSpec {-do_substitutions:substdefault 1}
   set :per-object false
   set :position 0
   set :required false
   set :substdefault 0b111
   set :trace none
   : init}

::nsf::object::alloc ::xo::Attribute ::xowiki::Page::slot::creation_user {set :accessor public
   set :configurable true
   set :convert false
   set :datatype text
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xowiki::Page
   set :incremental false
   set :manager ::xowiki::Page::slot::creation_user
   set :methodname creation_user
   set :multiplicity 1..1
   set :name creation_user
   set :parameterSpec -creation_user
   set :per-object false
   set :position 0
   set :pretty_name #xowiki.Page-creation_user#
   set :pretty_plural {}
   set :required false
   set :spec user_id
   set :trace none
   : init}

::nsf::object::alloc ::xotcl::Attribute ::xowiki::Page::slot::render_adp {set :accessor public
   set :configurable true
   set :convert false
   set :default 1
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xowiki::Page
   set :incremental 0
   set :manager ::xowiki::Page::slot::render_adp
   set :methodname render_adp
   set :multiplicity 1..1
   set :name render_adp
   set :parameterSpec {-render_adp:substdefault 1}
   set :per-object false
   set :position 0
   set :required false
   set :substdefault 0b111
   set :trace none
   : init}

::nsf::object::alloc ::xo::db::CrAttribute ::xowiki::Page::slot::description {set :accessor public
   set :column_name description
   set :configurable true
   set :convert false
   set :create_acs_attribute false
   set :create_table_attribute false
   set :datatype text
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xo::db::CrItem
   set :incremental false
   set :manager ::xowiki::Page::slot::description
   set :max_n_values 1
   set :methodname description
   set :min_n_values 1
   set :multiplicity 1..1
   set :name description
   set :parameterSpec -description
   set :per-object false
   set :position 0
   set :pretty_name #xotcl-core.description#
   set :pretty_plural #xotcl-core.descriptions#
   set :references {}
   set :required false
   set :spec textarea,cols=80,rows=2,label=#xowiki.Page-description#
   set :sqltype text
   set :trace none
   : init}

::nsf::object::alloc ::xo::db::CrAttribute ::xowiki::Page::slot::text {set :accessor public
   set :column_name text
   set :configurable true
   set :convert false
   set :create_acs_attribute false
   set :create_table_attribute false
   set :datatype text
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xo::db::CrItem
   set :incremental false
   set :manager ::xowiki::Page::slot::text
   set :max_n_values 1
   set :methodname text
   set :min_n_values 1
   set :multiplicity 1..1
   set :name text
   set :parameterSpec -text
   set :per-object false
   set :position 0
   set :pretty_name Text
   set :pretty_plural {}
   set :references {}
   set :required false
   set :spec richtext
   set :sqltype text
   set :trace none
   : init}

::nsf::object::alloc ::xotcl::Attribute ::xowiki::Page::slot::absolute_links {set :accessor public
   set :configurable true
   set :convert false
   set :default 0
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xowiki::Page
   set :incremental 0
   set :manager ::xowiki::Page::slot::absolute_links
   set :methodname absolute_links
   set :multiplicity 1..1
   set :name absolute_links
   set :parameterSpec {-absolute_links:substdefault 0}
   set :per-object false
   set :position 0
   set :required false
   set :substdefault 0b111
   set :trace none
   : init}

::nsf::object::alloc ::xo::Attribute ::xowiki::Page::slot::nls_language {set :accessor public
   set :configurable true
   set :convert false
   set :datatype text
   set :default {[ad_conn locale]}
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xowiki::Page
   set :incremental false
   set :manager ::xowiki::Page::slot::nls_language
   set :methodname nls_language
   set :multiplicity 1..1
   set :name nls_language
   set :parameterSpec {-nls_language:substdefault {[ad_conn locale]}}
   set :per-object false
   set :position 0
   set :pretty_name #xowiki.Page-nls_language#
   set :pretty_plural {}
   set :required false
   set :spec {select,options=[xowiki::locales]}
   set :substdefault 0b111
   set :trace none
   : init}

::nsf::object::alloc ::xotcl::Attribute ::xowiki::Page::slot::mime_type {set :accessor public
   set :configurable true
   set :convert false
   set :default text/html
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xowiki::Page
   set :incremental false
   set :manager ::xowiki::Page::slot::mime_type
   set :methodname mime_type
   set :multiplicity 1..1
   set :name mime_type
   set :parameterSpec {-mime_type:substdefault text/html}
   set :per-object false
   set :position 0
   set :required false
   set :substdefault 0b111
   set :trace none
   : init}

::nsf::object::alloc ::xo::Attribute ::xowiki::Page::slot::name {set :accessor public
   set :configurable true
   set :convert false
   set :datatype text
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xowiki::Page
   set :help_text #xowiki.Page-name-help_text#
   set :incremental false
   set :manager ::xowiki::Page::slot::name
   set :methodname name
   set :multiplicity 1..1
   set :name name
   set :parameterSpec -name
   set :per-object false
   set :position 0
   set :pretty_name #xowiki.Page-name#
   set :pretty_plural {}
   set :required false
   set :spec maxlength=400,required
   set :trace none
   set :validator name
   : init}

::nsf::object::alloc ::xo::db::CrAttribute ::xowiki::Page::slot::title {set :accessor public
   set :column_name title
   set :configurable true
   set :convert false
   set :create_acs_attribute false
   set :create_table_attribute false
   set :datatype text
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xo::db::CrItem
   set :incremental false
   set :manager ::xowiki::Page::slot::title
   set :max_n_values 1
   set :methodname title
   set :min_n_values 1
   set :multiplicity 1..1
   set :name title
   set :parameterSpec -title
   set :per-object false
   set :position 0
   set :pretty_name #xotcl-core.title#
   set :pretty_plural #xotcl-core.titles#
   set :references {}
   set :required false
   set :sqltype varchar(1000)
   set :trace none
   : init}

::nsf::object::alloc ::xo::db::CrAttribute ::xowiki::Page::slot::publish_date {set :accessor public
   set :column_name publish_date
   set :configurable true
   set :convert false
   set :create_acs_attribute false
   set :create_table_attribute false
   set :datatype date
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xo::db::CrItem
   set :incremental false
   set :manager ::xowiki::Page::slot::publish_date
   set :max_n_values 1
   set :methodname publish_date
   set :min_n_values 1
   set :multiplicity 1..1
   set :name publish_date
   set :parameterSpec -publish_date
   set :per-object false
   set :position 0
   set :pretty_name #xo.CrItem-publish_date#
   set :pretty_plural {}
   set :references {}
   set :required false
   set :spec date,label=#xowiki.Page-publish_date#
   set :sqltype date
   set :trace none
   : init}

::nsf::object::alloc ::xo::Attribute ::xowiki::Page::slot::last_modified {set :accessor public
   set :configurable true
   set :convert false
   set :datatype text
   set :defaultmethods {}
   set :disposition alias
   set :domain ::xowiki::Page
   set :incremental false
   set :manager ::xowiki::Page::slot::last_modified
   set :methodname last_modified
   set :multiplicity 1..1
   set :name last_modified
   set :parameterSpec -last_modified
   set :per-object false
   set :position 0
   set :pretty_name #xowiki.Page-last_modified#
   set :pretty_plural {}
   set :required false
   set :spec date
   set :trace none
   : init}

namespace eval ::xowiki {::namespace export Menu YUIMenuBar YUIMenuBarItem YUIMenu YUIMenuItem YUIMenuItemList YUIContextMenu YUIContextMenuItem}
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: