%3 ::xo::db::CrCache::Item ::xo::db::CrCache::Item ::xotcl::Object ::xotcl::Object ::xo::db::CrCache::Item->::xotcl::Object ::xowiki::Page ::xowiki::Page → find_slot anchor create_form_fields_from_form_constraints create_link find_slot get_form_data include is_folder_page pretty_link render resolve_included_page_name save_new substitute_markup www-autosave-attribute www-bulk-delete www-clipboard-add www-clipboard-clear www-clipboard-content www-clipboard-copy www-clipboard-export www-create-new www-create-or-use www-csv-dump www-delete www-delete-revision www-diff www-duplicate www-edit www-list www-make-live-revision www-popular-tags www-revisions www-save-attributes www-save-tags www-toggle-publish-status www-validate-attribute www-view ::xo::db::CrItem ::xo::db::CrItem ::xowiki::Page->::xo::db::CrItem ::xowiki::File ::xowiki::File www-download ::xowiki::File->::xowiki::Page ::xowiki::PageTemplate ::xowiki::PageTemplate www-delete ::xowiki::PageTemplate->::xowiki::Page ::xowiki::PageInstance ::xowiki::PageInstance get_from_template www-use-template ::xowiki::PageInstance->::xowiki::Page ::xowiki::PlainPage ::xowiki::PlainPage ::xowiki::PlainPage->::xowiki::Page ::xowiki::PodcastItem ::xowiki::PodcastItem ::xowiki::PodcastItem->::xowiki::File ::xowiki::Form ::xowiki::Form ::xowiki::Form->::xowiki::PageTemplate ::xowiki::FormPage ::xowiki::FormPage → compute_filter_clauses → fetch_object → filter_expression → get_all_children combine_data_and_form_field_default extra_html_fields get_property is_folder_page load_values_into_form_fields property render_thumbnails set_form_data set_live_revision set_property update_attribute_from_slot update_item_index www-edit www-file-upload www-toggle-modebutton ::xowiki::FormPage->::xowiki::PageInstance ::xowiki::Object ::xowiki::Object ::xowiki::Object->::xowiki::PlainPage

Class ::xowiki::Page

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

Defined in

Class Relations

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

Methods (to be applied on the object)

Methods (to be applied on instances)

  • anchor (scripted)

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

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

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

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

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

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

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

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

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

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

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

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

    Switches:
    -field_names (optional)
    Parameters:
    form_fields (required)

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

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

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

    Switches:
    -configure (optional)
    Parameters:
    arg (required)

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

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

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

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

    Testcases:
    xowiki_test_cases
    return 0
  • pretty_link (scripted, public)

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

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

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

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

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

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

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

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

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

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

    Parameters:
    page_name (required)

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

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

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

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

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

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

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

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

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

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

    Testcases:
    No testcase defined.
    ::security::csrf::validate
    
    if {![:exists_form_parameter "objects"]} {
      :msg "nothing to delete"
    }
    
    set instantiate_p [:form_parameter instantiate_p:boolean false]
    
    set item_ids [:get_ids_for_bulk_actions [:form_parameter objects: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
  • www-clipboard-add (scripted, public)

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

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

    Testcases:
    No testcase defined.
    if {![:exists_form_parameter "objects"] && [ns_conn method] eq "POST"} {
      :msg "nothing to copy"
    }
    
    ::xowiki::clipboard add [:get_ids_for_bulk_actions [:form_parameter objects: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
    }
  • www-clipboard-clear (scripted, public)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Testcases:
    create_test_items, create_composite_test_item, xowiki_test_cases, create_form_with_form_instance, create_form_with_numeric, form_validate, create_folder_and_configure
    set original_package_id ${:package_id}
    
    if {[:exists_query_parameter "package_instance"]} {
      set package_instance [:query_parameter package_instance:localurl]
      #
      # Initialize the target package and set the variable package_id.
      #
      ad_try {
        ::xowiki::Package initialize  -url $package_instance -user_id [::xo::cc user_id]  -actual_query ""
      } on error {errorMsg} {
        ns_log error "Package initialize: $errorMsg\n$::errorInfo"
        return [::$original_package_id error_msg  "Page <b>'${:name}'</b> invalid provided package instance=$package_instance<p>$errorMsg</p>"]
      }
    }
    
    #
    # Collect some default values from query parameters.
    #
    set default_variables {}
    #
    # 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
  • www-create-or-use (scripted, public)

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

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

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

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

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

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

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

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

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

    Switches:
    -return_url (optional)

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

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

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

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

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

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

    Testcases:
    No testcase defined.
    
    set compare_id [:query_parameter compare_revision_id:int32 0]
    if {$compare_id == 0} {
      return ""
    }
    ::xo::Page requireCSS urn:ad:css:xowiki-[::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
    }
  • www-duplicate (scripted, public)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Switches:
    -return_url (optional)

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

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

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

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

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

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

    Parameters:
    content (optional)

    Testcases:
    xowiki_test_cases
    #ns_log notice "www-view <$content>"
    
    #
    # The recursion_count os maintained to avoid recursive includes
    # inside a page.
    #
    ::xowiki::Page set recursion_count 0
    set page_package_id    ${:package_id}
    set context_package_id [::xo::cc package_id]
    set folder_id [::$page_package_id folder_id]
    
    #:msg "page_package_id=$page_package_id, context_package_id=$context_package_id"
    
    set template_file [ns_normalizepath [:query_parameter "template_file"  [::$context_package_id get_parameter template_file:graph view-default]]]
    if {[nsf::is object ::xowiki::$template_file]} {
      $template_file before_render [self]
    }
    
    #
    # Set up template variables.
    #
    set object_type [::$page_package_id get_parameter object_type:graph [:info class]]
    set rev_link    [::$page_package_id make_link [self] revisions]
    
    if {[::$context_package_id query_parameter m:token ""] eq "edit"} {
      set view_link [::$page_package_id make_link [self] view return_url]
      set edit_link ""
    } else {
      set edit_link [::$page_package_id make_link [self] edit return_url]
      set view_link ""
    }
    
    set delete_link [::$page_package_id make_link [self] delete return_url]
    if {[info exists :__link(new)]} {
      set new_link [set :__link(new)]
    } else {
      set new_link [:new_link $page_package_id]
    }
    
    set admin_link  [::$context_package_id make_link -privilege admin -link admin/ ::$context_package_id]
    set index_link  [::$context_package_id make_link -privilege public ::$context_package_id]
    set view_link   [::$page_package_id make_link [self] view return_url]
    
    set notification_subscribe_link ""
    if {[::$context_package_id get_parameter with_notifications:boolean 1]} {
      if {[::xo::cc user_id] != 0} {
        #
        # Notifications are only be displayed for logged-in users.
        #
        set notifications_return_url [expr {[info exists return_url] ? $return_url : [ad_return_url]}]
        set notification_type [notification::type::get_type_id -short_name xowiki_notif]
        set notification_text "Subscribe to [::$context_package_id instance_name]"
        set notification_subscribe_link  [export_vars -base /notifications/request-new  {{return_url $notifications_return_url}
                   {pretty_name $notification_text}
                   {type_id $notification_type}
                   {object_id $context_package_id}}]
        set notification_image "<adp:icon name='envelope' title='[ns_quotehtml $notification_text]'>"
      }
    }
    
    #
    # The content may be passed by other methods (e.g. edit) to
    # make use of the same templating machinery below.
    #
    if {$content eq ""} {
      set content [:content_header_get][:render]
      #:msg "--after render"
    } else {
      set content [:content_header_get]$content
    }
    #set content [::xowiki::adp_parse_tags $content]
    
    #
    # These variables can be influenced via set-parameter.
    #
    set autoname [::$page_package_id get_parameter autoname:boolean 0]
    
    #
    # Setup top includeletes and footers.
    #
    
    set footer [:htmlFooter -content $content]
    set top_includelets ""
    set vp [string trim [::$context_package_id get_parameter top_includelet ""]]
    if {$vp ne "" && $vp ne "none"} {
      set top_includelets [:include $vp]
    }
    
    if {[::$context_package_id get_parameter MenuBar:boolean 0]} {
      #
      # When a "MenuBar" is used, it might contain folder-specific
      # content. Therefore, we have to compute the tree. The resulting
      # HTML code can be placed via adp templates differently (or it
      # can be ignored).
    
      set folderhtml [:include {folders -style folders}]
      ::xo::Page set_property body folderHTML $folderhtml
      # TODO: there should be no need to pass manually folderhtml,
      # use the property instead
    
      #
      # At this place, the menu should be complete, we can render it.
      #
      set mb [::xowiki::MenuBar info instances -closure]
      if {$mb ne ""} {
        set mbHTML [$mb render-preferred]
        ::xo::Page set_property body menubarHTML $mbHTML
      }
    }
    
    if {[::$context_package_id get_parameter with_user_tracking:boolean 1]} {
      :record_last_visited
    }
    
    #
    # Deal with the views package (many thanks to Malte for this
    # snippet!)
    #
    if {[::$context_package_id get_parameter with_views_package_if_available:boolean 1]
        && [info commands ::views::record_view] ne ""} {
      views::record_view -object_id ${:item_id} -viewer_id [::xo::cc user_id]
      array set views_data [views::get -object_id ${:item_id}]
    }
    
    if {[:exists_query_parameter return_url]} {
      set return_url [:query_parameter return_url:localurl]
    }
    
    #:log "--after notifications [info exists notification_image]"
    
    set master [::$context_package_id get_parameter master:boolean 1]
    if {![string is boolean -strict $master]} {
      ad_return_complaint 1 "value of master is not boolean"
      ad_script_abort
    }
    
    if {$master} {
      set context [list ${:title}]
      #:msg "$context_package_id title=[::$context_package_id instance_name] - ${:title}"
      #:msg "::xo::cc package_id = [::xo::cc package_id]  ::xo::cc url= [::xo::cc url] "
      ::xo::Page set_property doc title "[::$context_package_id instance_name] - ${:title}"
      ::xo::Page set_property body title ${:title}
    
      # We could offer a user to translate the current page to his preferred language
      #
      # set create_in_req_locale_link ""
      # if {[::$context_package_id get_parameter use_connection_locale:boolean 0]} {
      #  $context_package_id get_lang_and_name -path [::$context_package_id set object] req_lang req_local_name
      #  set default_lang [::$page_package_id default_language]
      #  if {$req_lang ne $default_lang} {
      #      set l [Link create new -destroy_on_cleanup  #             -page [self] -type language -stripped_name $req_local_name  #             -name ${default_lang}:$req_local_name -lang $default_lang  #             -label $req_local_name -parent_id ${:parent_id} -item_id 0  #                 -package_id $context_package_id -init  #             -return_only undefined]
      #      $l render
      #   }
      # }
    
      #:log "--after context delete_link=$delete_link "
      #set template [::$context_package_id get_parameter template ""]
      set template ""
      set page [self]
    
      foreach css [::$context_package_id get_parameter extra_css: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
    }