$title
" \ + "Created by $creator, " \ + "last modified by [::xo::get_user_name $creation_user] " \ + "$pretty_date
" \ + $description $more $my_footer \n\ + "Index: openacs-4/packages/xowiki/COPYRIGHT
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/COPYRIGHT,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/xowiki/COPYRIGHT 13 Sep 2012 16:05:25 -0000 1.4
@@ -0,0 +1,23 @@
+ * xowiki
+ *
+ * Copyright (C) 2005-2008 Gustaf Neumann, neumann@wu-wien.ac.at
+ *
+ * Vienna University of Economics and Business Administration
+ * Institute of Information Systems and New Media
+ * A-1090, Augasse 2-6
+ * Vienna, Austria
+ *
+ * This is a BSD-Style license applicable for the files in this
+ * directory and below, except when stated explicitly different.
+ *
+ * Permission to use, copy, modify, distribute, and sell this
+ * software and its documentation for any purpose is hereby granted
+ * without fee, provided that the above copyright notice appear in
+ * all copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation. We make no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+
+
Index: openacs-4/packages/xowiki/xowiki.info
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/xowiki.info,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/xowiki/xowiki.info 13 Sep 2012 16:05:26 -0000 1.146
@@ -0,0 +1,126 @@
+
+
+
+ $date \
+ [my report_lines]"
+ }
+
+ Importer instproc import {-object:required -replace -create_user_ids} {
+ #
+ # Import a single object. In essence, this method demarshalls a
+ # single object and inserts it (or updates it) in the database. It
+ # takes as well care about categories.
+ #
+ my instvar package_id user_id
+
+ $object demarshall -parent_id [$object parent_id] -package_id $package_id \
+ -creation_user $user_id -create_user_ids $create_user_ids
+ set item_id [::xo::db::CrClass lookup -name [$object name] -parent_id [$object parent_id]]
+ #my msg "lookup of [$object name] parent [$object parent_id] => $item_id"
+ if {$item_id != 0} {
+ if {$replace} { ;# we delete the original
+ ::xo::db::CrClass delete -item_id $item_id
+ set item_id 0
+ my report_line $object replaced
+ my incr replaced
+ } else {
+ #my msg "$item_id update: [$object name]"
+ ::xo::db::CrClass get_instance_from_db -item_id $item_id
+ $item_id copy_content_vars -from_object $object
+ $item_id save -use_given_publish_date [$item_id exists publish_date] \
+ -modifying_user [$object set modifying_user]
+ #my log "$item_id saved"
+ $object set item_id [$item_id item_id]
+ #my msg "$item_id updated: [$object name]"
+ my report_line $item_id updated
+ my incr updated
+ }
+ }
+ if {$item_id == 0} {
+ set n [$object save_new -use_given_publish_date [$object exists publish_date] \
+ -creation_user [$object set modifying_user] ]
+ $object set item_id $n
+ set item_id $object
+ #my msg "$object added: [$object name]"
+ my report_line $object added
+ my incr added
+ }
+ #
+ # The method demarshall might set the mapped __category_ids in $object.
+ # Insert these into the category object map
+ #
+ if {[$object exists __category_ids]} {
+ #my msg "$item_id map_categories [object set __category_ids] // [$item_id item_id]"
+ $item_id map_categories [$object set __category_ids]
+ }
+
+ $package_id flush_references -item_id [$object item_id] -name [$object name]
+ }
+
+ Importer instproc import_all {-replace -objects:required {-create_user_ids 0} {-keep_inherited 1}} {
+ #
+ # Import a series of objects. This method takes care especially
+ # about dependencies of objects, which is reflected by the order
+ # of object-imports.
+ #
+ #
+ # Extact information from objects to be imported, that might be
+ # changed later in the objects.
+ #
+ foreach o $objects {
+ #
+ # Remember old item_ids and old_names for pages with
+ # item_ids. Only these can have parents (page_templates) or
+ # child_objects
+ #
+ if {[$o exists item_id]} {
+ set item_ids([$o item_id]) $o
+ set old_names([$o item_id]) [$o name]
+ } {
+ $o item_id ""
+ }
+ # Remember old parent_ids for name-mapping, names are
+ # significant per parent_id.
+ if {[$o exists parent_id]} {
+ set parent_ids([$o item_id]) [$o parent_id]
+ } {
+ $o parent_id ""
+ }
+ set todo($o) 1
+
+ #
+ # Handle import of categories in the first pass
+ #
+ if {[$o exists __map_command]} {
+ $o package_id [my package_id]
+ $o eval [$o set __map_command]
+ }
+ # FIXME remove?
+ #if {[$o exists __category_map]} {
+ # array set ::__category_map [$o set __category_map]
+ #}
+ }
+ #my msg "item_ids=[array names item_ids], parent_ids=[array names parent_ids]"
+
+ #
+ # Make a fix-point iteration during import. Do only import, when
+ # all prerequirement pages are already loaded.
+ #
+ while {[llength [array names todo]] > 0} {
+ set new 0
+ foreach o [array names todo] {
+ #my msg "work on $o [$o info class] [$o name]"
+
+ set old_name [$o name]
+ set old_item_id [$o item_id]
+ set old_parent_id [$o parent_id]
+
+ # page instances have references to page templates, add the templates first
+ if {[$o istype ::xowiki::PageInstance]} {
+ set old_template_id [$o page_template]
+ if {![info exists old_names($old_template_id)]} {
+ set new 0
+ my msg "need name for $old_template_id. Maybe item_ids for PageTemplate missing?"
+ break
+ }
+
+ set template_name_key $parent_ids($old_template_id)-$old_names($old_template_id)
+ if {![info exists name_map($template_name_key)]} {
+ #my msg "... delay import of $o (no object with name $template_name_key) imported"
+ continue
+ }
+ #my msg "we found entry for name_map($template_name_key) = $name_map($template_name_key)"
+ }
+
+ if {[info exists item_ids($old_parent_id)]} {
+ # we have a child object
+ if {![info exists id_map($old_parent_id)]} {
+ #my msg "... delay import of $o (map of parent_id $old_parent_id missing)"
+ continue
+ }
+ }
+
+ set need_to_import 1
+ #
+ # If the page was implicitly added (due to being a
+ # page_template of an exported page), and a page (e.g. a form
+ # or a workflow) with the same name can be found in the
+ # target, don't materialize the inherited page.
+ #
+ if {$keep_inherited
+ && [$o exists __export_reason]
+ && [$o set __export_reason] eq "implicit_page_template"} {
+ $o unset __export_reason
+ set page [[my package_id] get_page_from_item_ref \
+ -allow_cross_package_item_refs false \
+ -use_package_path true \
+ -use_site_wide_pages true \
+ -use_prototype_pages false \
+ [$o name] \
+ ]
+
+ # If we would like to restrict to just inherited pages in
+ # the target, we could extend the test below with a test like
+ # the following:
+ # set inherited [expr {[$page physical_parent_id] ne [$page parent_id]}]
+
+ if {$page ne ""} {
+ #my msg "page [$o name] can ne found in folder [my parent_id]"
+ my incr inherited
+ unset todo($o)
+ set o $page
+ set need_to_import 0
+ }
+ }
+
+ if {$need_to_import} {
+ # Now, all requirements are met, parent-object and
+ # child-object conditions are fulfilled. We have to map
+ # page_template for PageInstances and parent_ids for child
+ # objects to new IDs.
+ #
+ if {[$o istype ::xowiki::PageInstance]} {
+ #my msg "importing [$o name] page_instance, map $template_name_key to $name_map($template_name_key)"
+ $o page_template $name_map($template_name_key)
+ #my msg "exists template? [my isobject [$o page_template]]"
+ if {![my isobject [$o page_template]]} {
+ ::xo::db::CrClass get_instance_from_db -item_id [$o page_template]
+ #my msg "[my isobject [$o page_template]] loaded"
+ }
+ }
+
+ if {[info exists item_ids($old_parent_id)]} {
+ $o set parent_id $id_map($old_parent_id)
+ } else {
+ $o set parent_id [my parent_id]
+ }
+
+ # Everything is mapped, we can now do the import.
+
+ #my msg "start import for $o, name=[$o name]"
+ my import \
+ -object $o \
+ -replace $replace \
+ -create_user_ids $create_user_ids
+ #my msg "import for $o done, name=[$o name]"
+
+ unset todo($o)
+ }
+
+ #
+ # Maintain the maps and iterate
+ #
+ if {$old_item_id ne ""} {
+ set id_map($old_item_id) [$o item_id]
+ }
+ set name_map($old_parent_id-$old_name) [$o item_id]
+ #my msg "setting name_map($old_parent_id-$old_name)=$name_map($old_parent_id-$old_name), o=$o, old_item_id=$old_item_id"
+
+ set new 1
+ }
+ if {$new == 0} {
+ my msg "could not import [array names todo]"
+ break
+ }
+ }
+ #my msg "final name_map=[array get name_map], id_map=[array get id_map]"
+
+ #
+ # final cleanup
+ #
+ foreach o $objects {$o destroy}
+
+ [my package_id] flush_page_fragment_cache
+ }
+
+ #
+ # A small helper for exporting objects
+ #
+
+ Object create exporter
+ exporter proc include_needed_objects {item_ids} {
+ #
+ # Load the objects
+ #
+ foreach item_id $item_ids {
+ if {[::xo::db::CrClass get_instance_from_db -item_id $item_id] eq ""} {
+ my log "Warning: cannot fetch item $item_id for exporting"
+ } else {
+ set items($item_id) 1
+ }
+ }
+
+ #
+ # In a second step, include the objects which should be exported implicitly
+ #
+ while {1} {
+ set new 0
+ ns_log notice "--export works on [array names items]"
+ foreach item_id [array names items] {
+ #
+ # We flag the reason, why the implicitely included elements were
+ # included. If the target can resolve already such items
+ # (e.g. forms), we might not have to materialize these finally.
+ #
+ # For PageInstances (or its subtypes), include the parent-objects as well
+ #
+ if {[$item_id istype ::xowiki::PageInstance]} {
+ set template_id [$item_id page_template]
+ if {![info exists items($template_id)]} {
+ ns_log notice "--export including parent-object $template_id [$template_id name]"
+ set items($template_id) 1
+ ::xo::db::CrClass get_instance_from_db -item_id $template_id
+ set new 1
+ $template_id set __export_reason implicit_page_template
+ continue
+ }
+ }
+ #
+ # check for child objects of the item
+ #
+ set sql [::xowiki::Page instance_select_query -folder_id $item_id -with_subtypes true]
+ db_foreach instance_select $sql {
+ if {![info exists items($item_id)]} {
+ ::xo::db::CrClass get_instance_from_db -item_id $item_id
+ ns_log notice "--export including child $item_id [$item_id name]"
+ set items($item_id) 1
+ set new 1
+ $item_id set __export_reason implicit_child_page
+ }
+ }
+ }
+ if {!$new} break
+ }
+ return [array names items]
+ }
+
+ exporter proc marshall_all {item_ids} {
+ set content ""
+ foreach item_id $item_ids {
+ if {[catch {set obj [$item_id marshall]} errorMsg]} {
+ ns_log error "Error while exporting $item_id [$item_id name]\n$errorMsg\n$::errorInfo"
+ } else {
+ append content $obj\n
+ }
+ }
+ return $content
+ }
+
+ exporter proc export {item_ids} {
+ #
+ # include implictely needed objects, instantiate the objects.
+ #
+ set item_ids [my include_needed_objects $item_ids]
+ #
+ # stream the objects via ns_write
+ #
+ ns_set put [ns_conn outputheaders] "Content-Type" "text/plain"
+ ns_set put [ns_conn outputheaders] "Content-Disposition" "attachment;filename=export.xotcl"
+ ReturnHeaders
+
+ foreach item_id $item_ids {
+ ns_log notice "--exporting $item_id [$item_id name]"
+ if {[catch {set obj [$item_id marshall]} errorMsg]} {
+ ns_log error "Error while exporting $item_id [$item_id name]\n$errorMsg\n$::errorInfo"
+ } else {
+ ns_write "$obj\n"
+ }
+ }
+ }
+
+
+ #
+ # Simple archive file manager
+ #
+ # The Archive manages supports importing .zip files and .tar.gz
+ # files as ::xowiki::File into xowiki folders.
+ #
+ ::xotcl::Class create ArchiveFile -parameter {
+ file
+ name
+ parent_id
+ {use_photo_form false}
+ }
+ ArchiveFile instproc init {} {
+ my destroy_on_cleanup
+ ::xo::db::CrClass get_instance_from_db -item_id [my parent_id]
+ my set tmpdir [ns_tmpnam]
+ file mkdir [my set tmpdir]
+ }
+ ArchiveFile instproc delete {} {
+ file delete -force [my set tmpdir]
+ next
+ }
+ ArchiveFile instproc unpack {} {
+ my instvar name file
+ set success 0
+ #my log "::xowiki::guesstype '$name' => [::xowiki::guesstype $name]"
+ switch [::xowiki::guesstype $name] {
+ application/zip -
+ application/x-zip -
+ application/x-zip-compressed {
+ set zipcmd [::util::which unzip]
+ #my msg "zip = $zipcmd, tempdir = [my set tmpdir]"
+ exec $zipcmd $file -d [my set tmpdir]
+ my import -dir [my set tmpdir] -parent_id [my parent_id]
+ set success 1
+ }
+ application/x-compressed {
+ if {[string match *tar.gz $name]} {
+ set cmd [::util::which tar]
+ exec $cmd -xzf $file -C [my set tmpdir]
+ my import -dir [my set tmpdir] -parent_id [my parent_id]
+ set success 1
+ } else {
+ my msg "unknown compressed file type $name"
+ }
+ }
+ default {my msg "type [::xowiki::guesstype $name] of $name unknown"}
+ }
+ #my msg success=$success
+ return $success
+ }
+ ArchiveFile instproc import {-dir -parent_id} {
+ set package_id [$parent_id package_id]
+
+ foreach tmpfile [glob -nocomplain -directory $dir *] {
+ #my msg "work on $tmpfile [::file isdirectory $tmpfile]"
+ set file_name [::file tail $tmpfile]
+ if {[::file isdirectory $tmpfile]} {
+ # ignore mac os x resource fork directories
+ if {[string match *__MACOSX $tmpfile]} continue
+ set folder_object [$package_id get_page_from_name -assume_folder true \
+ -name $file_name -parent_id $parent_id]
+ if {$folder_object ne ""} {
+ # if the folder exists already, we have nothing to do
+ } else {
+ # we create a new folder ...
+ set folder_form_id [::xowiki::Weblog instantiate_forms -forms en:folder.form \
+ -package_id $package_id]
+ set folder_object [FormPage new -destroy_on_cleanup \
+ -title $file_name \
+ -name $file_name \
+ -package_id $package_id \
+ -parent_id $parent_id \
+ -nls_language en_US \
+ -instance_attributes {} \
+ -page_template $folder_form_id]
+ $folder_object save_new
+ # ..... and refetch it under its canonical name
+ ::xo::db::CrClass get_instance_from_db -item_id [$folder_object item_id]
+ }
+ my import -dir $tmpfile -parent_id [$folder_object item_id]
+ } else {
+ set mime_type [::xowiki::guesstype $file_name]
+ if {[string match image/* $mime_type] && [my use_photo_form]} {
+ set photo_object [$package_id get_page_from_name -name en:$file_name -parent_id $parent_id]
+ if {$photo_object ne ""} {
+ # photo entry exists already, create a new revision
+ my log "Photo $file_name exists already"
+ $photo_object set title $file_name
+ set f [::xowiki::formfield::file new -object $photo_object -name "image" -destroy_on_cleanup]
+ $f set value $file_name
+ $f content-type $mime_type
+ $f set tmpfile $tmpfile
+ $f convert_to_internal
+ $photo_object save
+ } else {
+ # create a new photo entry
+ my log "new Photo $file_name"
+ set photoFormObj [::xowiki::Weblog instantiate_forms \
+ -parent_id $parent_id -forms en:photo.form -package_id $package_id]
+ set photo_object [$photoFormObj create_form_page_instance \
+ -name en:$file_name \
+ -nls_language en_US \
+ -creation_user [::xo::cc user_id] \
+ -parent_id $parent_id \
+ -package_id $package_id \
+ -instance_attributes [list image $file_name]]
+ $photo_object title $file_name
+ $photo_object publish_status "ready"
+ $photo_object save_new ;# to obtain item_id needed by the form-field
+ set f [::xowiki::formfield::file new -object $photo_object -name "image" -destroy_on_cleanup]
+ $f set value $file_name
+ $f content-type $mime_type
+ $f set tmpfile $tmpfile
+ $f convert_to_internal
+ #my log "after convert to internal $file_name"
+ }
+ } else {
+ set file_object [$package_id get_page_from_name -name file:$file_name -parent_id $parent_id]
+ if {$file_object ne ""} {
+ my msg "file $file_name exists already"
+ # file entry exists already, create a new revision
+ $file_object set import_file $tmpfile
+ $file_object set mime_type $mime_type
+ $file_object set title $file_name
+ $file_object save
+ } else {
+ my msg "file $file_name created new"
+ set file_object [::xowiki::File new -destroy_on_cleanup \
+ -title $file_name \
+ -name file:$file_name \
+ -parent_id $parent_id \
+ -mime_type $mime_type \
+ -package_id $package_id \
+ -creation_user [::xo::cc user_id] ]
+ $file_object set import_file $tmpfile
+ $file_object save_new
+ }
+ }
+ }
+ }
+ }
+}
+::xo::library source_dependent
+
Index: openacs-4/packages/xowiki/tcl/includelet-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/includelet-procs.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/xowiki/tcl/includelet-procs.tcl 13 Sep 2012 16:05:27 -0000 1.183
@@ -0,0 +1,4529 @@
+::xo::library doc {
+ XoWiki - define various kind of includelets
+
+ @creation-date 2006-10-10
+ @author Gustaf Neumann
+ @cvs-id $Id: includelet-procs.tcl,v 1.183 2012/09/13 16:05:27 victorg Exp $
+}
+namespace eval ::xowiki::includelet {
+ #
+ # Define a meta-class for creating Includelet classes.
+ # We use a meta-class for making it easier to define properties
+ # on classes of includelets, which can be used without instantiating
+ # it. One can for example use the query from the page fragment
+ # cache the caching properties of the class.
+ #
+ Class create ::xowiki::IncludeletClass \
+ -superclass ::xotcl::Class \
+ -parameter {
+ {localized true}
+ {personalized true}
+ {cacheable false}
+ {aggregating false}
+ }
+
+ # The general superclass for includelets
+
+ Class create ::xowiki::Includelet \
+ -superclass ::xo::Context \
+ -parameter {
+ {name ""}
+ {title ""}
+ {__decoration "portlet"}
+ {parameter_declaration {}}
+ {id}
+ }
+#2.8.0r4
+ ::xowiki::Includelet proc require_YUI_CSS {{-version 2.7.0} {-ajaxhelper true} path} {
+ if {$ajaxhelper} {
+ ::xo::Page requireCSS "/resources/ajaxhelper/yui/$path"
+ } else {
+ ::xo::Page requireCSS "http://yui.yahooapis.com/$version/build/$path"
+ }
+ }
+
+ ::xowiki::Includelet proc require_YUI_JS {{-version 2.7.0} {-ajaxhelper true} path} {
+ if {$ajaxhelper} {
+ ::xo::Page requireJS "/resources/ajaxhelper/yui/$path"
+ } else {
+ ::xo::Page requireJS "http://yui.yahooapis.com/$version/build/$path"
+ }
+ }
+
+ ::xowiki::Includelet proc describe_includelets {includelet_classes} {
+ #my log "--plc=$includelet_classes "
+ foreach cl $includelet_classes {
+ set result ""
+ append result "{{[namespace tail $cl]"
+ foreach p [$cl info parameter] {
+ if {[llength $p] != 2} continue
+ foreach {name value} $p break
+ if {$name eq "parameter_declaration"} {
+ foreach pp $value {
+ #append result ""
+ switch [llength $pp] {
+ 1 {append result " $pp"}
+ 2 {
+ set v [lindex $pp 1]
+ if {$v eq ""} {set v {""}}
+ append result " [lindex $pp 0] $v"
+ }
+ }
+ #append result "\n"
+ }
+ }
+ }
+ append result "}}\n"
+ my set html([namespace tail $cl]) $result
+ my describe_includelets [$cl info subclass]
+ }
+ }
+ ::xowiki::Includelet proc available_includelets {} {
+ if {[my array exists html]} {my array unset html}
+ my describe_includelets [::xowiki::Includelet info subclass]
+ set result " $gc_link #general-comments.Comments# No pages with parent object [$p name], page_order not NULL and an appropriate publish status found Collaboration Graph for [::xo::get_user_name $user_id] in this wiki"
+ if {[array size i] < 1} {
+ append result " No collaborations found No activities found Last $total activities were done by user " \
+ "[::xo::get_user_name $user_id]."
+ } else {
+ append result " Collaborations in last $total activities by [array size user] Users in this wiki For more details, see [$page set title] Do you want to create page $name new?"
+ } else {
+ return ""
+ }
+ }
+ Package array set delegate_link_to_target {
+ csv-dump 1 download 1 list 1
+ }
+ Package instproc invoke {-method {-error_template error-template} {-batch_mode 0}} {
+ set page_or_package [my resolve_page [my set object] method]
+ #my log "--r resolve_page => $page_or_package"
+ if {$page_or_package ne ""} {
+ if {[$page_or_package istype ::xowiki::FormPage]
+ && [$page_or_package is_link_page]
+ && [[self class] exists delegate_link_to_target($method)]} {
+ # if the target is a link, we may want to call the method on the target
+ set target [$page_or_package get_target_from_link_page]
+ #my msg "delegate $method from $page_or_package [$page_or_package name] to $target [$target name]"
+ if {$target ne ""} {set page_or_package $target}
+ }
+ if {[$page_or_package procsearch $method] eq ""} {
+ return [my error_msg "Method '$method' is not defined for this object"]
+ } else {
+ #my msg "--invoke [my set object] id=$page_or_package method=$method ([my id] batch_mode $batch_mode)"
+ if {$batch_mode} {[my id] set __batch_mode 1}
+ set r [my call $page_or_package $method ""]
+ if {$batch_mode} {[my id] unset __batch_mode}
+ return $r
+ }
+ } else {
+ # the requested page was not found, provide an error message and
+ # an optional link for creating the page
+ set path [::xowiki::Includelet html_encode [my set object]]
+ set edit_snippet [my create_new_snippet $path]
+ return [my error_msg -status_code 404 -template_file $error_template \
+ "Page '$path' is not available. $edit_snippet"]
+ }
+ }
+
+ Package instproc error_msg {{-template_file error-template} {-status_code 200} error_msg} {
+ my instvar id
+ if {![regexp {^[./]} $template_file]} {
+ set template_file /packages/xowiki/www/$template_file
+ }
+ set context [list [$id instance_name]]
+ set title Error
+ set header_stuff [::xo::Page header_stuff]
+ set index_link [my make_link -privilege public -link "" $id {} {}]
+ set link [my query_parameter "return_url" ""]
+ if {$link ne ""} {set back_link $link}
+ set top_includelets ""; set content $error_msg
+ ::xo::cc set status_code $status_code
+ $id return_page -adp $template_file -variables {
+ context title index_link back_link header_stuff error_msg
+ top_includelets content
+ }
+ }
+
+ Package instproc get_page_from_item_or_revision_id {item_id} {
+ set revision_id [my query_parameter revision_id 0]
+ set [expr {$revision_id ? "item_id" : "revision_id"}] 0
+ #my log "--instantiate item_id $item_id revision_id $revision_id"
+ return [::xo::db::CrClass get_instance_from_db -item_id $item_id -revision_id $revision_id]
+ }
+
+ Package instproc resolve_page {{-use_package_path true} {-simple false} -lang object method_var} {
+ #
+ # Try to resolve from object (path) and query parameter the called
+ # object (might be a packge or page) and the method to be called.
+ #
+ # @return instantiated object (Page or Package) or empty
+ #
+ upvar $method_var method
+ my instvar id
+
+ # get the default language if not specified
+ if {![info exists lang]} {
+ set lang [my default_language]
+ }
+ #my msg "resolve_page '$object', default-lang $lang"
+
+ #
+ # First, resolve package level methods,
+ # having the syntax PACKAGE_URL?METHOD&....
+ #
+
+ if {$object eq ""} {
+ #
+ # We allow only to call methods defined by the policy
+ #
+ set exported [[my set policy] defined_methods Package]
+ foreach m $exported {
+ #my log "--QP my exists_query_parameter $m = [my exists_query_parameter $m] || [my exists_form_parameter $m]"
+ if {[my exists_query_parameter $m] || [my exists_form_parameter $m]} {
+ set method $m ;# determining the method, similar file extensions
+ return [self]
+ }
+ }
+ }
+
+ if {[string match "//*" $object]} {
+ # we have a reference to another instance, we cant resolve this from this package.
+ # Report back not found
+ return ""
+ }
+
+ #my log "--o object is '$object'"
+ if {$object eq ""} {
+ # we have no object, but as well no method callable on the package
+ set object [$id get_parameter index_page "index"]
+ #my log "--o object is now '$object'"
+ }
+ #
+ # second, resolve object level
+ #
+ #my msg "call item_info_from url"
+ array set "" [my item_info_from_url -with_package_prefix false -default_lang $lang $object]
+
+ if {$(item_id) ne 0} {
+ if {$(method) ne ""} { set method $(method) }
+ return [my get_page_from_item_or_revision_id $(item_id)]
+ }
+ if {$simple} { return ""}
+ #my msg "NOT found object=$object"
+
+ # try standard page
+ set standard_page [$id get_parameter $(stripped_name)_page]
+ if {$standard_page ne ""} {
+ #
+ # allow for now mapped standard pages just on the toplevel
+ #
+ set page [my get_page_from_item_ref \
+ -allow_cross_package_item_refs false \
+ -use_package_path true \
+ -use_site_wide_pages true \
+ -use_prototype_pages true \
+ -default_lang $lang \
+ -parent_id [my folder_id] \
+ $standard_page]
+ #my log "--o resolving standard_page '$standard_page' returns $page"
+ if {$page ne ""} {
+ return $page
+ }
+ # Maybe we are calling from a different language, but the
+ # standard page with en: was already instantiated.
+ #set standard_page "en:$stripped_object"
+ #set page [my resolve_request -default_lang en -path $standard_page method]
+ #my msg "resolve -default_lang en -path $standard_page returns --> $page"
+ #if {$page ne ""} {
+ # return $page
+ #}
+ }
+
+ # Maybe, a prototype page was imported with language en:, but the current language is different
+ #if {$lang ne "en"} {
+ # set page [my resolve_request -default_lang en -path $stripped_object method]
+ # #my msg "resolve -default_lang en -path $stripped_object returns --> $page"
+ # if {$page ne ""} {
+ # return $page
+ # }
+ #}
+
+ if {$use_package_path} {
+ # Check for this page along the package path
+ #my msg "check along package path"
+ foreach package [my package_path] {
+ set page [$package resolve_page -simple true -lang $lang $object method]
+ if {$page ne ""} {
+ #my msg "set_resolve_context inherited -package_id [my id] -parent_id [my folder_id]"
+ $page set_resolve_context -package_id [my id] -parent_id [my folder_id]
+ return $page
+ }
+ }
+ #my msg "package path done [array get {}]"
+ }
+
+ set page [::xowiki::Package get_site_wide_page -name en:$(stripped_name)]
+ #my msg "get_site_wide_page for en:'$(stripped_name)' returned '$page' (stripped name)"
+ if {$page ne ""} {
+ #my msg "set_resolve_context site-wide -package_id [my id] -parent_id [my folder_id]"
+ $page set_resolve_context -package_id [my id] -parent_id [my folder_id]
+ return $page
+ }
+
+ #my msg "we have to try to import a prototype page for $stripped_object"
+ set page [my import-prototype-page $(stripped_name)]
+ if {$page ne ""} {
+ return $page
+ }
+ my log "no prototype for '$object' found"
+ return $page
+ }
+
+ Package instproc package_path {} {
+ #
+ # Compute a list fo package objects which should be used for
+ # resolving ("inheritance of objects from other instances").
+ #
+ set packages [list]
+ set package_url [string trimright [my package_url] /]
+ set package_path [my get_parameter PackagePath]
+ #
+ # To avoid recursions, remove the current package from the list of
+ # packages if was accidentally included. Get the package objects
+ # from the remaining URLs.
+ #
+ foreach package_instance_url $package_path {
+ #my msg "compare $package_instance_url eq $package_url"
+ if {$package_instance_url eq $package_url} continue
+ lappend packages ::[::xowiki::Package initialize \
+ -url $package_instance_url/[my set object] \
+ -keep_cc true -init_url false]
+ }
+ # final sanity check, in case package->initialize is broken
+ set p [lsearch $packages ::[my id]]
+ if {$p > -1} {set packages [lreplace $packages $p $p]}
+
+ #my msg "[my id] packages=$packages, p=$p"
+ return $packages
+ }
+
+ Package instproc prefixed_lookup {{-default_lang ""} -lang:required -stripped_name:required -parent_id:required} {
+ # todo unify with package->lookup
+ #
+ # This method tries a direct lookup of stripped_name under
+ # parent_id followed by a prefixed lookup. The direct lookup is
+ # only performed, when $default-lang == $lang. The prefixed lookup
+ # might change lang in the result set.
+ #
+ # @return item-ref info
+ #
+
+ set item_id 0
+ if {$lang eq $default_lang || [string match *:* $stripped_name]} {
+ # try a direct lookup; ($lang eq "file" needed for links to files)
+ set item_id [::xo::db::CrClass lookup -name $stripped_name -parent_id $parent_id]
+ if {$item_id != 0} {
+ set name $stripped_name
+ regexp {^(..):(.+)$} $name _ lang stripped_name
+ #my log "direct $stripped_name"
+ }
+ }
+
+ # TODO
+ #my log ">>>>>>>> HERE HERE item_id=$item_id"
+ if { $item_id == 0 } {
+ set item_id [my get_page_from_super -folder_id $parent_id $stripped_name]
+ if { $item_id != 0 } {
+ set name $stripped_name
+ }
+ }
+
+ if {$item_id == 0} {
+ set name ${lang}:$stripped_name
+ set item_id [::xo::db::CrClass lookup -name $name -parent_id $parent_id]
+ #my log "comp $name"
+ }
+ return [list item_id $item_id parent_id $parent_id \
+ lang $lang stripped_name $stripped_name name $name ]
+ }
+
+ Package instproc lookup {
+ {-use_package_path true}
+ {-use_site_wide_pages false}
+ {-default_lang ""}
+ -name:required
+ {-parent_id ""}
+ } {
+ # Lookup name (with maybe cross-package references) from a
+ # given parent_id or from the list of configured instances
+ # (obtained via package_path).
+ #
+ array set "" [my get_package_id_from_page_name -default_lang $default_lang $name]
+ #my msg "result = [array get {}]"
+ if {![info exists (package_id)]} {
+ return 0
+ }
+
+ if {$parent_id eq ""} {set parent_id [$(package_id) folder_id]}
+ set item_id [::xo::db::CrClass lookup -name $(page_name) -parent_id $parent_id]
+ #my log "lookup $(page_name) $parent_id in package $(package_id) returns $item_id, parent_id $parent_id"
+
+ # Test for "0" is only needed when we want to create the first root folder
+ if {$item_id == 0 && $parent_id ne "0"} {
+ #
+ # Page not found so far. Is the parent-page a regular page and a folder-link?
+ # If so, de-reference the link.
+ #
+ set p [::xo::db::CrClass get_instance_from_db -item_id $parent_id]
+ if {[$p istype ::xowiki::FormPage] && [$p is_link_page] && [$p is_folder_page]} {
+ set target [$p get_target_from_link_page]
+ #my log "LINK LOOKUP from target-package [$target package_id] source package $(package_id)"
+ return [[$target package_id] lookup \
+ -use_package_path $use_package_path \
+ -use_site_wide_pages $use_site_wide_pages \
+ -default_lang $default_lang \
+ -name $name \
+ -parent_id [$target item_id]]
+ }
+ }
+
+ if {$item_id == 0 && $use_package_path} {
+ #
+ # Page not found so far. Is the page inherited along the package
+ # path?
+ #
+ foreach package [my package_path] {
+ set item_id [$package lookup -name $name]
+ #my msg "lookup from package $package $name returns $item_id"
+ if {$item_id != 0} break
+ }
+ }
+
+ if {$item_id == 0 && $use_site_wide_pages} {
+ #
+ # Page not found so far. Is the page a site_wide page?
+ #
+ set item_id [::xowiki::Package lookup_side_wide_page -name $name]
+ }
+
+ return $item_id
+ }
+
+ #
+ # Resolving item refs
+ # (symbolic references to content items and content folders)
+ #
+
+ Package ad_instproc item_ref {
+ {-use_package_path false}
+ {-use_site_wide_pages false}
+ {-normalize_name true}
+ -default_lang:required
+ -parent_id:required
+ link
+ } {
+
+ An item_ref refers to an item (existing or nonexisting) in the
+ content repository relative to some parent_id. The item might be
+ either a folder or some kind of "page" (e.g. a file). An item_ref
+ might be complex, i.e. consist of a path of simple_item_refs,
+ separated by "/". An item_ref stops at the first unknown part in
+ the path and returns item_id == 0 and the appropriate parent_id
+ (and name etc.) for insertion.
+
+ @return item info containing link_type form prefix stripped_name item_id parent_id
+
+ } {
+ # A trailing slash says that the last element is a folder. We
+ # substitute it to allow easy iteration over the slash separated
+ # segments.
+ if {[string match */ $link]} {
+ set llink [string trimright $link /]\0
+ } else {
+ set llink $link
+ }
+
+ set elements [split $llink /]
+ # Get start-page, if path is empty
+ if {[llength $elements] == 0} {
+ set link [my get_parameter index_page "index"]
+ set elements [list $link]
+ }
+
+ # Iterate until the first unknown element appears in the path
+ # (we can handle only one unknown at a time).
+ set nr_elements [llength $elements]
+ set n 0
+ set ref_ids {}
+ foreach element $elements {
+ set (last_parent_id) $parent_id
+ lappend ref_ids $parent_id
+ array set "" [my simple_item_ref \
+ -normalize_name $normalize_name \
+ -use_package_path $use_package_path \
+ -use_site_wide_pages $use_site_wide_pages \
+ -default_lang $default_lang \
+ -parent_id $parent_id \
+ -assume_folder [expr {[incr n] < $nr_elements}] \
+ $element]
+ #my log "$element => [array get {}]"
+ if {$(item_id) == 0} {
+ set parent_id $(parent_id)
+ break
+ } else {
+ set parent_id $(item_id)
+ }
+ }
+
+ return [list link $link link_type $(link_type) form $(form) \
+ prefix $(prefix) stripped_name $(stripped_name) \
+ item_id $(item_id) parent_id $(parent_id) ref_ids $ref_ids]
+ }
+
+ Package instproc simple_item_ref {
+ -default_lang:required
+ -parent_id:required
+ {-use_package_path true}
+ {-use_site_wide_pages false}
+ {-normalize_name true}
+ {-assume_folder:required false}
+ element
+ } {
+ if {$normalize_name} {
+ set element [my normalize_name $element]
+ }
+ #my log el=[string map [list \0 MARKER] $element]-assume_folder=$assume_folder,parent_id=$parent_id
+ set (form) ""
+ set use_default_lang 0
+
+ if {[regexp {^(file|image|js|css|swf):(.+)$} $element _ (link_type) (stripped_name)]} {
+ # (typed) file links
+ set (prefix) file
+ set name file:$(stripped_name)
+ } elseif {[regexp {^folder:(.+)$} $element _ (stripped_name)]} {
+ # (typed) file links
+ array set "" [list prefix "" link_type link form "en:folder.form"]
+ set name $(stripped_name)
+ } elseif {[regexp {^(..):([^:]{3,}?):(..):(.+)$} $element _ form_lang form (prefix) (stripped_name)]} {
+ array set "" [list link_type "link" form "$form_lang:$form.form"]
+ set name $(prefix):$(stripped_name)
+ #my msg "FIRST case name=$name, form=$form_lang:$form"
+ } elseif {[regexp {^(..):([^:]{3,}?):(.+)$} $element _ form_lang form (stripped_name)]} {
+ array set "" [list link_type "link" form "$form_lang:$form.form" prefix $default_lang]
+ set name $default_lang:$(stripped_name)
+ set use_default_lang 1
+ #my msg "SECOND case name=$name, form=$form_lang:$form"
+ } elseif {[regexp {^([^:]{3,}?):(..):(.+)$} $element _ form (prefix) (stripped_name)]} {
+ array set "" [list link_type "link" form "$default_lang:$form.form"]
+ set name $(prefix):$(stripped_name)
+ #my msg "THIRD case name=$name, form=$default_lang:$form"
+ } elseif {[regexp {^([^:]{3,}?):(.+)$} $element _ form (stripped_name)]} {
+ array set "" [list link_type "link" form "$default_lang:$form.form" prefix $default_lang]
+ set name $default_lang:$(stripped_name)
+ set use_default_lang 1
+ #my msg "FOURTH case name=$name, form=$default_lang:$form"
+ } elseif {[regexp {^(..):(.+)$} $element _ (prefix) (stripped_name)]} {
+ array set "" [list link_type "link"]
+ set name $(prefix):$(stripped_name)
+ } elseif {[regexp {^(.+)\0$} $element _ (stripped_name)]} {
+ array set "" [list link_type "link" form "en:folder.form" prefix ""]
+ set name $(stripped_name)
+ } elseif {$assume_folder} {
+ array set "" [list link_type "link" form "en:folder.form" prefix "" stripped_name $element]
+ set name $element
+ } else {
+ array set "" [list link_type "link" prefix $default_lang stripped_name $element]
+ set name $default_lang:$element
+ set use_default_lang 1
+ }
+
+ if {$use_default_lang && $default_lang eq ""} {
+ my log "WARNING: Trying to use empty default lang on link '$element' => $name"
+ }
+
+ set name [string trimright $name \0]
+ set (stripped_name) [string trimright $(stripped_name) \0]
+
+ if {$element eq "" || $element eq "\0"} {
+ set folder_id [my folder_id]
+ array set "" [my item_info_from_id $folder_id]
+ set item_id $folder_id
+ set parent_id $(parent_id)
+ #my msg "SETTING item_id $item_id parent_id $parent_id // [array get {}]"
+ } elseif {$element eq "." || $element eq ".\0"} {
+ array set "" [my item_info_from_id $parent_id]
+ set item_id $parent_id
+ set parent_id $(parent_id)
+ } elseif {$element eq ".." || $element eq "..\0"} {
+ set id [::xo::db::CrClass get_parent_id -item_id $parent_id]
+ if {$id > 0} {
+ # refuse to traverse past root folder
+ set parent_id $id
+ }
+ array set "" [my item_info_from_id $parent_id]
+ set item_id $parent_id
+ set parent_id $(parent_id)
+ } else {
+ # with the following construct we need in most cases just 1 lookup
+
+ set item_id [my lookup \
+ -use_package_path $use_package_path \
+ -use_site_wide_pages $use_site_wide_pages \
+ -name $name -parent_id $parent_id]
+ #my log "[my id] lookup -use_package_path $use_package_path -name $name -parent_id $parent_id => $item_id"
+
+ if {$item_id == 0} {
+ #
+ # The first lookup was not successful, so we try again.
+ #
+ if {$(link_type) eq "link" && $element eq $(stripped_name)} {
+ #
+ # try a direct lookup, in case it is a folder
+ #
+ set item_id [my lookup \
+ -use_package_path $use_package_path \
+ -use_site_wide_pages $use_site_wide_pages \
+ -name $(stripped_name) -parent_id $parent_id]
+ #my msg "try again direct lookup, parent_id $parent_id $(stripped_name) => $item_id"
+ if {$item_id > 0} {array set "" [list prefix ""]}
+ }
+
+ if {$item_id == 0 && $(link_type) eq "link" && $assume_folder && $(prefix) eq ""} {
+ set item_id [my lookup \
+ -use_package_path $use_package_path \
+ -use_site_wide_pages $use_site_wide_pages \
+ -name $default_lang:$element -parent_id $parent_id]
+ if {$item_id > 0} {array set "" [list link_type "link" prefix $default_lang stripped_name $element]
+ }
+ }
+
+ if {$item_id == 0 && $(link_type) eq "link" && $use_default_lang && $(prefix) ne "en"} {
+ #
+ # If the name was not specified explicitely (we are using
+ # $default_lang), try again with language "en" try again,
+ # maybe element is folder in a different language
+ #
+ set item_id [my lookup \
+ -use_package_path $use_package_path \
+ -use_site_wide_pages $use_site_wide_pages \
+ -name en:$(stripped_name) -parent_id $parent_id]
+ #my msg "try again in en en:$(stripped_name) => $item_id"
+ if {$item_id > 0} {array set "" [list link_type "link" prefix en]}
+ }
+
+ # If the item is still unknown, try filename-based lookup,
+ # when the entry looks like a filename with an extension.
+ if {$item_id == 0 && [string match *.* $element] && ![regexp {[.](form|wf)$} $element]} {
+ #
+ # Get the mime type to distinguish between images, flash
+ # files and ordinary files.
+ #
+ set mime_type [::xowiki::guesstype $name]
+ set (prefix) file
+ switch -glob $mime_type {
+ "image/*" {
+ set name file:$(stripped_name)
+ set (link_type) image
+ }
+ application/x-shockwave-flash {
+ set name file:$(stripped_name)
+ set (link_type) swf
+ }
+ default {
+ set name file:$(stripped_name)
+ if {![info exists (link_type)]} {set (link_type) file}
+ }
+ }
+ set item_id [my lookup \
+ -use_package_path $use_package_path \
+ -use_site_wide_pages $use_site_wide_pages \
+ -name file:$(stripped_name) -parent_id $parent_id]
+ }
+ }
+ }
+
+ #my msg "return link_type $(link_type) prefix $(prefix) stripped_name $(stripped_name) form $(form) parent_id $parent_id item_id $item_id"
+ return [list link_type $(link_type) prefix $(prefix) stripped_name $(stripped_name) \
+ form $(form) parent_id $parent_id item_id $item_id ]
+ }
+
+ Package instproc item_info_from_id {
+ item_id
+ } {
+ #
+ # Obtain (partial) item info from id. It does not handle
+ # e.g. special link_types as for e.g file|image|js|css|swf, etc.
+ #
+ ::xo::db::CrClass get_instance_from_db -item_id $item_id
+ set name [$item_id name]
+ set parent_id [$item_id parent_id]
+ if {[$item_id is_folder_page]} {
+ return [list link_type "folder" prefix "" stripped_name $name parent_id $parent_id]
+ }
+ regexp {^(.+):(.+)$} $name _ prefix stripped_name
+ return [list link_type "link" prefix $prefix stripped_name $stripped_name parent_id $parent_id]
+ }
+
+ Package instproc item_info_from_url {{-with_package_prefix true} {-default_lang ""} url} {
+ #
+ # Obtain item info (item_id parent_id lang stripped_name) from the
+ # specified url. Search starts always at the root.
+ #
+ # @parm with_package_prefix flag, if provided url contains package-url
+ # @return item ref data (parent_id lang stripped_name method)
+ #
+ if {$with_package_prefix && [string match /* $url]} {
+ set url [string range $url [string length [my package_url]] end]
+ }
+ if {$default_lang eq ""} {set default_lang [my default_language]}
+ my get_lang_and_name -default_lang $default_lang -path $url (lang) stripped_url
+ set (parent_id) [my get_parent_and_name \
+ -lang $(lang) -path $stripped_url \
+ -parent_id [my folder_id] \
+ parent (stripped_name)]
+
+ #my msg "get_parent_and_name '$stripped_url' returns [array get {}]"
+
+ if {![regexp {^(download)/(.+)$} $(lang) _ (method) (lang)]} {
+ set (method) ""
+ # The lang value "tag" is used for allowing tag-urls without
+ # parameters, since several tag harvester assume such a syntax
+ # and don't process arguments. We rewrite in such cases simply
+ # the url and query parameters and update the connection
+ # context.
+ if {$(lang) eq "tag"} {
+ # todo: missing: tag links to subdirectories, also on url generation
+ set tag $stripped_url
+ set summary [::xo::cc query_parameter summary 0]
+ set popular [::xo::cc query_parameter popular 0]
+ set tag_kind [expr {$popular ? "ptag" :"tag"}]
+ set weblog_page [my get_parameter weblog_page]
+ my get_lang_and_name -default_lang $default_lang -name $weblog_page (lang) (stripped_name)
+ #set name $(lang):$(stripped_name)
+ my set object $weblog_page
+ ::xo::cc set actual_query $tag_kind=$tag&summary=$summary
+ }
+ }
+ array set "" [my prefixed_lookup -parent_id $(parent_id) \
+ -default_lang $default_lang -lang $(lang) -stripped_name $(stripped_name)]
+ #my msg "prefixed_lookup '$(stripped_name)' returns [array get {}]"
+
+ if {$(item_id) == 0} {
+ # check link (todo should happen in package->lookup?)
+ ::xo::db::CrClass get_instance_from_db -item_id $(parent_id)
+ if {[$(parent_id) is_link_page] && [$(parent_id) is_folder_page]} {
+ set target [$(parent_id) get_target_from_link_page]
+ #$target set_resolve_context -package_id [my id] -parent_id $(parent_id)
+ #my msg "LINK prefixed LOOKUP from target-package [$target package_id] source package [my id]"
+ array set "" [[$target package_id] prefixed_lookup -parent_id [$target item_id] \
+ -default_lang $default_lang -lang $(lang) -stripped_name $(stripped_name)]
+ #my msg "-lang $(lang) -stripped_name $(stripped_name) => got=$(item_id)"
+ }
+ }
+ return [array get ""]
+ }
+
+ Package instproc get_page_from_item_ref {
+ {-allow_cross_package_item_refs true}
+ {-use_package_path false}
+ {-use_site_wide_pages true}
+ {-use_prototype_pages false}
+ {-default_lang ""}
+ {-parent_id ""}
+ link
+ } {
+ #
+ # Get page from an item ref name (either with language prefix or
+ # not). First it try to resolve the item_ref from the actual
+ # package. If not successful, it checks optionally along the
+ # package_path and on the side-wide pages.
+ #
+ # @return page object or empty ("").
+ #
+ #my log "get_page_from_item_ref [self args]"
+
+ if {$allow_cross_package_item_refs && [string match //* $link]} {
+
+ # todo check: get_package_id_from_page_name uses a different lookup based on site nodes
+
+ set referenced_package_id [my resolve_package_path $link rest_link]
+ #my log "get_page_from_item_ref $link recursive rl?[info exists rest_link] in $referenced_package_id"
+ if {$referenced_package_id != 0 && $referenced_package_id != [my id]} {
+ # TODO: we have still to check, whether or not we want
+ # site-wide-pages etc. in cross package links, and if, under
+ # which parent we would like to create newly importage pages.
+ #
+ # For now, we do not want to create pages this way, we pass
+ # the root folder of the referenced package as start
+ # parent_page for the search and turn off all page creation
+ # facilities.
+
+ #my log cross-package
+ return [$referenced_package_id get_page_from_item_ref \
+ -allow_cross_package_item_refs false \
+ -use_package_path false \
+ -use_site_wide_pages false \
+ -use_prototype_pages false \
+ -default_lang $default_lang \
+ -parent_id [$referenced_package_id folder_id] \
+ $rest_link]
+ } else {
+ # it is a link to the same package, we start search for page at top.
+ set link $rest_link
+ set search_parent_id ""
+ }
+ } else {
+ set search_parent_id $parent_id
+ }
+
+ #my log "my folder [my folder_id]"
+
+ if {$search_parent_id eq ""} {
+ set search_parent_id [my folder_id]
+ }
+ if {$parent_id eq ""} {
+ set parent_id [my folder_id]
+ }
+ #my log call-item_ref-on:$link-parent_id=$parent_id,search_parent_id=$search_parent_id
+ array set "" [my item_ref -normalize_name false \
+ -use_package_path $use_package_path \
+ -use_site_wide_pages $use_site_wide_pages \
+ -default_lang $default_lang \
+ -parent_id $search_parent_id \
+ $link]
+
+ #my msg "[my instance_name] (root [my folder_id]) item-ref for '$link' search parent $search_parent_id, parent $parent_id, returns\n[array get {}]"
+ if {$(item_id)} {
+ set page [::xo::db::CrClass get_instance_from_db -item_id $(item_id)]
+ if {[$page package_id] ne [my id] || [$page parent_id] != $(parent_id)} {
+ #my msg "set_resolve_context site_wide_pages [my id] and -parent_id $parent_id"
+ $page set_resolve_context -package_id [my id] -parent_id $parent_id
+ }
+ return $page
+ }
+
+ if {!$(item_id) && $use_prototype_pages} {
+ array set "" [my item_ref \
+ -normalize_name false \
+ -default_lang $default_lang \
+ -parent_id $parent_id \
+ $link]
+ set page [::xowiki::Package import_prototype_page \
+ -package_key [my package_key] \
+ -name $(stripped_name) \
+ -parent_id $(parent_id) \
+ -package_id [my id] ]
+ #my msg "import_prototype_page for '$(stripped_name)' => '$page'"
+ if {$page ne ""} {
+ # we want to be able to address the page via ::$item_id
+ set page [::xo::db::CrClass get_instance_from_db -item_id [$page item_id]]
+ }
+ return $page
+ }
+
+ return ""
+ }
+
+ #
+ # import for prototype pages
+ #
+
+ Package instproc import-prototype-page {{prototype_name ""}} {
+ set page ""
+ if {$prototype_name eq ""} {
+ set prototype_name [my query_parameter import-prototype-page ""]
+ set via_url 1
+ }
+ if {$prototype_name eq ""} {
+ error "No name for prototype given"
+ }
+
+ set page [::xowiki::Package import_prototype_page \
+ -package_key [my package_key] \
+ -name $prototype_name \
+ -parent_id [my folder_id] \
+ -package_id [my id] ]
+
+ if {[info exists via_url] && [my exists_query_parameter "return_url"]} {
+ my returnredirect [my query_parameter "return_url" [my package_url]]
+ }
+ return $page
+ }
+
+ Package proc import_prototype_page {
+ -package_key:required
+ -name:required
+ -parent_id:required
+ -package_id:required
+ } {
+ set page ""
+ set fn [get_server_root]/packages/$package_key/www/prototypes/$name.page
+ my log "--W check $fn"
+ if {[file readable $fn]} {
+ my instvar id
+ # We have the file of the prototype page. We try to create
+ # either a new item or a revision from definition in the file
+ # system.
+ if {[regexp {^(..):(.*)$} $name _ lang local_name]} {
+ set fullName $name
+ } else {
+ set fullName en:$name
+ }
+ my log "--sourcing page definition $fn, using name '$fullName'"
+ set page [source $fn]
+ $page configure -name $fullName \
+ -parent_id $parent_id -package_id $package_id
+ # xowiki::File has a different interface for build-name to
+ # derive the "name" from a file-name. This is not important for
+ # prototype pages, so we skip it
+ if {![$page istype ::xowiki::File]} {
+ $page name [$page build_name]
+ }
+ if {![$page exists title]} {
+ $page set title $object
+ }
+ $page destroy_on_cleanup
+ $page set_content [string trim [$page text] " \n"]
+ $page initialize_loaded_object
+ set p [$package_id get_page_from_name -name $fullName -parent_id $parent_id]
+ if {$p eq ""} {
+ # We have to create the page new. The page is completed with
+ # missing vars on save_new.
+ $page save_new
+ } else {
+ # An old page exists already, make a revision. Update the
+ # existing page with all scalar variables from the prototype
+ # page (which is just partial)
+ foreach v [$page info vars] {
+ if {[$page array exists $v]} continue ;# don't copy arrays
+ $p set $v [$page set $v]
+ }
+ $p save
+ set page $p
+ }
+ if {$page ne ""} {
+ # we want to be able to address the page via the canonical name ::$item_id
+ set page [::xo::db::CrClass get_instance_from_db -item_id [$page item_id]]
+ }
+ }
+ return $page
+ }
+
+ Package proc require_site_wide_pages {
+ {-refetch:boolean false}
+ } {
+ set parent_id -100
+ set package_id [::xowiki::Package first_instance]
+ ::xowiki::Package require $package_id
+ #::xowiki::Package initialize -package_id $package_id -init_url false -keep_cc true
+ set package_key "xowiki"
+
+ foreach n {folder.form link.form page.form import-archive.form photo.form} {
+ set item_id [::xo::db::CrClass lookup -name en:$n -parent_id $parent_id]
+ #my ds "lookup en:$n => $item_id"
+ if {!$item_id || $refetch} {
+ set page [::xowiki::Package import_prototype_page \
+ -name $n \
+ -package_key $package_key \
+ -parent_id $parent_id \
+ -package_id $package_id ]
+ my log "Page en:$n loaded as '$page'"
+ }
+ }
+ }
+
+ Package proc lookup_side_wide_page {-name:required} {
+ return [::xo::db::CrClass lookup -name $name -parent_id -100]
+ }
+
+ Package proc get_site_wide_page {-name:required} {
+ set item_id [my lookup_side_wide_page -name $name]
+ #my ds "lookup from base objects $name => $item_id"
+ if {$item_id} {
+ set page [::xo::db::CrClass get_instance_from_db -item_id $item_id]
+ ::xo::Package require [$page package_id]
+ return $page
+ }
+ return ""
+ }
+
+ Package instproc call {object method options} {
+ my instvar policy id
+ set allowed [$policy enforce_permissions \
+ -package_id $id -user_id [::xo::cc user_id] \
+ $object $method]
+ if {$allowed} {
+ #my log "--p calling $object ([$object name] [$object info class]) '$method'"
+ eval $object $method $options
+ } else {
+ my log "not allowed to call $object $method"
+ }
+ }
+ Package instforward check_permissions {%my set policy} %proc
+
+ Package ad_instproc require_root_folder {
+ {-parent_id -100}
+ {-content_types {}}
+ -name:required
+ } {
+ Make sure, the root folder for the given package exists. If not,
+ create it and register all allowed content types.
+
+ @return folder_id
+ } {
+ my instvar id
+
+ set folder_id [ns_cache eval xotcl_object_type_cache root_folder-$id {
+
+ set folder_id [::xo::db::CrClass lookup -name $name -parent_id $parent_id]
+ if {$folder_id == 0} {
+ ::xowiki::Package require_site_wide_pages
+ set form_id [::xowiki::Weblog instantiate_forms -forms en:folder.form -package_id $id]
+ set f [FormPage new -destroy_on_cleanup \
+ -name $name \
+ -text "" \
+ -package_id $id \
+ -parent_id $parent_id \
+ -nls_language en_US \
+ -publish_status ready \
+ -instance_attributes {} \
+ -page_template $form_id]
+ $f save_new
+ set folder_id [$f item_id]
+
+ ::xo::db::sql::acs_object set_attribute -object_id_in $folder_id \
+ -attribute_name_in context_id -value_in $id
+
+ my log "CREATED folder '$name' and parent $parent_id ==> $folder_id"
+ }
+
+ # register all specified content types
+ #::xo::db::CrFolder register_content_types \
+ # -folder_id $folder_id \
+ # -content_types $content_types
+ #my log "returning from cache folder_id $folder_id"
+ return $folder_id
+ }]
+ #my log "returning from require folder_id $folder_id"
+ return $folder_id
+ }
+
+ Package instproc require_folder_object { } {
+ set folder_id [my require_root_folder -name "xowiki: [my id]" \
+ -content_types ::xowiki::Page* ]
+ ::xo::db::CrClass get_instance_from_db -item_id $folder_id
+ my set folder_id $folder_id
+ }
+
+
+ ###############################################################
+ #
+ # user callable methods on package level
+ #
+
+ Package ad_instproc refresh-login {} {
+ Force a refresh of a login and do a redict. Intended for use from ajax.
+ } {
+ set return_url [my query_parameter return_url]
+ if {[::xo::cc user_id] == 0} {
+ set url [subsite::get_url]register
+ ad_returnredirect [export_vars -base $url return_url]
+ } else {
+ ad_returnredirect $return_url
+ }
+ }
+
+ #
+ # reindex (for site wide search)
+ #
+
+ Package ad_instproc reindex {} {
+ reindex all items of this package
+ } {
+ my instvar folder_id id
+ set pages [db_list [my qn get_pages] {
+ select page_id,package_id from xowiki_page, cr_revisions r, cr_items ci, acs_objects o
+ where page_id = r.revision_id and ci.item_id = r.item_id and ci.live_revision = page_id
+ and publish_status = 'ready'
+ and page_id = o.object_id and o.package_id = :id
+ }]
+ #my log "--reindex returns <$pages>"
+ foreach page_id $pages {
+ #search::queue -object_id $page_id -event DELETE
+ search::queue -object_id $page_id -event INSERT
+ }
+ ad_returnredirect .
+ }
+
+ #
+ # change-page-order (normally called via ajax POSTs)
+ #
+ Package ad_instproc change-page-order {} {
+ Change Page Order for pages by renumbering and filling gaps.
+ } {
+ my instvar folder_id
+ set to [string trim [my form_parameter to ""]]
+ set from [string trim [my form_parameter from ""]]
+ set clean [string trim [my form_parameter clean ""]] ;# only for inserts
+
+ #set from {1.2 1.3 1.4}; set to {1.3 1.4 1.2}; set clean {...}
+ #set from {1.2 1.3 1.4}; set to {1.3 1.4 2.1 1.2}; set clean {2.1}
+ #set from {1 2}; set to {1 1.2 2}; set clean {1.2 1.3 1.4}
+
+ if {$from eq "" || $to eq "" || [llength $to]-[llength $from] >1 || [llength $to]-[llength $from]<0} {
+ my log "unreasonable request from='$from', to='$to'"
+ return
+ }
+ my log "--cpo from=$from, to=$to, clean=$clean"
+ set gap_renames [list]
+ #
+ # We distinguish two cases:
+ # - pure reordering: length(to) == length(from)
+ # - insert from another section: length(to) == length(from)+1
+ #
+ if {[llength $to] == [llength $from]} {
+ my log "--cpo reorder"
+ } elseif {[llength $clean] > 1} {
+ my log "--cpo insert"
+ #
+ # We have to fill the gap. First, find the newly inserted
+ # element in $to.
+ #
+ foreach e $to {
+ if {[lsearch -exact $from $e] == -1} {
+ set inserted $e
+ break
+ }
+ }
+ if {![info exists inserted]} {error "invalid 'to' list (no inserted element detected)"}
+ #
+ # compute the remaining list
+ #
+ set remaining [list]
+ foreach e $clean {if {$e ne $inserted} {lappend remaining $e}}
+ #
+ # compute rename rename commands for it
+ #
+ set gap_renames [::xowiki::utility page_order_renames -parent_id $folder_id \
+ -start [lindex $clean 0] -from $remaining -to $remaining]
+ foreach {page_id item_id name old_page_order new_page_order} $gap_renames {
+ my log "--cpo gap $page_id (name) rename $old_page_order to $new_page_order"
+ }
+ }
+ #
+ # Compute the rename commands for the drop target
+ #
+ set drop_renames [::xowiki::utility page_order_renames -parent_id $folder_id \
+ -start [lindex $from 0] -from $from -to $to]
+ #my log "--cpo drops l=[llength $drop_renames]"
+ foreach {page_id item_id name old_page_order new_page_order} $drop_renames {
+ my log "--cpo drop $page_id ($name) rename $old_page_order to $new_page_order"
+ }
+
+ #
+ # Perform the actual renames
+ #
+ set temp_obj [::xowiki::Page new -name dummy -volatile]
+ set slot [$temp_obj find_slot page_order]
+ db_transaction {
+ foreach {page_id item_id name old_page_order new_page_order} [concat $drop_renames $gap_renames] {
+ #my log "--cpo UPDATE $page_id new_page_order $new_page_order"
+ $temp_obj item_id $item_id
+ $temp_obj update_attribute_from_slot -revision_id $page_id $slot $new_page_order
+ ::xo::clusterwide ns_cache flush xotcl_object_cache ::$item_id
+ ::xo::clusterwide ns_cache flush xotcl_object_cache ::$page_id
+ }
+ }
+ #
+ # Flush the page fragement caches (page fragments based on page_order might be sufficient)
+ my flush_page_fragment_cache -scope agg
+ ns_return 200 text/plain ok
+ }
+
+
+
+ #
+ # RSS 2.0 support
+ #
+ Package ad_instproc rss {
+ -maxentries
+ -name_filter
+ -entries_of
+ -title
+ -days
+ } {
+ Report content of xowiki folder in rss 2.0 format. The
+ reporting order is descending by date. The title of the feed
+ is taken from the title, the description
+ is taken from the description field of the folder object.
+
+ @param maxentries maximum number of entries retrieved
+ @param days report entries changed in speficied last days
+
+ } {
+ set package_id [my id]
+ set folder_id [$package_id folder_id]
+ if {![info exists name_filter]} {
+ set name_filter [my get_parameter -type word name_filter ""]
+ }
+ if {![info exists entries_of]} {
+ set entries_of [my get_parameter entries_of ""]
+ }
+ if {![info exists title]} {
+ set title [my get_parameter PackageTitle [my instance_name]]
+ }
+ set description [my get_parameter PackageDescription ""]
+
+ if {![info exists days] &&
+ [regexp {[^0-9]*([0-9]+)d} [my query_parameter rss] _ days]} {
+ # setting the variable days
+ } else {
+ set days 10
+ }
+
+ set r [RSS new -destroy_on_cleanup \
+ -package_id [my id] \
+ -parent_ids [my query_parameter parent_ids ""] \
+ -name_filter $name_filter \
+ -entries_of $entries_of \
+ -title $title \
+ -description $description \
+ -days $days]
+
+ #set t text/plain
+ set t text/xml
+ ns_return 200 $t [$r render]
+ }
+
+ #
+ # Google sitemap support
+ #
+
+ Package ad_instproc google-sitemap {
+ {-max_entries ""}
+ {-changefreq "daily"}
+ {-priority "0.5"}
+ } {
+ Report content of xowiki folder in google site map format
+ https://www.google.com/webmasters/sitemaps/docs/en/protocol.html
+
+ @param maxentries maximum number of entries retrieved
+ @param package_id to determine the xowiki instance
+ @param changefreq changefreq as defined by google
+ @param priority priority as defined by google
+
+ } {
+ set package_id [my id]
+ set folder_id [::$package_id folder_id]
+
+ set timerange_clause ""
+
+ set content {
+ "
+ set importer [Importer new -package_id [my id] -parent_id $parent_id -user_id $user_id]
+ $importer import_all -replace $replace -objects $objects -create_user_ids $create_user_ids
+ append msg [$importer report]
+ }
+
+ Package instproc flush_references {-item_id:integer,required -name -parent_id} {
+ my instvar id folder_id
+ if {![info exists parent_id]} {
+ set parent_id [::xo::db::CrClass get_parent_id -item_id $item_id]
+ }
+ if {![info exists name]} {
+ set name [::xo::db::CrClass get_name -item_id $item_id]
+ }
+ my flush_name_cache -name $name -parent_id $parent_id
+ }
+
+ Package instproc flush_name_cache {-name:required -parent_id:required} {
+ # Different machines in the cluster might have different entries in their caches.
+ # Since we use wild-cards to find these, it has to be done on every machine
+ ::xo::clusterwide xo::cache_flush_all xowiki_cache link-*-$name-$parent_id
+ ::xo::clusterwide ns_cache flush xotcl_object_type_cache $parent_id-$name
+ }
+
+ Package instproc delete_revision {-revision_id:required -item_id:required} {
+ ::xo::clusterwide ns_cache flush xotcl_object_cache ::$item_id
+ ::xo::clusterwide ns_cache flush xotcl_object_cache ::$revision_id
+ ::xo::db::sql::content_revision del -revision_id $revision_id
+ }
+
+ Package instproc delete {-item_id -name -parent_id} {
+ #
+ # This delete method does not require an instanantiated object,
+ # while the class-specific delete methods in xowiki-procs need these.
+ # If a (broken) object can't be instantiated, it cannot be deleted.
+ # Therefore we need this package level delete method.
+ # While the class specific methods are used from the
+ # application pages, the package_level method is used from the admin pages.
+ #
+ #my log "--D delete [self args]"
+ #
+ my instvar id
+ #
+ # if no item_id given, take it from the query parameter
+ #
+ if {![info exists item_id]} {
+ set item_id [my query_parameter item_id]
+ #my log "--D item_id from query parameter $item_id"
+ }
+ #
+ # if no name is given, take it from the query parameter
+ #
+ if {![info exists name]} {
+ set name [my query_parameter name]
+ }
+
+ if {$item_id eq ""} {
+ array set "" [my item_info_from_url -with_package_prefix false $name]
+ if {$(item_id) == 0} {
+ ns_log notice "lookup of '$name' with parent_id $parent_id failed"
+ } else {
+ set parent_id $(parent_id)
+ set item_id $(item_id)
+ set name $(name)
+ }
+ } else {
+ set name [::xo::db::CrClass get_name -item_id $item_id]
+ if {![info exists parent_id]} {
+ set parent_id [::xo::db::CrClass get_parent_id -item_id $item_id]
+ }
+ }
+ #my msg item_id=$item_id/name=$name
+
+ if {$item_id ne ""} {
+ my log "--D trying to delete $item_id $name"
+ set object_type [::xo::db::CrClass get_object_type -item_id $item_id]
+ # In case of PageTemplate and subtypes, we need to check
+ # for pages using this template
+ set classes [concat $object_type [$object_type info heritage]]
+ if {[lsearch $classes "::xowiki::PageTemplate"] > -1} {
+ set count [::xowiki::PageTemplate count_usages -item_id $item_id -publish_status all]
+ if {$count > 0} {
+ return [$id error_msg \
+ [_ xowiki.error-delete_entries_first [list count $count]]]
+ }
+ }
+ if {[my get_parameter "with_general_comments" 0]} {
+ #
+ # We have general comments. In a first step, we have to delete
+ # these, before we are able to delete the item.
+ #
+ set comment_ids [db_list [my qn get_comments] \
+ "select comment_id from general_comments where object_id = $item_id"]
+ foreach comment_id $comment_ids {
+ my log "-- deleting comment $comment_id"
+ ::xo::db::sql::content_item del -item_id $comment_id
+ }
+ }
+ foreach child_item_id [::xo::db::CrClass get_child_item_ids -item_id $item_id] {
+ my flush_references -item_id $child_item_id
+ }
+ $object_type delete -item_id $item_id
+ my flush_references -item_id $item_id -name $name -parent_id $parent_id
+ my flush_page_fragment_cache -scope agg
+ } else {
+ my log "--D nothing to delete!"
+ }
+ my returnredirect [my query_parameter "return_url" [$id package_url]]
+ }
+
+ Package instproc flush_page_fragment_cache {{-scope agg}} {
+ switch -- $scope {
+ agg {set key PF-[my id]-agg-*}
+ all {set key PF-[my id]-*}
+ default {error "unknown scope for flushing page fragment cache"}
+ }
+ foreach entry [ns_cache names xowiki_cache $key] {
+ ns_log notice "::xo::clusterwide ns_cache flush xowiki_cache $entry"
+ ::xo::clusterwide ns_cache flush xowiki_cache $entry
+ }
+ }
+
+ #
+ # Perform per connection parameter caching. Using the
+ # per-connection cache speeds later lookups up by a factor of 15.
+ # Repeated parameter lookups are quite likely
+ #
+
+ Class ParameterCache
+ ParameterCache instproc get_parameter {{-check_query_parameter true} {-type ""} attribute {default ""}} {
+ set key [list [my id] [self proc] $attribute]
+ if {[info command "::xo::cc"] ne ""} {
+ if {[::xo::cc cache_exists $key]} {
+ return [::xo::cc cache_get $key]
+ }
+ return [::xo::cc cache_set $key [next]]
+ } else {
+ # in case, we have no ::xo::cc (e.g. during bootstrap).
+ ns_log notice "warning: no ::xo::cc available, returning default for parameter $attribute"
+ return $default
+ }
+ }
+ Package instmixin add ParameterCache
+
+
+ #
+ # policy management
+ #
+
+ Package instproc condition=has_class {query_context value} {
+ return [expr {[$query_context query_parameter object_type ""] eq $value}]
+ }
+ Package instproc condition=has_name {query_context value} {
+ return [regexp $value [$query_context query_parameter name ""]]
+ }
+
+ Class create Policy -superclass ::xo::Policy
+
+ Policy policy1 -contains {
+
+ Class Package -array set require_permission {
+ reindex swa
+ change-page-order {{id admin}}
+ import-prototype-page swa
+ refresh-login none
+ rss none
+ google-sitemap none
+ google-sitemapindex none
+ manage-categories {{id admin}}
+ edit-category-tree {{id admin}}
+ delete {{id admin}}
+ edit-new {
+ {{has_class ::xowiki::Object} swa}
+ {{has_class ::xowiki::FormPage} nobody}
+ {{has_name {[.](js|css)$}} id admin}
+ {id create}
+ }
+ }
+
+ Class Page -array set require_permission {
+ view none
+ revisions {{package_id write}}
+ diff {{package_id write}}
+ edit {
+ {{regexp {name {(weblog|index)$}}} package_id admin}
+ {package_id write}
+ }
+ save-form-data {{package_id write}}
+ save-attributes {{package_id write}}
+ make-live-revision {{package_id write}}
+ delete-revision {{package_id admin}}
+ delete {{package_id admin}}
+ save-tags login
+ popular-tags login
+ create-new {{parent_id create}}
+ create-or-use {{parent_id create}}
+ } -set default_permission {{package_id write}}
+
+ Class Object -array set require_permission {
+ edit swa
+ }
+ Class File -array set require_permission {
+ download none
+ }
+ Class Form -array set require_permission {
+ list {{package_id read}}
+ edit admin
+ view admin
+ }
+ Class CrFolder -array set require_permission {
+ view none
+ delete {{package_id admin}}
+ edit-new {{item_id write}}
+ }
+ }
+
+ Policy policy2 -contains {
+ #
+ # we require side wide admin rights for deletions and code
+ #
+
+ Class Package -array set require_permission {
+ reindex {{id admin}}
+ rss none
+ refresh-login none
+ google-sitemap none
+ google-sitemapindex none
+ change-page-order {{id admin}}
+ manage-categories {{id admin}}
+ edit-category-tree {{id admin}}
+ delete swa
+ edit-new {
+ {{has_class ::xowiki::Object} swa}
+ {{has_class ::xowiki::FormPage} nobody}
+ {{has_name {[.](js|css)$}} swa}
+ {id create}
+ }
+ }
+
+ Class Page -array set require_permission {
+ view {{package_id read}}
+ revisions {{package_id write}}
+ diff {{package_id write}}
+ edit {
+ {{regexp {name {(weblog|index)$}}} package_id admin}
+ {package_id write}
+ }
+ save-attributes {{package_id write}}
+ make-live-revision {{package_id write}}
+ delete-revision swa
+ delete swa
+ save-tags login
+ popular-tags login
+ create-new {{parent_id create}}
+ create-or-use {{parent_id create}}
+ }
+
+ Class Object -array set require_permission {
+ edit swa
+ }
+ Class File -array set require_permission {
+ download {{package_id read}}
+ }
+ Class Form -array set require_permission {
+ view admin
+ edit admin
+ list {{package_id read}}
+ }
+ }
+
+ Policy policy3 -contains {
+ #
+ # we require side wide admin rights for deletions
+ # we perform checking on item_ids for pages.
+ #
+
+ Class Package -array set require_permission {
+ reindex {{id admin}}
+ rss none
+ refresh-login none
+ google-sitemap none
+ google-sitemapindex none
+ change-page-order {{id admin}}
+ manage-categories {{id admin}}
+ edit-category-tree {{id admin}}
+ delete swa
+ edit-new {
+ {{has_class ::xowiki::Object} swa}
+ {{has_class ::xowiki::FormPage} nobody}
+ {{has_name {[.](js|css)$}} swa}
+ {id create}
+ }
+ }
+
+ Class Page -array set require_permission {
+ view {{item_id read}}
+ revisions {{item_id write}}
+ diff {{item_id write}}
+ edit {{item_id write}}
+ make-live-revision {{item_id write}}
+ save-attributes {{package_id write}}
+ delete-revision swa
+ delete swa
+ save-tags login
+ popular-tags login
+ create-new {{parent_id create}}
+ create-or-use {{parent_id create}}
+ }
+
+ Class Object -array set require_permission {
+ edit swa
+ }
+ Class File -array set require_permission {
+ download {{package_id read}}
+ }
+ Class Form -array set require_permission {
+ view admin
+ edit admin
+ list {{item_id read}}
+ }
+# Class FormPage -array set require_permission {
+# view {
+# {{is_true {_creation_user = @current_user@}} item_id read}
+# swa
+# }
+# }
+ }
+
+ #Policy policy4 -contains {
+ # ::xotcl::Object function -array set require_permission {
+ # f none
+ # } -set default_permission login
+ #}
+
+ #my log "--set granted [policy4 check_permissions -user_id 0 -package_id 0 function f]"
+
+ #
+ # an example with in_state condition...
+ #
+ Policy policy5 -contains {
+
+ Class Package -array set require_permission {
+ reindex {{id admin}}
+ rss none
+ refresh-login none
+ google-sitemap none
+ google-sitemapindex none
+ change-page-order {{id admin}}
+ manage-categories {{id admin}}
+ edit-category-tree {{id admin}}
+ delete swa
+ edit-new {
+ {{has_class ::xowiki::Object} swa}
+ {{has_class ::xowiki::FormPage} nobody}
+ {{has_name {[.](js|css)$}} swa}
+ {id create}
+ }
+ }
+
+ Class Page -array set require_permission {
+ view {{item_id read}}
+ revisions {{item_id write}}
+ diff {{item_id write}}
+ edit {{item_id write}}
+ save-attributes {{item_id write}}
+ make-live-revision {{item_id write}}
+ delete-revision swa
+ delete swa
+ save-tags login
+ popular-tags login
+ create-new {{parent_id create}}
+ create-or-use {{parent_id create}}
+ }
+
+ Class Object -array set require_permission {
+ edit swa
+ }
+ Class File -array set require_permission {
+ download {{package_id read}}
+ }
+ Class FormPage -array set require_permission {
+ view creator
+ edit {
+ {{in_state initial|suspended|working} creator} admin
+ }
+ }
+ Class Form -array set require_permission {
+ view admin
+ edit admin
+ list admin
+ }
+ }
+
+}
+
+::xo::library source_dependent
+
+
+
Index: openacs-4/packages/xowiki/tcl/syndicate-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/syndicate-procs.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/xowiki/tcl/syndicate-procs.tcl 13 Sep 2012 16:05:28 -0000 1.40
@@ -0,0 +1,569 @@
+namespace eval ::xowiki {
+ #
+ # RSS 2.0 support
+ #
+ Class XMLSyndication -parameter {package_id}
+
+ XMLSyndication instproc init {} {
+ my set xmlMap [list & "&" < "<" > ">" \" """ ' "'"]
+ }
+
+ XMLSyndication instproc tag {{-atts } name value} {
+ my instvar xmlMap
+ set attsXML ""
+ if {[info exists atts]} {
+ foreach {attName attValue} $atts {
+ append attsXML " $attName='[string map [list ' {'} { } { }] $attValue]'"
+ }
+ }
+ return <$name$attsXML>[string map $xmlMap $value]$name>
+ }
+
+ Class create RSS -superclass XMLSyndication -parameter {
+ maxentries
+ {parent_ids ""}
+ {name_filter ""}
+ {entries_of ""}
+ {days ""}
+ {css ""}
+ {siteurl "[ad_url]"}
+ {description ""}
+ {language en-us}
+ {title ""}
+ } \
+ -ad_doc {
+ Report content of xowiki folder in rss 2.0 format. The
+ reporting order is descending by date. The title of the feed
+ is taken from the title, the description
+ is taken from the description field of the folder object.
+
+ @param maxentries maximum number of entries retrieved
+ @param days report entries changed in speficied last days
+ @param name_filter include only pages matching the provided regular expression (postgres)
+ }
+
+ RSS instproc css_link {} {
+ my instvar css
+ if {$css ne ""} {
+ #
+ # firefox 2.0 appears to overwrite the style info, so one has to use such ugly tricks:
+ # http://www.blingblog.info/2006/10/30/firefox-big-browser/
+ # when we want to use custom style sheets
+ #
+ set user_agent [string tolower [ns_set get [ns_conn headers] User-Agent]]
+ set filler [expr {[string first firefox $user_agent] >- 1 ?
+ "" : ""
+ }]
+ set css_link [expr {[string match "/*" $css] ? $css : "/resources/xowiki/$css"}]
+ return "\n\n$filler"
+ }
+ return ""
+ }
+
+ RSS instproc head {} {
+ my instvar title link description language
+ return "[my css_link]
+ Comparing
+ #file-storage.lt_Comments_on_this_file#
+ @title@ (@page_context@)
@title@
+
+Contributors
+ \n"
+ }
+ return $result
+ }
+
+ Chat proc initialize_nsvs {} {;} ;# noop
+
+ Chat proc login {-chat_id -package_id -mode} {
+ my log "--"
+ auth::require_login
+ if {![info exists package_id]} {set package_id [ad_conn package_id] }
+ if {![info exists chat_id]} {set chat_id $package_id }
+ set context id=$chat_id&s=[ad_conn session_id].[clock seconds]
+ set path [lindex [site_node::get_url_from_object_id -object_id $package_id] 0]
+
+ if {![info exists mode]} {
+ set mode polling
+ if {[info command ::thread::mutex] ne "" &&
+ ![catch {ns_conn contentsentlength}]} {
+ # we seem to have libthread installed, and the patch for obtaining the tcl-stream
+ # from a connection thread, so we can use the background delivery thread;
+ # scripted streaming should work everywhere
+ set mode scripted-streaming
+ if {[regexp (firefox) [string tolower [ns_set get [ns_conn headers] User-Agent]]]} {
+ # for firefox, we could use the nice mode without the spinning load indicator
+ # currently, streaming mode seems broken with current firefox...
+ #set mode streaming
+ }
+ }
+ my log "--chat mode $mode"
+ }
+
+ switch $mode {
+ polling {
+ ::xo::Page requireJS "/resources/xowiki/get-http-object.js"
+ set jspath packages/xowiki/www/ajax/chat.js
+ set login_url ${path}ajax/chat?m=login&$context
+ set get_update "chatSendCmd(\"$path/ajax/chat?m=get_new&$context\",chatReceiver)"
+ set get_all "chatSendCmd(\"$path/ajax/chat?m=get_all&$context\",chatReceiver)"
+ }
+ streaming {
+ set jspath packages/xowiki/www/ajax/streaming-chat.js
+ set subscribe_url ${path}ajax/chat?m=subscribe&$context
+ }
+ scripted-streaming {
+ append context &mode=scripted
+ set jspath packages/xowiki/www/ajax/scripted-streaming-chat.js
+ set subscribe_url ${path}ajax/chat?m=subscribe&$context
+ }
+ }
+ set send_url ${path}ajax/chat?m=add_msg&$context&msg=
+
+ if { ![file exists [acs_root_dir]/$jspath] } {
+ return -code error "File [acs_root_dir]/$jspath does not exist"
+ }
+ set file [open [acs_root_dir]/$jspath]; set js [read $file]; close $file
+
+ my log "--CHAT mode=$mode"
+
+ switch $mode {
+ polling {return "\
+
+ "
+ }
+
+
+ streaming {return "\
+
+
+ "
+ }
+ }
+ }
+}
+
Index: openacs-4/packages/xowiki/tcl/folder-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/folder-procs.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/xowiki/tcl/folder-procs.tcl 13 Sep 2012 16:05:26 -0000 1.17
@@ -0,0 +1,928 @@
+::xo::library doc {
+
+ This is an experimental implemetation for folders
+ based on xowiki form pages. In particular, this file provides
+
+ * An xowiki includelet to display the "folders"
+ * An xowiki includelet to display the "child-resources"
+ of a page (e.g. the contents of a folder)
+
+ @author Michael Aram
+ @author Gustaf Neumann
+}
+
+::xo::library require xowiki-procs
+::xo::library require includelet-procs
+::xo::library require form-field-procs
+::xo::library require -package xotcl-core 30-widget-procs
+
+namespace eval ::xowiki::includelet {
+ ###########################################################
+ #
+ # ::xowiki::includelet::folders
+ #
+ ###########################################################
+ ::xowiki::IncludeletClass create folders \
+ -superclass ::xowiki::Includelet \
+ -cacheable false \
+ -parameter {
+ {__decoration plain}
+ {parameter_declaration {
+ {-show_full_tree false}
+ {-context_tree_view false}
+ }}
+ {id "[xowiki::Includelet js_name [self]]"}
+ }
+
+ folders instproc include_head_entries {} {
+ ::xowiki::Tree include_head_entries -renderer yuitree -style folders
+ }
+
+ folders instproc render {} {
+ my get_parameters
+ set js "
+ var [my js_name];
+ YAHOO.util.Event.onDOMReady(function() {
+ [my js_name] = new YAHOO.widget.TreeView('foldertree_[my id]');
+ [my js_name].subscribe('clickEvent',function(oArgs) {
+ var m = /href=\"(\[^\"\]+)\"/.exec(oArgs.node.html);
+ return false;
+ });
+ [my js_name].render();
+ });
+ "
+ set tree [my build_tree]
+ return [$tree render -style yuitree -js $js]
+ }
+
+ folders instproc collect_folders {
+ -package_id:required
+ -folder_form_id:required
+ -link_form_id:required
+ {-subtree_query ""}
+ {-depth 3}
+ } {
+ set folders [list]
+
+ # safety belt, for recursive structures
+ if {$depth < 1} {return $folders}
+
+ #
+ # get folders
+ #
+ set folder_pages [::xowiki::FormPage get_form_entries \
+ -base_item_ids $folder_form_id -form_fields "" \
+ -extra_where_clause $subtree_query \
+ -publish_status ready -package_id $package_id]
+ #
+ # get links
+ #
+ set links [::xowiki::FormPage get_form_entries \
+ -base_item_ids $link_form_id -form_fields "" \
+ -extra_where_clause $subtree_query \
+ -publish_status ready -package_id $package_id]
+ #my msg "[llength [$links children]] links"
+
+ set folders [$folder_pages children]
+ my instvar current_folder_id
+
+ #
+ # filter links to folders.
+ # links might be cross-package links
+ #
+ foreach l [$links children] {
+ set link_type [$l get_property_from_link_page link_type]
+ set cross_package [$l get_property_from_link_page cross_package]
+
+ if {$link_type ne "folder_link"} continue
+
+ if {$cross_package} {
+ #
+ # we found a cross-package link. These kind of links require further queries
+ #
+ set target [$l get_target_from_link_page]
+
+ # the following clause needs an oracle counter-part
+ set tree_sortkey [db_string get_tree_sort_key "select tree_sortkey from acs_objects where object_id = [$target item_id]"]
+ set extra_where "and bt.item_id in (select object_id from acs_objects \
+ where tree_sortkey between '$tree_sortkey' and tree_right('$tree_sortkey') \
+ and object_type = 'content_item')"
+
+ set sub_folders [my collect_folders -package_id [$target package_id] \
+ -folder_form_id $folder_form_id -link_form_id $link_form_id \
+ -subtree_query $extra_where -depth [expr {$depth -1}]]
+
+
+ foreach f $sub_folders {
+
+ #my msg "$f [$f name] is a folder-link pointing to $target [$target name] current $current_folder_id"
+ if {[$f parent_id] eq [$target item_id]} {
+ #my msg "1 found child [$f name] and reset parent_id from [$f parent_id] to [$l item_id], package_id [$l package_id]"
+ #
+ # reset the current_folder if necessary
+ #
+ if {$current_folder_id eq [$f parent_id]} {set current_folder_id [$l item_id]}
+ #
+ # set the resolve_context
+ #
+ $f set_resolve_context -package_id [$l package_id] -parent_id [$l item_id]
+ #
+ # TODO we could save the double-fetch by collecing in
+ # get_form_entries via item-ids, not via new-objects
+ #
+ ::xo::db::CrClass get_instance_from_db -item_id [$f item_id]
+ [$f item_id] set_resolve_context -package_id [$l package_id] -parent_id [$l item_id]
+ } else {
+ #my msg "2 found child [$f name] and reset parent_id from [$f parent_id] to [$f parent_id], package id [$l package_id]"
+ $f set_resolve_context -package_id [$l package_id] -parent_id [$f parent_id]
+ ::xo::db::CrClass get_instance_from_db -item_id [$f item_id]
+ [$f item_id] set_resolve_context -package_id [$l package_id] -parent_id [$f parent_id]
+ }
+
+ #my msg "including $f [$f name] [$f item_id]"
+ lappend folders $f
+ }
+ }
+ #my msg link=$link
+ lappend folders $l
+ }
+ return $folders
+ }
+
+ folders instproc build_tree {} {
+ my instvar current_folder current_folder_id folder_form_id link_form_id
+ my get_parameters
+
+ set with_links 0
+
+ set page [my set __including_page]
+ set package_id [::xo::cc package_id]
+ #my ds [::xo::cc serialize]
+ set lang [::xo::cc lang]
+ #set lang en
+ set return_url [::xo::cc url]
+ set nls_language [$page get_nls_language_from_lang $lang]
+
+ set folder_form_id [::xowiki::Weblog instantiate_forms -forms en:folder.form \
+ -package_id $package_id]
+ set link_form_id [::xowiki::Weblog instantiate_forms -forms en:link.form \
+ -package_id $package_id]
+ #my msg folder_form=$folder_form_id
+
+ set current_folder [$page get_folder -folder_form_ids $folder_form_id]
+ set current_folder_id [$current_folder item_id]
+
+ #my msg "FOLDERS [$page name] package_id $package_id current_folder $current_folder [$current_folder name]"
+
+ # Start with the "package's folder" as root folder
+ set root_folder_id [::$package_id folder_id]
+ set root_folder [::xo::db::CrClass get_instance_from_db -item_id $root_folder_id]
+ set root_folder_is_current [expr {$current_folder_id == [$root_folder item_id]}]
+
+ set mb [info command ::__xowiki__MenuBar]
+ if {$mb ne ""} {
+ #
+ # We have a menubar. Add folder-specific content to the
+ # menubar.
+ #
+ if {$root_folder_is_current} {
+ #
+ # We do not want to see unneeded parent_ids in the links. When
+ # we insert to the root folder, set opt_parent_id to empty to
+ # make argument passing easy. "make_link" just checks for the
+ # existance of the variable, so we unset parent_id in this case.
+ #
+ set opt_parent_id ""
+ set folder_link [$package_id package_url]
+ if {[info exists parent_id]} {unset parent_id}
+ } else {
+ set parent_id $current_folder_id
+ set opt_parent_id $parent_id
+ ::xo::db::CrClass get_instance_from_db -item_id $parent_id
+ set folder_link [$current_folder pretty_link]
+ }
+ set return_url [::xo::cc url]
+ set new_folder_link [$package_id make_form_link -form en:folder.form \
+ -parent_id $opt_parent_id \
+ -return_url $return_url]
+ if {$with_links} {
+ set new_sym_link [$package_id make_form_link -form en:link.form \
+ -parent_id $opt_parent_id \
+ -nls_language $nls_language -return_url $return_url]
+ }
+# set new_page_link [$package_id make_link -with_entities 0 \
+# $package_id edit-new \
+# {object_type ::xowiki::Page} \
+# parent_id return_url autoname template_file]
+
+ set new_page_link [$package_id make_form_link -form en:page.form \
+ -parent_id $opt_parent_id \
+ -return_url $return_url]
+ set new_file_link [$package_id make_link -with_entities 0 \
+ $package_id edit-new \
+ {object_type ::xowiki::File} \
+ parent_id return_url autoname template_file]
+ set new_form_link [$package_id make_link -with_entities 0 \
+ $package_id edit-new \
+ {object_type ::xowiki::Form} \
+ parent_id return_url autoname template_file]
+ set import_link [$package_id make_link -privilege admin \
+ -link "admin/import" $package_id {} parent_id return_url]
+ set import_archive_link [$package_id make_form_link -form en:import-archive.form \
+ -parent_id $opt_parent_id]
+
+ set index_link [$package_id make_link -link $folder_link $current_folder list]
+
+ $mb add_menu_item -name Package.Startpage \
+ -item [list text #xowiki.index# url $index_link]
+
+ $mb add_menu_item -name New.Page \
+ -item [list text #xowiki.new# url $new_page_link]
+ $mb add_menu_item -name New.File \
+ -item [list text File url $new_file_link]
+ $mb add_menu_item -name New.Folder \
+ -item [list text Folder url $new_folder_link]
+ if {$with_links} {
+ $mb add_menu_item -name New.SymLink \
+ -item [list text SymLink url $new_sym_link]
+ }
+ $mb add_menu_item -name New.Form \
+ -item [list text Form url $new_form_link]
+ $mb add_menu_item -name Package.ImportDump -item [list url $import_link]
+ $mb add_menu_item -name Package.ImportArchive -item [list url $import_archive_link]
+
+ if {[::xowiki::clipboard is_empty]} {
+ set clipboard_copy_link ""
+ set clipboard_export_link ""
+ set clipboard_content_link ""
+ set clipboard_clear_link ""
+ } else {
+ # todo: check, whether the use is allowed to insert into the current folder
+ set clipboard_copy_link [$current_folder pretty_link]?m=clipboard-copy
+ set clipboard_export_link [$current_folder pretty_link]?m=clipboard-export
+ set clipboard_content_link [$current_folder pretty_link]?m=clipboard-content
+ set clipboard_clear_link [$current_folder pretty_link]?m=clipboard-clear
+ }
+ # todo: we should check either, whether to user is allowed to
+ # copy-to-clipboard from the current folder, and/or the user is
+ # allowed to do this with certain items.... (the latter in
+ # clipboad-add)
+ $mb add_menu_item -name Clipboard.Add \
+ -item [list url javascript:acs_ListBulkActionClick("objects","$folder_link?m=clipboard-add")]
+ $mb add_menu_item -name Clipboard.Content -item [list url $clipboard_content_link]
+ $mb add_menu_item -name Clipboard.Clear -item [list url $clipboard_clear_link]
+ $mb add_menu_item -name Clipboard.Use.Copy -item [list url $clipboard_copy_link]
+ $mb add_menu_item -name Clipboard.Use.Export -item [list url $clipboard_export_link]
+
+ # A folder page can contain extra menu entries (sample
+ # below). Iterate of the extra_menu property and add according
+ # menu entries.
+ foreach me [$current_folder property extra_menu_entries] {
+ array unset ""
+ set kind [lindex $me 0]
+ if {[string range $kind 0 0] eq "#"} continue
+ switch $kind {
+ clear_menu {
+ # sample entry: clear_menu -menu New
+ array set "" [lrange $me 1 end]
+ $mb clear_menu -menu $(-menu)
+ }
+
+ form_link -
+ entry {
+ # sample entry: form_entry -name New.YouTubeLink -label YouTube -form en:YouTube.form
+ if {$kind eq "form_link"} {
+ my log "$me, name 'form_link' is deprecated, use 'entry' instead"
+ }
+ array set "" [lrange $me 1 end]
+ if {[info exists (-form)]} {
+ set link [$package_id make_form_link -form $(-form) \
+ -parent_id $opt_parent_id \
+ -nls_language $nls_language -return_url $return_url]
+ } elseif {[info exists (-object_type)]} {
+ set link [$package_id make_link -with_entities 0 \
+ $package_id edit-new \
+ [list object_type $(-object_type)] \
+ parent_id return_url autoname template_file]
+ } else {
+ my log "Warning: no link specified"
+ set link ""
+ }
+ set item [list url $link]
+ if {[info exists (-label)]} {lappend item text $(-label)}
+ $mb add_menu_item -name $(-name) -item $item
+ }
+
+ default { error "unknown kind of menu entry: $kind" }
+ }
+ }
+ }
+
+ set top_folder_of_tree $root_folder
+ #
+ # Check, if the optional context tree view is activated
+ #
+ if {$context_tree_view || [$package_id get_parameter FolderContextTreeView false]} {
+ set parent_id [$current_folder parent_id]
+ if {$parent_id ne -100} {
+ set top_folder_of_tree $parent_id
+ #my msg top_folder_of_tree=$top_folder_of_tree
+ }
+ }
+
+ set parent_folder [$top_folder_of_tree parent_id]
+ if {$top_folder_of_tree eq $root_folder || $parent_folder eq "-100"} {
+ set href [::$package_id package_url]
+ set label [::$package_id instance_name]
+ #my msg "use instance name"
+ } else {
+ set href [$top_folder_of_tree pretty_link]
+ set label "[$top_folder_of_tree title] ..."
+ }
+
+ set t [::xowiki::Tree new -id foldertree_[my id] ]
+ set node [::xowiki::TreeNode new \
+ -href $href \
+ -label $label \
+ -highlight [expr {$current_folder_id == [$top_folder_of_tree item_id]}] \
+ -object $top_folder_of_tree \
+ -expanded 1 \
+ -open_requests 1]
+ $t add $node
+ set folders [my collect_folders \
+ -package_id $package_id \
+ -folder_form_id $folder_form_id \
+ -link_form_id $link_form_id]
+
+ #my msg "folder [my set folder_form_id] has [llength $folders] entries"
+ #foreach f $folders {lappend _ [$f item_id]}; my msg $_
+
+ my build_sub_tree -node $node -folders $folders
+ return $t
+ }
+
+ folders instproc build_sub_tree {
+ {-node}
+ {-folders}
+
+ } {
+ my get_parameters
+ my instvar current_folder_id
+
+ set current_object [$node object]
+ set current_item_id [$current_object item_id]
+
+ set sub_folders [list]
+ set remaining_folders [list]
+ foreach f $folders {
+ if {[$f parent_id] ne $current_item_id} {
+ lappend remaining_folders $f
+ } else {
+ lappend sub_folders $f
+ }
+ }
+
+ foreach c $sub_folders {
+
+ set label [$c title]
+ set object $c
+ set folder_href [$c pretty_link]
+
+ set is_current [expr {$current_folder_id eq [$c item_id]}]
+ set is_open [expr {$is_current || $show_full_tree}]
+
+ #regexp {^..:(.+)$} $label _ label
+
+ set subnode [::xowiki::TreeNode new \
+ -href $folder_href \
+ -label $label \
+ -object $c \
+ -highlight $is_current \
+ -expanded $is_open \
+ -open_requests 1]
+ $node add $subnode
+
+ if {$is_current} {
+ $node open_tree
+
+ if {[info command ::__xowiki__MenuBar] ne ""
+ && [::__xowiki__MenuBar exists submenu_pages(folder)]} {
+ set owner [::__xowiki__MenuBar set submenu_owner(folder)]
+ $subnode add_pages -full true \
+ -book_mode [$owner set book_mode] \
+ -owner $owner \
+ [::__xowiki__MenuBar set submenu_pages(folder)]
+ }
+ }
+
+ my build_sub_tree -node $subnode -folders $remaining_folders
+ }
+ }
+}
+
+
+namespace eval ::xowiki::includelet {
+
+ ###########################################################
+ #
+ # ::xowiki::includelet::child-resources
+ #
+ ###########################################################
+ ::xowiki::IncludeletClass create child-resources \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {
+ parameter_declaration {
+ {-skin:optional "yui-skin-sam"}
+ {-show_types "::xowiki::Page,::xowiki::File,::xowiki::Form,::xowiki::FormPage"}
+ {-regexp:optional}
+ {-with_subtypes:optional false}
+ {-orderby:optional "last_modified,desc"}
+ {-publish_status "ready"}
+ {-view_target ""}
+ {-html-content}
+ {-parent .}
+ {-hide}
+ }
+ }
+ }
+
+ child-resources instproc types_to_show {} {
+ my get_parameters
+ foreach type [split $show_types ,] {set ($type) 1}
+ return [lsort [array names ""]]
+ }
+
+ child-resources instproc render {} {
+ my get_parameters
+
+ set current_folder [my set __including_page]
+
+ if {$parent eq ".."} {
+ set current_folder [$current_folder parent_id]
+ ::xo::db::CrClass get_instance_from_db -item_id $current_folder
+ }
+ if {![$current_folder istype ::xowiki::FormPage]} {
+ # current folder has to be a FormPage
+ set current_folder [$current_folder parent_id]
+ if {![$current_folder istype ::xowiki::FormPage]} {
+ error "child-resources not included from a FormPage"
+ }
+ }
+ set current_folder_id [$current_folder item_id]
+
+ if {[::xo::cc query_parameter m] ne "list" && $parent ne ".."} {
+ set index [$current_folder property index]
+ if {$index ne ""} {
+ set download [string match "file:*" $index]
+ set index_link [$package_id pretty_link \
+ -parent_id [$current_folder item_id] \
+ -download $download \
+ $index]
+ return [$package_id returnredirect $index_link]
+ }
+ }
+
+ set logical_folder_id $current_folder_id
+ if {[$current_folder exists physical_item_id]} {
+ set current_folder_id [$current_folder set physical_item_id]
+ }
+
+ $package_id instvar package_key
+
+ set return_url [::xo::cc url] ;#"[$package_id package_url]edit-done"
+ set category_url [export_vars -base [$package_id package_url] { {manage-categories 1} {object_id $package_id}}]
+
+ set columns {objects edit object_type name last_modified delete}
+ foreach column $columns {set ::hidden($column) 0 }
+ if {[info exists hide]} {
+ foreach column $hide {if {[info exists ::hidden($column)]} {set ::hidden($column) 1}}
+ }
+
+ set t [::YUI::DataTable new -skin $skin -volatile \
+ -columns {
+ BulkAction objects -id ID -hide $::hidden(objects) -actions {
+ Action new -label select -tooltip select -url admin/select
+ }
+ # The "-html" options are currenty ignored in the YUI
+ # DataTable. Not sure, it can be integrated in the traditional way.
+ #
+ # A full example for skinning the datatable is here:
+ # http://developer.yahoo.com/yui/examples/datatable/dt_skinning.html
+ #
+ HiddenField ID
+ AnchorField edit -CSSclass edit-item-button -label "" \
+ -hide $::hidden(edit) \
+ -html {style "padding: 0px;"}
+ Field object_type -label [_ xowiki.page_kind] -orderby object_type -richtext false \
+ -hide $::hidden(object_type) \
+ -html {style "padding: 0px;"}
+ AnchorField name -label [_ xowiki.Page-name] -orderby name \
+ -hide $::hidden(name) \
+ -html {style "padding: 2px;"}
+ Field last_modified -label [_ xowiki.Page-last_modified] -orderby last_modified \
+ -hide $::hidden(last_modified)
+ AnchorField delete -CSSclass delete-item-button \
+ -hide $::hidden(delete) \
+ -label "" ;#-html {onClick "return(confirm('Confirm delete?'));"}
+ }]
+
+
+ set extra_where_clause "true"
+ # TODO: why filter on title and name?
+ if {[info exists regexp]} {set extra_where_clause "(bt.title ~ '$regexp' OR ci.name ~ '$regexp' )"}
+ set publish_status_clause [::xowiki::Includelet publish_status_clause $publish_status]
+
+ set items [::xowiki::FormPage get_all_children \
+ -folder_id $current_folder_id \
+ -object_types [my types_to_show] \
+ -extra_where_clause $extra_where_clause]
+
+ set package_id [::xo::cc package_id]
+ set pkg ::$package_id
+ set url [::xo::cc url]
+ $pkg get_lang_and_name -default_lang "" -name [$current_folder name] lang name
+ set folder [$pkg folder_path -parent_id [$current_folder parent_id]]
+ set folder_ids [$items set folder_ids]
+
+ foreach c [$items children] {
+ set name [$c name]
+ set page_link [::$package_id pretty_link \
+ -parent_id $logical_folder_id \
+ -context_url $url \
+ -folder_ids $folder_ids \
+ $name]
+ array set icon [$c render_icon]
+
+ if {[catch {set prettyName [$c pretty_name]} errorMsg]} {
+ my msg "can't obtain pretty name of [$c item_id] [$c name]: $errorMsg"
+ set prettyName $name
+ }
+
+ #set delete_link [export_vars -base [$package_id package_url] \
+ # [list {delete 1} \
+ # [list item_id [$c item_id]] \
+ # [list name [$c pretty_link]] return_url]]
+
+ set delete_link [export_vars -base $page_link {{m delete} return_url}]
+
+ $t add \
+ -ID [$c name] \
+ -name $prettyName \
+ -name.href [export_vars -base $page_link {template_file html-content}] \
+ -name.title [$c set title] \
+ -object_type $icon(text) \
+ -object_type.richtext $icon(is_richtext) \
+ -last_modified [$c set last_modified] \
+ -edit "" \
+ -edit.href [export_vars -base $page_link {{m edit} return_url}] \
+ -edit.title #xowiki.edit# \
+ -delete "" \
+ -delete.href $delete_link \
+ -delete.title #xowiki.delete#
+ }
+
+ foreach {att order} [split $orderby ,] break
+ $t orderby -order [expr {$order eq "asc" ? "increasing" : "decreasing"}] $att
+ set resources_list "[$t asHTML]"
+
+ set viewers [util_coalesce [$current_folder property viewers] [$current_folder get_parameter viewers]]
+ set viewer_links ""
+ foreach v $viewers {
+ set wf_link "${v}?p.folder=[${current_folder} name]"
+ append wf_link "&m=create-or-use"
+ append viewer_links [subst -nocommands -nobackslashes {$timeshort \
+ [my encode $creator] \
+ $viewer_links
[$t asHTML]"
+
+ }
+}
+
+namespace eval ::xowiki::formfield {
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::menuentries
+ #
+ ###########################################################
+
+ Class menuentries -superclass textarea -parameter {
+ {rows 10}
+ {cols 80}
+ }
+ menuentries instproc pretty_value {v} {
+ [my object] do_substitutions 0
+ return "[string map [list & {&} < {<} > {>}] [my value]]
"
+ }
+}
+
+#####################
+# #
+# YUI stuff #
+# #
+#####################
+
+namespace eval ::YUI {
+
+ Object loader -ad_doc {
+ The YUI Library comes with a "Loader" module, that resolves YUI-module
+ dependencies. Also, it combines numerous files into one single file to
+ increase page loading performance.
+ This works only for the "hosted" YUI library. This Loader module should
+ basically do the same (in future). For two simple calls like e.g.
+ "::YUI::loader require menu" and "::YUI::loader require datatable"
+ it should take care of selecting all the files needed and assemble them
+ into one single resource, that may be delivered.
+ Note, that this is not implemented yet.
+ }
+
+ loader set ajaxhelper 1
+
+ # TODO: Make "::YUI::loader require -module XYZ" work everywhere "out-of-the-box"
+ # Now, as we use "::xo:Page require_JS" we have to include the generated
+ # header_stuff "manually" (e.g. in tcl-adp pairs), whereas ::template::head...
+ # includes it directly, which is nice.
+
+ loader ad_proc require {
+ -module
+ {-version "2.7.0b"}
+ } {
+ This is the key function of the loader, that will be used by other packages.
+ @param module
+ The YUI Module to be loaded
+ } {
+ my instvar ajaxhelper
+ switch -- [string tolower $module] {
+
+ utilities {
+ # utilities.js: The utilities.js aggregate combines the Yahoo Global Object,
+ # Dom Collection, Event Utility, Element Utility, Connection Manager,
+ # Drag & Drop Utility, Animation Utility, YUI Loader and the Get Utility.
+ # Use this file to reduce HTTP requests whenever you are including more
+ # than three of its constituent components.
+ ::xowiki::Includelet require_YUI_JS -ajaxhelper $ajaxhelper "yahoo-dom-event/yahoo-dom-event.js"
+ ::xowiki::Includelet require_YUI_JS -ajaxhelper $ajaxhelper "utilities/utilities.js"
+ }
+ menubar {
+ #
+ # We should not have two different versions of the YUI
+ # library on one page, because YUI2 (afaik) doesnt support
+ # "sandboxing". If we use e.g. the yui-hosted utilities.js file here
+ # we may end up with two YAHOO object definitions, because e.g.
+ # the tree-procs uses the local yahoo-dom-event.
+
+ # In future, the YUI loader object should be capable of
+ # resolving such conflicts. for now, the simple fix is to stick to
+ # the local versions, because then the requireJS function takes care
+ # of duplicates.
+ #
+ my require -module "utilities"
+ # todo : this is more than necessary
+ foreach jsFile {
+ "container/container-min.js"
+ "treeview/treeview-min.js"
+ "button/button-min.js"
+ "menu/menu-min.js"
+ "datasource/datasource-min.js"
+ "autocomplete/autocomplete-min.js"
+ "datatable/datatable-min.js"
+ "selector/selector-min.js"
+ } {
+ ::xowiki::Includelet require_YUI_JS -ajaxhelper $ajaxhelper $jsFile
+ }
+
+ my require -module "reset-fonts-grids"
+ my require -module "base"
+
+ foreach cssFile {
+ "container/assets/container.css"
+ "datatable/assets/skins/sam/datatable.css"
+ "button/assets/skins/sam/button.css"
+ "assets/skins/sam/skin.css"
+ "menu/assets/skins/sam/menu.css"
+ } {
+ ::xowiki::Includelet require_YUI_CSS -ajaxhelper $ajaxhelper $cssFile
+ }
+ ::xowiki::Includelet require_YUI_CSS -ajaxhelper 1 "treeview/assets/folders/tree.css"
+ }
+ datatable {
+ # see comment above
+ my require -module "utilities"
+ # todo : this is more than necessary
+ foreach jsFile {
+ "container/container-min.js"
+ "treeview/treeview-min.js"
+ "button/button-min.js"
+ "menu/menu-min.js"
+ "datasource/datasource-min.js"
+ "autocomplete/autocomplete-min.js"
+ "datatable/datatable-min.js"
+ "selector/selector-min.js"
+ } {
+ ::xowiki::Includelet require_YUI_JS -version "2.7.0b" -ajaxhelper $ajaxhelper $jsFile
+ }
+
+ my require -module "reset-fonts-grids"
+ my require -module "base"
+
+ foreach cssFile {
+ "container/assets/container.css"
+ "datatable/assets/skins/sam/datatable.css"
+ "button/assets/skins/sam/button.css"
+ "assets/skins/sam/skin.css"
+ "menu/assets/skins/sam/menu.css"
+ } {
+ ::xowiki::Includelet require_YUI_CSS -ajaxhelper $ajaxhelper $cssFile
+ }
+ #::xowiki::Includelet require_YUI_CSS -ajaxhelper 1 "treeview/assets/skins/sam/treeview.css"
+ #::xowiki::Includelet require_YUI_CSS -ajaxhelper 1 "treeview/assets/folders/tree.css"
+ }
+ reset {
+ ::xowiki::Includelet require_YUI_CSS -ajaxhelper $ajaxhelper "reset/reset.css"
+ }
+ fonts {
+ ::xowiki::Includelet require_YUI_CSS -ajaxhelper $ajaxhelper "fonts/fonts.css"
+ }
+ grids {
+ ::xowiki::Includelet require_YUI_CSS -ajaxhelper $ajaxhelper "grids/grids.css"
+ }
+ base {
+ ::xowiki::Includelet require_YUI_CSS -ajaxhelper $ajaxhelper "base/base.css"
+ }
+ "reset-fonts-grids" {
+ ::xowiki::Includelet require_YUI_CSS -ajaxhelper $ajaxhelper "reset-fonts-grids/reset-fonts-grids.css"
+ }
+ }
+ }
+
+ Class DataTable \
+ -superclass ::xo::Table \
+ -parameter {
+ {skin "yui-skin-sam"}
+ }
+
+ DataTable instproc init {} {
+ set trn_mixin [expr {[lang::util::translator_mode_p] ?"::xo::TRN-Mode" : ""}]
+ my render_with YUIDataTableRenderer $trn_mixin
+ next
+ }
+
+ Class AnchorField \
+ -superclass ::xo::Table::AnchorField \
+ -ad_doc "
+ In addition to the standard TableWidget's AnchorField, we also allow the attributes
+
+
+ " \
+ -instproc get-slots {} {
+ set slots [list -[my name]]
+ foreach subfield {href title CSSclass target onclick} {
+ lappend slots [list -[my name].$subfield ""]
+ }
+ return $slots
+ }
+}
+
+# TODO Allow renderers from other namespaces in 30-widget-procs
+
+namespace eval ::xo::Table {
+
+ Class create YUIDataTableRenderer \
+ -superclass TABLE3 \
+ -instproc init_renderer {} {
+ next
+ my set css.table-class list-table
+ my set css.tr.even-class even
+ my set css.tr.odd-class odd
+ my set id [::xowiki::Includelet js_name [::xowiki::Includelet html_id [self]]]
+ }
+
+ YUIDataTableRenderer ad_instproc -private render_yui_js {} {
+ Generates the JavaScript fragment, that is put below and
+ (progressively enhances) the HTML table.
+ } {
+ my instvar id
+ set container ${id}_container
+ set datasource ${id}_datasource
+ set datatable ${id}_datatable
+ set coldef ${id}_coldef
+
+ set js "var $datasource = new YAHOO.util.DataSource(YAHOO.util.Dom.get('$id')); \n"
+ append js "$datasource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE; \n"
+ append js "$datasource.responseSchema = \{ \n"
+ append js " fields: \[ \n"
+ set js_fields [list]
+ foreach field [[self]::__columns children] {
+ if {[$field hide]} continue
+ lappend js_fields " \{ key: \"[$field set name]\" \}"
+ }
+ append js [join $js_fields ", "] " \] \n\};\n"
+ append js "var $coldef = \[\n"
+ set js_fields [list]
+ foreach field [[self]::__columns children] {
+ if {[$field hide]} continue
+ if {[$field istype HiddenField]} continue
+ if {[$field istype BulkAction]} {
+ set label ""
+ set sortable false
+ } else {
+ set label [$field label]
+ set sortable [expr {[$field exists sortable] ? [$field set sortable] : true}]
+ }
+ lappend js_fields " \{ key: \"[$field set name]\" , sortable: $sortable, label: \"$label\" \}"
+ }
+ append js [join $js_fields ", "] "\];\n"
+ append js "var $datatable = new YAHOO.widget.DataTable('$container', $coldef, $datasource);\n"
+ return $js
+ }
+
+ YUIDataTableRenderer instproc render-body {} {
+ html::thead {
+ html::tr -class list-header {
+ foreach o [[self]::__columns children] {
+ if {[$o hide]} continue
+ $o render
+ }
+ }
+ }
+ set children [my children]
+ html::tbody {
+ foreach line [my children] {
+ html::tr -class [expr {[my incr __rowcount]%2 ? [my set css.tr.odd-class] : [my set css.tr.even-class] }] {
+ foreach field [[self]::__columns children] {
+ if {[$field hide]} continue
+ html::td [concat [list class list] [$field html]] {
+ $field render-data $line
+ }
+ }
+ }
+ }
+ }
+ }
+
+ YUIDataTableRenderer instproc render {} {
+ ::YUI::loader require -module "datatable"
+ if {![my isobject [self]::__actions]} {my actions {}}
+ if {![my isobject [self]::__bulkactions]} {my __bulkactions {}}
+ set bulkactions [[self]::__bulkactions children]
+ if {[llength $bulkactions]>0} {
+ set name [[self]::__bulkactions set __identifier]
+ } else {
+ set name [::xowiki::Includelet js_name [self]]
+ }
+ # TODO: maybe use skin everywhere? hen to use style/CSSclass or skin?
+ set skin [expr {[my exists skin] ? [my set skin] : ""}]
+ html::div -id [my set id]_wrapper -class $skin {
+ html::form -name $name -id $name -method POST {
+ html::div -id [my set id]_container {
+ html::table -id [my set id] -class [my set css.table-class] {
+ # TODO do i need that?
+ my render-actions
+ my render-body
+ }
+ if {[llength $bulkactions]>0} { my render-bulkactions }
+ }
+ }
+ ::xo::Page requireJS "YAHOO.util.Event.onDOMReady(function () {\n[my render_yui_js]});"
+ }
+ }
+
+
+ #Class create YUIDataTableRenderer::AnchorField -superclass TABLE::AnchorField
+
+ Class create YUIDataTableRenderer::AnchorField \
+ -superclass TABLE::Field \
+ -ad_doc "
+ In addition to the standard TableWidget's AnchorField, we also allow the attributes
+
+
+ " \
+ -instproc render-data {line} {
+ set __name [my name]
+ if {[$line exists $__name.href] &&
+ [set href [$line set $__name.href]] ne ""} {
+ # use the CSS class rather from the Field than not the line
+ my instvar CSSclass
+ $line instvar [list $__name.title title] \
+ [list $__name.target target] \
+ [list $__name.onclick onclick]
+ html::a [my get_local_attributes href title {CSSclass class} target onclick] {
+ return "[next]"
+ }
+ }
+ next
+ }
+
+ Class create YUIDataTableRenderer::Action -superclass TABLE::Action
+ Class create YUIDataTableRenderer::Field -superclass TABLE::Field
+ Class create YUIDataTableRenderer::HiddenField -superclass TABLE::HiddenField
+ Class create YUIDataTableRenderer::ImageField -superclass TABLE::ImageField
+ Class create YUIDataTableRenderer::ImageAnchorField -superclass TABLE::ImageAnchorField
+ Class create YUIDataTableRenderer::BulkAction -superclass TABLE::BulkAction
+}
+
+::xo::library source_dependent
Index: openacs-4/packages/xowiki/tcl/form-field-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/form-field-procs.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/xowiki/tcl/form-field-procs.tcl 13 Sep 2012 16:05:27 -0000 1.219
@@ -0,0 +1,3481 @@
+::xo::library doc {
+ XoWiki - form fields
+
+ @creation-date 2007-06-22
+ @author Gustaf Neumann
+ @cvs-id $Id: form-field-procs.tcl,v 1.219 2012/09/13 16:05:27 victorg Exp $
+}
+
+namespace eval ::xowiki::formfield {
+
+ # Second approximation for form fields.
+ # FormFields are objects, which can be outputed as well in ad_forms
+ # or asHTML included in wiki pages. FormFields support
+ #
+ # - validation
+ # - help_text
+ # - error messages
+ # - internationlized pretty_values
+ #
+ # and inherit properties of the original datatypes via slots
+ # (e.g. for boolean entries). FormFields can be subclassed
+ # to ensure tailorability and high reuse.
+ #
+ # todo: at some later time, this could go into xotcl-core
+
+ ###########################################################
+ #
+ # ::xowiki::FormField (Base Class)
+ #
+ ###########################################################
+ Class create FormField -superclass ::xo::tdom::Object -parameter {
+ {required false}
+ {display_field true}
+ {hide_value false}
+ {inline false}
+ {disabled}
+ {show_raw_value}
+ CSSclass
+ style
+ {form_widget_CSSclass form-widget}
+ {form_item_wrapper_CSSclass form-item-wrapper}
+ {type text}
+ {label}
+ {name}
+ {id}
+ {value ""}
+ {spec ""}
+ {help_text ""}
+ {error_msg ""}
+ {validator ""}
+ {validate_via_ajax}
+
+ {autocomplete}
+ {autofocus}
+ {formnovalidate}
+ {multiple}
+ {pattern}
+ {placeholder}
+ {readonly}
+
+ locale
+ default
+ object
+ slot
+ answer
+ correct_when
+ feedback_answer_correct
+ feedback_answer_incorrect
+ }
+ FormField set abstract 1
+
+ FormField proc fc_encode {string} {
+ return [string map [list , __COMMA__] $string]
+ }
+ FormField proc fc_decode {string} {
+ return [string map [list __COMMA__ ,] $string]
+ }
+ #FormField proc fc_decode_colon {string} {
+ # return [string map [list __COLON__ :] $string]
+ #}
+
+ FormField proc get_from_name {object name} {
+ #
+ # Get a form field via name. The provided names are unique for a
+ # form. If multiple forms should be rendered simultaneously, we
+ # have to extend the addressing mechanism.
+ #
+ # todo: we could speed this up by an index if needed
+ foreach f [::xowiki::formfield::FormField info instances -closure] {
+ if {[$f name] eq $name} {
+ if {![$f exists object]} {
+ my msg "strange, $f [$f name] was created without object but fits name"
+ return $f
+ } elseif {$object eq [$f object]} {
+ return $f
+ }
+ }
+ }
+ #my msg not-found-$object-$name
+ return ""
+ }
+
+
+ FormField instproc init {} {
+ if {![my exists label]} {my label [string totitle [my name]]}
+ if {![my exists id]} {my id [my name]}
+ if {[my exists id]} {my set html(id) [my id]}
+ #if {[my exists default]} {my set value [my default]}
+ my config_from_spec [my spec]
+ }
+
+ #
+ # Basic initialze method, doing nothing; should be subclassed by the
+ # application classes
+ FormField instproc initialize {} {next}
+
+ FormField instproc get_json {} {
+ return [util_spec2json [list [my get_spec]]]
+ }
+
+ FormField instproc get_spec {} {
+ set pairs [list [list CSSclass class]]
+ # Special handling of HTML boolean attributes, since they require a
+ # different coding; it would be nice, if tdom would care for this.
+ set booleanAtts [list required readonly disabled multiple formnovalidate autofocus]
+ foreach att $booleanAtts {
+ if {[my exists $att] && [my set $att]} {
+ my set __#$att $att
+ lappend pairs [list __#$att $att]
+ }
+ }
+
+ set atts [eval my get_attributes type size maxlength id name value \
+ pattern placeholder $pairs]
+
+ foreach att $booleanAtts {
+ if {[my exists __#$att]} {my unset __#$att}
+ }
+
+ return [list "input" $atts {}]
+ }
+
+ FormField instproc validation_check {validator_method value} {
+ return [my $validator_method $value]
+ }
+
+ FormField instproc validate {obj} {
+ my instvar name required
+
+ # use the 'value' method to deal e.g. with compound fields
+ set value [my value]
+ #my msg "[my info class] value=$value req=$required // [my set value] //"
+
+ if {$required && $value eq "" && ![my istype ::xowiki::formfield::hidden]} {
+ my instvar label
+ return [_ acs-templating.Element_is_required]
+ }
+ #
+ #my msg "++ [my name] [my info class] validator=[my validator] ([llength [my validator]]) value=$value"
+ foreach validator [my validator] {
+ set errorMsg ""
+ #
+ # The validator might set the variable errorMsg in this scope.
+ #
+ set success 1
+ set validator_method check=$validator
+ set proc_info [my procsearch $validator_method]
+ #my msg "++ [my name]: field-level validator exists '$validator_method' ? [expr {$proc_info ne {}}]"
+ if {$proc_info ne ""} {
+ # we have a slot checker, call it
+ #my msg "++ call-field level validator $validator_method '$value'"
+ set success [my validation_check $validator_method $value]
+ }
+ if {$success == 1} {
+ # the previous check was ok, check now for a validator on the
+ # object level
+ set validator_method validate=$validator
+ set proc_info [$obj procsearch $validator_method]
+ #my msg "++ [my name]: page-level validator exists ? [expr {$proc_info ne {}}]"
+ if {$proc_info ne ""} {
+ set success [$obj $validator_method $value]
+ #my msg "++ call page-level validator $validator_method '$value' returns $success"
+ }
+ }
+ if {$success == 0} {
+ #
+ # We have an error message. Get the class name from procsearch and construct
+ # a message key based on the class and the name of the validator.
+ #
+ set cl [namespace tail [lindex $proc_info 0]]
+ return [_ xowiki.$cl-validate_$validator [list value $value errorMsg $errorMsg]]
+ #return [::lang::message::lookup "" xowiki.$cl-validate_$validator %errorMsg% [list value $value errorMsg $errorMsg] 1]
+ }
+ }
+ return ""
+ }
+
+ FormField instproc reset_parameter {} {
+ # reset application specific parameters (defined below ::xowiki::formfield::FormField)
+ # such that searchDefaults will pick up the new defaults, when a form field
+ # is reclassed.
+
+ if {[my exists per_object_behavior]} {
+ # remove per-object mixin from the "behavior"
+ my mixin delete [my set per_object_behavior]
+ my unset per_object_behavior
+ }
+
+ #my msg "reset along [my info precedence]"
+ foreach c [my info precedence] {
+ if {$c eq "::xowiki::formfield::FormField"} break
+ foreach s [$c info slots] {
+ if {![$s exists default]} continue
+ set var [$s name]
+ set key processed($var)
+ if {[info exists $key]} continue
+ my set $var [$s default]
+ set $key 1
+ }
+ }
+ if {[my exists disabled]} {
+ my set_disabled 0
+ }
+ }
+
+ FormField proc interprete_condition {-package_id -object cond} {
+ if {[::xo::cc info methods role=$cond] ne ""} {
+ if {$cond eq "creator"} {
+ set success [::xo::cc role=$cond \
+ -object $object \
+ -user_id [::xo::cc user_id] \
+ -package_id $package_id]
+ } else {
+ set success [::xo::cc role=$cond \
+ -user_id [::xo::cc user_id] \
+ -package_id $package_id]
+ }
+ } else {
+ set success 0
+ }
+ return $success
+ }
+
+ FormField set cond_regexp {^([^=?]+)[?]([^:]*)[:](.*)$}
+
+ FormField proc get_single_spec {-package_id -object string} {
+ if {[regexp [my set cond_regexp] $string _ condition true_spec false_spec]} {
+ if {[my interprete_condition -package_id $package_id -object $object $condition]} {
+ return [my get_single_spec -package_id $package_id -object $object $true_spec]
+ } else {
+ return [my get_single_spec -package_id $package_id -object $object $false_spec]
+ }
+ }
+ return $string
+ }
+
+ FormField instproc remove_omit {} {
+ set m ::xowiki::formfield::omit
+ if {[my ismixin $m]} {my mixin delete $m}
+ }
+ FormField instproc set_disabled {disable} {
+ #my msg "[my name] set disabled $disable"
+ if {$disable} {
+ my set disabled true
+ } else {
+ my unset -nocomplain disabled
+ }
+ }
+
+ FormField instproc behavior {mixin} {
+
+ #
+ # Specify the behavior of a form field via
+ # per object mixins
+ #
+ set obj [my object]
+ set pkgctx [[$obj package_id] context]
+ if {[$pkgctx exists embedded_context]} {
+ set ctx [$pkgctx set embedded_context]
+ set classname ${ctx}::$mixin
+ #my msg ctx=$ctx-viewer=$mixin,found=[my isclass $classname]
+ # TODO: search different places for the mixin. Special namespace?
+ if {[my isclass $classname]} {
+ if {[my exists per_object_behavior]} {
+ my mixin delete [my set per_object_behavior]
+ }
+ my mixin add $classname
+ my set per_object_behavior $classname
+ } else {
+ my msg "Could not find mixin '$mixin'"
+ }
+ }
+ }
+
+ FormField instproc repeatable {} {
+ my mixin add ::xowiki::formfield::repeatable
+ my reset_parameter
+ }
+
+ FormField instproc interprete_single_spec {s} {
+ if {$s eq ""} return
+
+ set object [my object]
+ set package_id [$object package_id]
+ set s [::xowiki::formfield::FormField get_single_spec -object $object -package_id $package_id $s]
+
+ switch -glob -- $s {
+ optional {my set required false}
+ required {my set required true; my remove_omit}
+ omit {my mixin add ::xowiki::formfield::omit}
+ repeatable {my repeatable}
+ noomit {my remove_omit}
+ disabled {my set_disabled true}
+ enabled {my set_disabled false}
+ label=* {my label [lindex [split $s =] 1]}
+ help_text=* {my help_text [lindex [split $s =] 1]}
+ *=* {
+ set p [string first = $s]
+ set attribute [string range $s 0 [expr {$p-1}]]
+ set value [string range $s [expr {$p+1}] end]
+ set definition_class [lindex [my procsearch $attribute] 0]
+ set method [my info methods $attribute]
+ if {[string match "::xotcl::*" $definition_class] || $method eq ""} {
+ error [_ xowiki.error-form_constraint-unknown_attribute [list class [my info class] name [my name] entry $attribute]]
+ }
+ if {[catch {
+ #
+ # We want to allow a programmer to use e.g. options=[xowiki::locales]
+ #
+ # Note: do not allow users to use [] via forms, since they might
+ # execute arbitrary commands. The validator for the form fields
+ # makes sure, that the input specs are free from square brackets.
+ #
+ if {[string match {\[*\]} $value]} {
+ set value [subst $value]
+ }
+ my $attribute $value
+ } errMsg]} {
+ error "Error during setting attribute '$attribute' to value '$value': $errMsg"
+ }
+ }
+ default {
+ # Check, if the spec value $s is a class.
+ set old_class [my info class]
+ # Don't allow to use namespaced values, since we would run
+ # into a recursive loop for richtext::wym (could be altered there as well).
+ if {[my isclass ::xowiki::formfield::$s] && ![string match "*:*" $s]} {
+ my class ::xowiki::formfield::$s
+ my remove_omit
+ if {$old_class ne [my info class]} {
+ #my msg "[my name]: reset class from $old_class to [my info class]"
+ my reset_parameter
+ my set __state reset
+ my initialize
+ }
+ } else {
+ if {$s ne ""} {
+ error [_ xowiki.error-form_constraint-unknown_spec_entry \
+ [list name [my name] entry $s x "Unknown spec entry for entry '$s'"]]
+ }
+ }
+ }
+ }
+ }
+
+ FormField instproc config_from_spec {spec} {
+ #my log "spec=$spec [my info class] [[my info class] exists abstract]"
+
+ my instvar type
+ if {[[my info class] exists abstract]} {
+ # had earlier here: [my info class] eq [self class]
+ # Check, wether the actual class is a concrete class (mapped to
+ # concrete field type) or an abstact class. Since
+ # config_from_spec can be called multiple times, we want to do
+ # the reclassing only once.
+ if {[my isclass ::xowiki::formfield::$type]} {
+ my class ::xowiki::formfield::$type
+ } else {
+ my class ::xowiki::formfield::text
+ }
+ # set missing instance vars with defaults
+ my set_instance_vars_defaults
+ }
+ regsub -all {,\s+} $spec , spec
+ foreach s [split $spec ,] {
+ my interprete_single_spec [FormField fc_decode $s]
+ }
+
+ #my msg "[my name]: after specs"
+ my set __state after_specs
+ my initialize
+
+ #
+ # It is possible, that a default value of a form field is changed through a spec.
+ # Since only the configuration might set values, checking value for "" seems safe here.
+ #
+ if {[my value] eq "" && [my exists default] && [my default] ne ""} {
+ #my msg "+++ reset value to [my default]"
+ my value [my default]
+ }
+
+ if {[lang::util::translator_mode_p]} {
+ my mixin add "::xo::TRN-Mode"
+ }
+
+ }
+
+ FormField instproc asWidgetSpec {} {
+ my instvar widget_type options label help_text format html display_html
+ set spec $widget_type
+ if {[my exists spell]} {append spec ",[expr {[my spell] ? {} : {no}}]spell"}
+
+ if {![my required]} {append spec ",optional"}
+ append spec " {label " [list $label] "} "
+
+ if {[my exists html]} {
+ append spec " {html {"
+ foreach {key value} [array get html] {
+ append spec $key " " [list $value] " "
+ }
+ append spec "}} "
+ }
+
+ if {[my exists options]} {
+ append spec " {options " [list $options] "} "
+ }
+ if {[my exists format]} {
+ append spec " {format " [list $format] "} "
+ }
+
+ if {$help_text ne ""} {
+ if {[string match "#*#" $help_text]} {
+ set internationalized [my localize $help_text]
+ append spec " {help_text {$internationalized}}"
+ } else {
+ append spec " {help_text {$help_text}}"
+ }
+ }
+ return $spec
+ }
+
+ FormField instproc render {} {
+ # In case, we use an asHTML of a FormField, we use this
+ # render definition
+ if {[my inline]} {
+ # with label, error message, help text
+ my render_form_widget
+ } else {
+ # without label, error message, help text
+ my render_item
+ }
+ my set __rendered 1
+ }
+
+ FormField instproc render_form_widget {} {
+ # This method provides the form-widget wrapper
+ set CSSclass [my form_widget_CSSclass]
+ if {[my error_msg] ne ""} {append CSSclass " form-widget-error"}
+ set atts [list class $CSSclass]
+ if {[my inline]} {lappend atts style "display: inline;"}
+ ::html::div $atts { my render_input }
+ }
+
+ FormField instproc render_input {} {
+ #
+ # This is the most general widget content renderer.
+ # If no special renderer is defined, we fall back to this one,
+ # which is in most cases a simple input fied of type string.
+ #
+ if {[my exists validate_via_ajax] && [my validator] ne ""} {
+ set ajaxhelper 1
+ ::xowiki::Includelet require_YUI_JS -ajaxhelper 0 "yahoo/yahoo-min.js"
+ ::xowiki::Includelet require_YUI_JS -ajaxhelper 0 "dom/dom-min.js"
+ ::xowiki::Includelet require_YUI_JS -ajaxhelper 0 "event/event-min.js"
+ ::xowiki::Includelet require_YUI_JS -ajaxhelper 0 "connection/connection-min.js"
+ ::xo::Page requireJS "/resources/xowiki/yui-form-field-validate.js"
+ set package_url [[[my object] package_id] package_url]
+ ::xo::Page requireJS "YAHOO.xo_form_field_validate.add('[my id]','$package_url');"
+ }
+
+ #::html::input [eval my get_attributes type size maxlength id name value \
+ # pattern placeholder $pairs] {}
+ util_createDom [list [my get_spec]]
+
+ #
+ # Disabled fieds are not returned by the browsers. For some
+ # fields, we require to be sent. therefore we include in these
+ # cases the value in an additional hidden field. Maybe we should
+ # change in the future the "name" of the disabled entry to keep
+ # some hypothetical html-checker quiet.
+ #
+ if {[my exists disabled] && [my exists transmit_field_always]} {
+ ::html::input [list type hidden name [my name] value [my set value]] {}
+ }
+ my set __rendered 1
+ }
+
+ FormField instproc render_item {} {
+ ::html::div -class [my form_item_wrapper_CSSclass] {
+ if {[my error_msg] ne ""} {
+ set CSSclass form-label-error
+ } else {
+ set CSSclass form-label
+ }
+ ::html::div -class $CSSclass {
+ ::html::label -for [my id] {
+ ::html::t [my label]
+ }
+ if {[my required]} {
+ ::html::div -class form-required-mark {
+ ::html::t " (#acs-templating.required#)"
+ }
+ }
+ }
+ my render_form_widget
+ my render_help_text
+ my render_error_msg
+ html::t \n
+ }
+ }
+
+ FormField instproc render_error_msg {} {
+ if {[my error_msg] ne "" && ![my exists error_reported]} {
+ ::html::div -class form-error {
+ my instvar label
+ ::html::t [::xo::localize [my error_msg]]
+ my render_localizer
+ my set error_reported 1
+ }
+ }
+ }
+
+ FormField instproc render_help_text {} {
+ set text [my help_text]
+ if {$text ne ""} {
+ html::div -class form-help-text {
+ html::img -src "/shared/images/info.gif" -alt {[i]} -title {Help text} \
+ -width "12" -height 9 -border 0 -style "margin-right: 5px" {}
+ html::t $text
+ }
+ }
+ }
+
+ FormField instproc render_localizer {} {
+ # Just an empty fall-back method.
+ # This method will be overloaded in trn mode by a mixin.
+ }
+
+ FormField instproc localize {v} {
+ # We localize in pretty_value the message keys in the
+ # language of the item (not the connection item).
+ if {[regexp "^#(.*)#$" $v _ key]} {
+ return [lang::message::lookup [my locale] $key]
+ }
+ return $v
+ }
+
+ FormField instproc value_if_nothing_is_returned_from_form {default} {
+ return $default
+ }
+
+ FormField instproc pretty_value {v} {
+ #my log "mapping $v"
+ return [string map [list & "&" < "<" > ">" \" """ ' "'" @ "@"] $v]
+ }
+
+ FormField instproc has_instance_variable {var value} {
+ if {[my exists $var] && [my set $var] eq $value} {return 1}
+ return 0
+ }
+ FormField instproc convert_to_internal {} {
+ # to be overloaded
+ }
+ FormField instproc convert_to_external {value} {
+ # to be overloaded
+ return $value
+ }
+
+ FormField instproc answer_check=eq {} {
+ my instvar value
+ set arg1 [lindex [my correct_when] 1]
+ return [expr {$value eq $arg1}]
+ }
+ FormField instproc answer_check=gt {} {
+ my instvar value
+ set arg1 [lindex [my correct_when] 1]
+ return [expr {$value > $arg1}]
+ }
+ FormField instproc answer_check=ge {} {
+ my instvar value
+ set arg1 [lindex [my correct_when] 1]
+ return [expr {$value >= $arg1}]
+ }
+ FormField instproc answer_check=lt {} {
+ my instvar value
+ set arg1 [lindex [my correct_when] 1]
+ return [expr {$value < $arg1}]
+ }
+ FormField instproc answer_check=le {} {
+ my instvar value
+ set arg1 [lindex [my correct_when] 1]
+ return [expr {$value <= $arg1}]
+ }
+ FormField instproc answer_check=btwn {} {
+ my instvar value
+ set arg1 [lindex [my correct_when] 1]
+ set arg2 [lindex [my correct_when] 2]
+ return [expr {$value >= $arg1 && $value <= $arg2}]
+ }
+ FormField instproc answer_check=in {} {
+ my instvar value
+ set values [lrange [my correct_when] 1 end]
+ return [expr {[lsearch -exact $values $value] > -1}]
+ }
+ FormField instproc answer_check=match {} {
+ return [string match [lindex [my correct_when] 1] [my value]]
+ }
+ FormField instproc answer_check=answer_words {} {
+ set value [regsub -all { +} [my value] " "]
+ if {[string match "*lower*" [lindex [my correct_when] 1]]} {
+ set value [string tolower $value]
+ }
+ return [expr {$value eq [my answer]}]
+ }
+
+ FormField instproc answer_is_correct {} {
+ #my msg "[my name] ([my info class]): value=[my value], answer=[expr {[my exists answer]?[my set answer]:{NONE}}]"
+ if {[my exists correct_when]} {
+ set op [lindex [my correct_when] 0]
+ if {[my procsearch answer_check=$op] ne ""} {
+ set r [my answer_check=$op]
+ if {$r == 0} {return -1} {return 1}
+ } else {
+ error "invalid operator '$op'"
+ }
+ } elseif {![my exists answer]} {
+ return 0
+ } elseif {[my value] ne [my answer]} {
+ #my msg "v='[my value]' NE a='[my answer]'"
+ return -1
+ } else {
+ return 1
+ }
+ }
+
+ FormField instproc field_value {v} {
+ if {[my exists show_raw_value]} {
+ return $v
+ } else {
+ return [my pretty_value]
+ }
+ }
+
+ FormField instproc pretty_image {-parent_id:required entry_name} {
+ if {$entry_name eq ""} return
+ if {[my set value] eq ""} return
+ my instvar object value
+
+ array set "" [$object item_ref -default_lang [$object lang] -parent_id $parent_id $entry_name]
+
+ set label [my label] ;# the label is used for alt und title
+ if {$label eq $(stripped_name)} {
+ # The label is apparently the default. For Photo.form instances,
+ # this is always "image". In such cases, use the title of the
+ # parent object as label.
+ set label [[my object] title]
+ }
+
+ set l [::xowiki::Link create new -destroy_on_cleanup \
+ -page $object -type "image" -lang $(prefix) \
+ [list -stripped_name $(stripped_name)] [list -label $label] \
+ -parent_id $(parent_id) -item_id $(item_id)]
+
+ if {[my istype file]} {
+ set revision_id [my get_from_value $value revision_id]
+ if {$revision_id ne ""} {
+ $l revision_id $revision_id
+ }
+ }
+
+ foreach option {
+ href cssclass
+ float width height
+ padding padding-right padding-left padding-top padding-bottom
+ margin margin-left margin-right margin-top margin-bottom
+ border border-width position top botton left right
+ geometry
+ } {
+ if {[my exists $option]} {$l set $option [my set $option]}
+ }
+ set html [$l render]
+ return $html
+ }
+
+ ###########################################################
+ #
+ # helper method for extending slots:
+ # either, we make a meta class for form-fields, or this should
+ # should go into xotcl-core
+ #
+ ###########################################################
+
+ ::Serializer exportMethods {
+ ::xotcl::Class instproc extend_slot
+ }
+ Class instproc extend_slot {name value} {
+ # create a mirroring slot and add the specified value to the default
+ foreach c [my info heritage] {
+ if {[info command ${c}::slot::$name] ne ""} {
+ set value [concat $value [${c}::slot::$name default]]
+ break
+ }
+ }
+ my slots [list Attribute create validator -default $value]
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::submit_button
+ #
+ ###########################################################
+
+ Class submit_button -superclass FormField
+ submit_button instproc initialize {} {
+ my set type submit
+ my set value [::xo::localize [_ xowiki.Form-submit_button]]
+ }
+ submit_button instproc render_input {} {
+ # don't disable submit buttons
+ if {[my type] eq "submit"} {my unset -nocomplain disabled}
+ ::html::input [my get_attributes name type {CSSclass class} value disabled] {}
+ my render_localizer
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::file
+ #
+ ###########################################################
+
+ Class create file -superclass FormField -parameter {
+ {size 40}
+ {sticky false}
+ link_label
+ }
+ file instproc tmpfile {value} {my set [self proc] $value}
+ file instproc content-type {value} {my set [self proc] $value}
+ file instproc initialize {} {
+ my type file
+ my set widget_type file(file)
+ next
+ }
+ file instproc entry_info {value} {
+ return [list name file:[my name] parent_id [[my object] item_id]]
+ }
+
+ file instproc get_value_from_form {} {
+ set old_value [[my object] form_parameter __old_value_[my name] ""]
+ set v [my set value]
+ #my msg "value '$v' // old_value '$old_value'"
+ #
+ # Figure out, if we got a different file-name (value). If the
+ # file-name is the same as in the last revision, we return a
+ # "-". This has the effect, that file file is not uploaded again.
+ #
+ #if {$old_value ne "" && $old_value eq [my set value]} {}
+
+ if {$old_value ne "" && $v eq ""} {
+ return "-"
+ }
+ return $v
+ }
+
+ file instproc get_from_value {value attribute {raw ""}} {
+ #
+ # The value of of a form entry might be:
+ # - an atomic list element
+ # - a list with attribute value pairs
+ #
+ # This function tries to obtain the queried attribute from the
+ # attribute value pair notation. If this fails, it returns a
+ # default value.
+ #
+ set valueLength [llength $value]
+ if {$valueLength > 1 && $valueLength %2 == 0} {
+ array set "" $value
+ if {[info exists ($attribute)]} {
+ return $($attribute)
+ }
+ }
+ return [lindex $raw 0]
+ }
+
+ file instproc convert_to_internal {} {
+ my instvar value
+
+ set v [my get_value_from_form]
+ if {$v eq "-" || $v eq ""} {
+ # nothing to do, keep the old value
+ #my msg "nothing to do with '$v'"
+ set value [[my object] form_parameter __old_value_[my name] ""]
+ [my object] set_property [my name] $value
+ return
+ }
+ regsub -all {\\+} $value {/} value ;# fix IE upload path
+ set value [::file tail $value]
+ [my object] set_property [my name] $value
+
+ set package_id [[my object] package_id]
+ array set entry_info [my entry_info $value]
+
+ set content_type [my set content-type]
+ if {$content_type eq "application/octetstream"
+ || $content_type eq "application/force-download"
+ } {
+ set content_type [::xowiki::guesstype $value]
+ }
+ #my msg "mime_type of $entry_info(name) = [::xowiki::guesstype $value] // [my set content-type] ==> $content_type"
+ set file_object [$package_id get_page_from_name -name $entry_info(name) -parent_id $entry_info(parent_id)]
+ if {$file_object ne ""} {
+ # file entry exists already, create a new revision
+ #my msg "new revision (value $value)"
+ $file_object set import_file [my set tmpfile]
+ $file_object set mime_type $content_type
+ $file_object set title $value
+ $file_object save
+ #
+ # Update the value with the attribute value pair list containing
+ # the revision_id. TODO: clear revision_id on export.
+ #
+ [my object] set_property -new 1 [my name] [list name $value revision_id [$file_object revision_id]]
+ } else {
+ # create a new file
+ #my msg "new file"
+ set file_object [::xowiki::File new -destroy_on_cleanup \
+ -title $value \
+ -name $entry_info(name) \
+ -parent_id $entry_info(parent_id) \
+ -mime_type $content_type \
+ -package_id [[my object] package_id] \
+ -creation_user [::xo::cc user_id] ]
+ $file_object set import_file [my set tmpfile]
+ $file_object save_new
+ # Make sure the value is just one list item
+ [my object] set_property -new 1 [my name] [list $value]
+ }
+ }
+
+ file instproc label_or_value {v} {
+ if {[my exists link_label]} {
+ return [my localize [my link_label]]
+ }
+ return $v
+ }
+
+ file instproc pretty_value {v} {
+ if {$v ne ""} {
+ my instvar object
+ array set "" [my entry_info $v]
+ array set "" [$object item_ref -default_lang [[my object] lang] -parent_id $(parent_id) $(name)]
+ #my msg "pretty value name '$(stripped_name)'"
+ set l [::xowiki::Link create new -destroy_on_cleanup \
+ -page $object -type "file" -lang $(prefix) \
+ [list -stripped_name $(stripped_name)] [list -label [my label]] \
+ [list -extra_query_parameter [list [list filename [my get_from_value $v name $v]]]] \
+ -parent_id $(parent_id) -item_id $(item_id)]
+ return [$l render]
+ }
+ }
+
+ file instproc render_input {} {
+ util_createDom [list [my get_spec]]
+ }
+
+ file instproc get_spec {} {
+ my instvar value
+ set package_id [[my object] package_id]
+ array set entry_info [my entry_info $value]
+ set fn [my get_from_value $value name $value]
+ set href [$package_id pretty_link -download 1 -parent_id $entry_info(parent_id) $entry_info(name)]
+ if {![my istype image]} {
+ append href ?filename=[ns_urlencode $fn]
+ }
+ #
+ # The HTML5 handling of "required" would force us to upload in
+ # every form the file again. To implement the sticky option, we
+ # set temporarily the "required" attribute to false
+ #
+ if {[my exists required]} {
+ set reset_required 1
+ my set required false
+ }
+
+ lassign [next] tag atts children
+
+ if {[info exists reset_required]} {
+ my set required true
+ }
+
+ set additional_spec [util_tdom2list {
+ # FOLLOWING GIVES TROUBLE, SEE util_spec2json FOR DETAILS
+ ::html::t " "
+ set id __old_value_[my name]
+ ::html::input -type hidden -name $id -id $id -value $value
+ #my msg "old_value '$value'"
+ ::html::span -class file-control -id __a$id {
+ ::html::a -href $href {::html::t [my label_or_value $fn] }
+ # Show the clear button just when
+ # - there is something to clear, and
+ # - the formfield is not disabled, and
+ # - the form-field is not sticky (default)
+ set disabled [expr {[my exists disabled] && [my disabled] ne "false"}]
+ if {$value ne "" && !$disabled && ![my sticky] } {
+ ::html::input -type button -value clear \
+ -onClick "document.getElementById('$id').value = ''; document.getElementById('__a$id').style.display = 'none';"
+ }
+ }
+ }]
+
+ lappend children $additional_spec
+ return [list $tag $atts $children]
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::import_archive
+ #
+ ###########################################################
+
+ Class import_archive -superclass file -parameter {
+ {cleanup false}
+ }
+ import_archive instproc initialize {} {
+ next
+ if {[my help_text] eq ""} {my help_text "#xowiki.formfield-import_archive-help_text#"}
+ }
+ import_archive instproc pretty_value {v} {
+ my instvar object
+ set package_id [$object package_id]
+ set parent_id [$object parent_id]
+ if {$v eq ""} {return ""}
+ array set "" [my entry_info $v]
+ set fn [my get_from_value $v name $v]
+ #
+ # Get the file object of the imported file to obtain is full name and path
+ #
+ set file_id [$package_id lookup -parent_id [$object item_id] -name $(name)]
+ ::xo::db::CrClass get_instance_from_db -item_id $file_id
+ set full_file_name [$file_id full_file_name]
+ #
+ # Call the archiver to unpack and handle the archive
+ #
+ set f [::xowiki::ArchiveFile new -file $full_file_name -name $fn -parent_id $parent_id]
+ if {[$f unpack]} {
+ #
+ # So, all the hard work is done. We take a hard measure here to
+ # cleanup the entry in case everything was imported
+ # successful. Note that setting "cleanup" without thought might
+ # lead to maybe unexpected deletions of the form-page
+ #
+ if {[my cleanup]} {
+ set return_url [$package_id query_parameter "return_url" [$parent_id pretty_link]]
+ $package_id returnredirect [export_vars -base [$object pretty_link] [list {m delete} return_url]]
+ }
+ }
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::image
+ #
+ ###########################################################
+
+ Class image -superclass file -parameter {
+ href cssclass
+ float width height
+ padding padding-right padding-left padding-top padding-bottom
+ margin margin-left margin-right margin-top margin-bottom
+ border border-width position top botton left right
+ }
+ image instproc pretty_value {v} {
+ array set "" [my entry_info $v]
+
+ return [my pretty_image -parent_id $(parent_id) $(name)]
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::hidden
+ #
+ ###########################################################
+
+ Class hidden -superclass FormField
+ hidden instproc initialize {} {
+ my type hidden
+ my set widget_type text(hidden)
+ # remove mixins in case of retyping
+ my mixin ""
+ }
+ hidden instproc render_item {} {
+ # don't render the labels
+ my render_form_widget
+ }
+ hidden instproc render_help_text {} {
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::omit
+ #
+ ###########################################################
+
+ Class omit -superclass FormField
+ omit instproc render_item {} {
+ # don't render the labels
+ #my render_form_widget
+ }
+ omit instproc render_help_text {} {
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::inform
+ #
+ ###########################################################
+
+ Class inform -superclass FormField
+ inform instproc initialize {} {
+ my type hidden
+ my set widget_type text(inform)
+ }
+ inform instproc render_input {} {
+ ::html::t [my value]
+ ::html::input [my get_attributes type id name value disabled {CSSclass class}] {}
+ }
+ inform instproc render_help_text {} {
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::text
+ #
+ ###########################################################
+
+ Class text -superclass FormField -parameter {
+ {size 80}
+ maxlength
+ }
+ text instproc initialize {} {
+ my type text
+ my set widget_type text
+ foreach p [list size maxlength] {if {[my exists $p]} {my set html($p) [my $p]}}
+ }
+ text instproc get_spec {} {
+ set atts [my get_attributes type size maxlength id name value \
+ pattern placeholder]
+
+ return [list input $atts {}]
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::color
+ #
+ ###########################################################
+
+ Class color -superclass text
+ color instproc initialize {} {
+ next
+ my type color
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::datetime
+ #
+ ###########################################################
+
+ Class datetime -superclass text
+ datetime instproc initialize {} {
+ next
+ my type datetime
+ }
+ # names for HTML5 types
+ # date, month
+ # already in use, should redefine accordingly when avail
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::datetime-local
+ #
+ ###########################################################
+
+ Class datetime-local -superclass text
+ datetime-local instproc initialize {} {
+ next
+ my type datetime-local
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::time
+ #
+ ###########################################################
+
+ Class time -superclass text
+ time instproc initialize {} {
+ next
+ my type time
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::week
+ #
+ ###########################################################
+
+ Class week -superclass text
+ week instproc initialize {} {
+ next
+ my type datetime
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::email
+ #
+ ###########################################################
+
+ Class email -superclass text
+ email instproc initialize {} {
+ next
+ my type email
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::search
+ #
+ ###########################################################
+
+ Class search -superclass text
+ search instproc initialize {} {
+ next
+ my type search
+ }
+ ###########################################################
+ #
+ # ::xowiki::formfield::tel
+ #
+ ###########################################################
+
+ Class tel -superclass text
+ tel instproc initialize {} {
+ next
+ my type tel
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::number
+ #
+ ###########################################################
+
+ Class number -superclass FormField -parameter {
+ min max step value
+ }
+ number instproc initialize {} {
+ my type number
+ my set widget_type text
+ }
+ number instproc render_input {} {
+ ::html::input [my get_attributes type id name value disabled {CSSclass class} min max step value \
+ autofocus formnovalidate multiple pattern placeholder readonly required] {}
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::range
+ #
+ ###########################################################
+
+ Class range -superclass FormField -parameter {
+ min max step value
+ }
+ range instproc initialize {} {
+ my type range
+ my set widget_type text
+ }
+ range instproc render_input {} {
+ ::html::input [my get_attributes type id name value disabled {CSSclass class} min max step value \
+ autofocus formnovalidate multiple pattern placeholder readonly required] {}
+ }
+
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::password
+ #
+ ###########################################################
+
+ Class password -superclass text
+ password instproc initialize {} {
+ next
+ my set widget_type password
+ my type password
+ }
+ ###########################################################
+ #
+ # ::xowiki::formfield::numeric
+ #
+ ###########################################################
+
+ Class numeric -superclass text -parameter {
+ {format %.2f}
+ } -extend_slot validator numeric
+ numeric instproc initialize {} {
+ next
+ my set widget_type numeric
+ # check, if we we have an integer format
+ my set is_integer [regexp {%[0-9.]*d} [my format]]
+ }
+ numeric instproc convert_to_external value {
+ if {$value ne ""} {
+ if { [catch "lc_numeric $value [my format] [my locale]" result] } {
+ util_user_message -message "[my label]: $result (locale=[my locale])"
+ #my msg [list lc_numeric $value [my format] [my locale]]
+ set converted_value $value
+ if {[catch {scan $value [my format] converted_value}]} {
+ return $value
+ } else {
+ return $converted_value
+ }
+ }
+ return $result
+ }
+ return $value
+ }
+ numeric instproc convert_to_internal {} {
+ if {[my value] ne ""} {
+ set value [lc_parse_number [my value] [my locale] [my set is_integer]]
+ [my object] set_property -new 1 [my name] [expr {$value}]
+ return
+ }
+ }
+ numeric instproc check=numeric {value} {
+ return [expr {[catch {lc_parse_number $value [my locale] [my set is_integer]}] == 0}]
+ }
+ numeric instproc pretty_value value {
+ return [my convert_to_external $value]
+ }
+ numeric instproc answer_check=eq {} {
+ # use numeric equality
+ return [expr {[my value] == [lindex [my correct_when] 1]}]
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::user_id
+ #
+ ###########################################################
+
+ Class user_id -superclass numeric -parameter {
+ {format %d}
+ }
+ user_id instproc initialize {} {
+ next
+ my set is_party_id 1
+ }
+ user_id instproc pretty_value {v} {
+ return [::xo::get_user_name $v]
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::author
+ #
+ ###########################################################
+
+ Class author -superclass user_id -parameter {
+ {photo_size 54}
+ {with_photo true}
+ {with_user_link false}
+ {label #xowiki.formfield-author#}
+ }
+ author instproc pretty_value {v} {
+ if {$v ne ""} {
+ my instvar object
+ acs_user::get -user_id $v -array user
+ if {[my with_photo]} {
+ set portrait_id [acs_user::get_portrait_id -user_id $v]
+ if {$portrait_id == 0} {
+ package require md5
+ set md5 [string tolower [md5::Hex [md5::md5 -- $user(email)]]]
+ set src http://www.gravatar.com/avatar/$md5?size=[my photo_size]&d=mm
+ } else {
+ set src "/shared/portrait-bits.tcl?user_id=$v"
+ }
+ set photo ""
+ set photo_class "photo"
+ } else {
+ set photo ""
+ set photo_class ""
+ }
+ set date_field [::xowiki::FormPage get_table_form_fields \
+ -base_item $object \
+ -field_names _last_modified \
+ -form_constraints ""]
+ set date [$date_field pretty_value [$object property _last_modified]]
+
+ if {[my with_user_link]} {
+ set user_link_begin ""
+ set user_link_end ""
+ } else {
+ set user_link_begin ""
+ set user_link_end ""
+ }
+
+ return [subst {
+ $html
"
+ } else {
+ return "[string map [list & {&} < {<} > {>}] [my value]]
"
+ }
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::richtext
+ #
+ ###########################################################
+
+ Class richtext -superclass textarea \
+ -extend_slot validator safe_html \
+ -parameter {
+ plugins
+ folder_id
+ script_dir
+ width
+ height
+ {wiki false}
+ }
+
+ richtext instproc editor {args} {
+ #
+ # TODO: this should be made a slot setting
+ #
+ #my msg "setting editor for [my name], args=$args,[llength $args]"
+ if {[llength $args] == 0} {return [my set editor]}
+ set editor [lindex $args 0]
+ if {[my exists editor] && $editor eq [my set editor] && [my exists __initialized]} return
+
+ set editor_class [self class]::$editor
+ if {$editor ne "" && ![my hasclass $editor_class]} {
+ if {![my isclass $editor_class]} {
+ set editors [list]
+ foreach c [::xowiki::formfield::richtext info subclass] {
+ if {![$c exists editor_mixin]} continue
+ lappend editors [namespace tail $c]
+ }
+ error [_ xowiki.error-form_constraint-unknown_editor \
+ [list name [my name] editor [my editor] editors $editors]]
+ }
+ foreach m [my info mixin] {if {[$m exists editor_mixin]} {my mixin delete $m}}
+ my mixin add $editor_class
+ #my msg "MIXIN $editor: [my info precedence]"
+ my reset_parameter
+ my set __initialized 1
+ }
+ my set editor $editor
+ }
+
+ richtext instproc initialize {} {
+ my display_field false
+ next
+ if {![my exists editor]} {my set editor xinha} ;# set the default editor
+ if {![my exists __initialized]} {
+ # Mixin the editor based on the attribute 'editor' if necessary
+ # and call initialize again in this case...
+ my editor [my set editor]
+ my initialize
+ }
+ }
+
+ richtext instproc render_richtext_as_div {} {
+ #my msg "[my get_attributes id style {CSSclass class}]"
+ ::html::div [my get_attributes id style {CSSclass class}] {
+ if {[my wiki]} {
+ [my object] set unresolved_references 0
+ [my object] set __unresolved_references [list]
+ #::html::t -disableOutputEscaping [[my object] substitute_markup [list [my value] text/html]]
+ ::html::t -disableOutputEscaping [[my object] substitute_markup [my value]]
+ } else {
+ ::html::t -disableOutputEscaping [my value]
+ }
+ }
+ ::html::div
+ }
+
+ richtext instproc check=safe_html {value} {
+ # don't check if the user has sufficient permissions on the package
+ if {[::xo::cc permission \
+ -object_id [::xo::cc package_id] \
+ -privilege swa \
+ -party_id [::xo::cc user_id]]} {
+ set msg ""
+ } else {
+ set msg [ad_html_security_check $value]
+ }
+ if {$msg ne ""} {
+ my uplevel [list set errorMsg $msg]
+ return 0
+ }
+ return 1
+ }
+ richtext instproc pretty_value {v} {
+ # for richtext, perform minimal output escaping
+ if {[my wiki]} {
+ return [[my object] substitute_markup $v]
+ } else {
+ return [string map [list @ "@"] $v]
+ }
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::richtext::ckeditor
+ #
+ # mode: wysiwyg, source
+ # skin: kama, v2, office2003
+ # extraPlugins: tcl-list, is converted to comma list for js
+ #
+ ###########################################################
+ Class richtext::ckeditor -superclass richtext -parameter {
+ {editor ckeditor}
+ {mode wysiwyg}
+ {skin kama}
+ {toolbar Full}
+ {CSSclass xowiki-ckeditor}
+ {uiColor ""}
+ {inplace false}
+ {CSSclass xowiki-ckeditor}
+ {customConfig "../ck_config.js"}
+ {callback "/* callback code */"}
+ {destroy_callback "/* callback code */"}
+ {extraPlugins ""}
+ {templatesFiles ""}
+ {templates ""}
+ {contentsCss /resources/xowiki/ck_contents.css}
+ {imageSelectorDialog /xowiki/ckeditor-images/}
+ }
+ richtext::ckeditor set editor_mixin 1
+ richtext::ckeditor instproc initialize {} {
+ if {[my set inplace]} {
+ my append help_text " #xowiki.ckeip_help#"
+ }
+ next
+ my set widget_type richtext
+ # Mangle the id to make it compatible with jquery; most probably
+ # not optimal and just a temporary solution
+ regsub -all {[.:]} [my id] "" id
+ my id $id
+ }
+
+ richtext::ckeditor instproc js_image_helper {} {
+ ::xo::Page requireJS {
+ function xowiki_image_callback(editor) {
+ $(editor.element.$.form).submit(function(e) {
+ calc_image_tags_to_wiki_image_links(this);
+ });
+ editor.setData(calc_wiki_image_links_to_image_tags(editor.getData()));
+ }
+
+ function calc_image_tags_to_wiki_image_links (form) {
+ var calc = function() {
+ var wiki_link = $(this).attr('alt');
+ $(this).replaceWith('[['+wiki_link+']]');
+ }
+ $(form).find('iframe').each(function() {
+ $(this).contents().find('img[type="wikilink"]').each(calc);
+ });
+
+ $(form).find('textarea.ckeip').each(function() {
+ var contents = $('
\n"
+ } else {
+ return [join $hrefs {, }]
+ }
+ } else {
+ foreach o [my set options] {
+ foreach {label value} $o break
+ #my log "comparing '$value' with '$v'"
+ if {$value eq $v} {
+ if {[my as_box]} {
+ return [$object include [list $value -decoration rightbox]]
+ }
+ set href [$package_id pretty_link -parent_id $parent_id $value]
+ return "$label"
+ }
+ }
+ }
+ }
+
+ abstract_page instproc render_input {} {
+ my compute_options
+ next
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::form_page
+ #
+ ###########################################################
+ Class form_page -superclass abstract_page -parameter {
+ {form}
+ {where}
+ {entry_label title}
+ }
+
+ form_page instproc initialize {} {
+ my instvar form_object_item_ids package_id object
+ if {![my exists form]} { return }
+ next
+ set form_name [my form]
+ set package_id [$object package_id]
+ set form_objs [::xowiki::Weblog instantiate_forms \
+ -parent_id [$object parent_id] \
+ -default_lang [$object lang] \
+ -forms $form_name -package_id $package_id]
+
+ #set form_obj [[my object] resolve_included_page_name $form_name]
+ if {$form_objs eq ""} {error "Cannot lookup Form '$form_name'"}
+
+ set form_object_item_ids [list]
+ foreach form_obj $form_objs {lappend form_object_item_ids [$form_obj item_id]}
+ }
+ form_page instproc compute_options {} {
+ my instvar form_object_item_ids where package_id
+ #my msg "[my name] compute_options [my exists form]"
+ if {![my exists form]} {
+ return
+ }
+
+ array set wc {tcl true h "" vars "" sql ""}
+ if {[info exists where]} {
+ array set wc [::xowiki::FormPage filter_expression $where &&]
+ #my msg "where '$where' => wc=[array get wc]"
+ }
+
+ set from_package_ids {}
+ set package_path [::$package_id package_path]
+ if {[llength $package_path] > 0} {
+ foreach p $package_path {
+ lappend from_package_ids [$p id]
+ }
+ }
+ set items [::xowiki::FormPage get_form_entries \
+ -base_item_ids $form_object_item_ids \
+ -form_fields [list] \
+ -publish_status ready \
+ -h_where [array get wc] \
+ -package_id $package_id \
+ -from_package_ids $from_package_ids]
+
+ set options [list]
+ foreach i [$items children] {
+ #
+ # If the form_page has a different package_id, prepend the
+ # package_url to the name. TODO: We assume here, that the form_pages
+ # have no special parent_id.
+ #
+ set object_package_id [$i package_id]
+ if {$package_id != $object_package_id} {
+ set package_prefix /[$object_package_id package_url]
+ } else {
+ set package_prefix ""
+ }
+
+ lappend options [list [$i title] $package_prefix[$i name]]
+ }
+ my options $options
+ }
+
+ form_page instproc pretty_value {v} {
+ my options [my get_labels $v]
+ if {![my exists form_object_item_ids]} {
+ error "No forms specified for form_field '[my name]'"
+ }
+ my set package_id [[lindex [my set form_object_item_ids] 0] package_id]
+ next
+ }
+
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::page
+ #
+ ###########################################################
+ Class page -superclass abstract_page -parameter {
+ {type ::xowiki::Page}
+ {with_subtypes false}
+ {glob}
+ {entry_label name}
+ }
+
+ page instproc compute_options {} {
+ my instvar type with_subtypes glob
+
+ set extra_where_clause ""
+ if {[my exists glob]} {
+ append extra_where_clause [::xowiki::Includelet glob_clause $glob]
+ }
+
+ set package_id [[my object] package_id]
+ set options [list]
+ db_foreach [my qn instance_select] \
+ [$type instance_select_query \
+ -folder_id [$package_id folder_id] \
+ -with_subtypes $with_subtypes \
+ -select_attributes [list title] \
+ -from_clause ", xowiki_page p" \
+ -where_clause "p.page_id = bt.revision_id $extra_where_clause" \
+ -orderby ci.name \
+ ] {
+ lappend options [list [set [my entry_label]] $name]
+ }
+ my options $options
+ }
+
+ page instproc pretty_value {v} {
+ my set package_id [[my object] package_id]
+ next
+ }
+
+
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::DD
+ #
+ ###########################################################
+
+ Class DD -superclass select
+ DD instproc initialize {} {
+ my options {
+ {01 1} {02 2} {03 3} {04 4} {05 5} {06 6} {07 7} {08 8} {09 9} {10 10}
+ {11 11} {12 12} {13 13} {14 14} {15 15} {16 16} {17 17} {18 18} {19 19} {20 20}
+ {21 21} {22 22} {23 23} {24 24} {25 25} {26 26} {27 27} {28 28} {29 29} {30 30}
+ {31 31}
+ }
+ next
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::HH24
+ #
+ ###########################################################
+
+ Class HH24 -superclass select
+ HH24 instproc initialize {} {
+ my options {
+ {00 0} {01 1} {02 2} {03 3} {04 4} {05 5} {06 6} {07 7} {08 8} {09 9}
+ {10 10} {11 11} {12 12} {13 13} {14 14} {15 15} {16 16} {17 17} {18 18} {19 19}
+ {20 20} {21 21} {22 22} {23 23}
+ }
+ next
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::MI
+ #
+ ###########################################################
+
+ Class MI -superclass select
+ MI instproc value args {
+ if {[llength $args] == 0} {return [my set value]} else {
+ set v [lindex $args 0]
+ if {$v eq ""} {return [my set value ""]} else {
+ # round to 5 minutes
+ my set value [lindex [my options] [expr {($v + 2) / 5}] 1]
+ }
+ }
+ }
+ MI instproc initialize {} {
+ my options {
+ {00 0} {05 5} {10 10} {15 15} {20 20} {25 25}
+ {30 30} {35 35} {40 40} {45 45} {50 50} {55 55}
+ }
+ next
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::MM
+ #
+ ###########################################################
+
+ Class MM -superclass select
+ MM instproc initialize {} {
+ my options {
+ {01 1} {02 2} {03 3} {04 4} {05 5} {06 6} {07 7} {08 8} {09 9} {10 10}
+ {11 11} {12 12}
+ }
+ next
+ }
+ ###########################################################
+ #
+ # ::xowiki::formfield::mon
+ #
+ ###########################################################
+
+ Class mon -superclass select
+ mon instproc initialize {} {
+ set values [lang::message::lookup [my locale] acs-lang.localization-abmon]
+ if {[lang::util::translator_mode_p]} {set values [::xo::localize $values]}
+ set last 0
+ foreach m {1 2 3 4 5 6 7 8 9 10 11 12} {
+ lappend options [list [lindex $values $last] $m]
+ set last $m
+ }
+ my options $options
+ next
+ }
+ ###########################################################
+ #
+ # ::xowiki::formfield::month
+ #
+ ###########################################################
+
+ Class month -superclass select
+ month instproc initialize {} {
+ set values [lang::message::lookup [my locale] acs-lang.localization-mon]
+ if {[lang::util::translator_mode_p]} {set values [::xo::localize $values]}
+ set last 0
+ foreach m {1 2 3 4 5 6 7 8 9 10 11 12} {
+ lappend options [list [lindex $values $last] $m]
+ set last $m
+ }
+ my options $options
+ next
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::YYYY
+ #
+ ###########################################################
+
+ Class YYYY -superclass numeric -parameter {
+ {size 4}
+ {maxlength 4}
+ } -extend_slot validator YYYY
+
+ YYYY instproc check=YYYY {value} {
+ if {$value ne ""} {
+ return [expr {[catch {clock scan "$value-01-01 00:00:00"}] == 0}]
+ }
+ return 1
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::youtube_url
+ #
+ ###########################################################
+ Class youtube_url -superclass text
+ youtube_url set urlre {^http://www.youtube.com/watch[?]v=([^?]+)([?]?)}
+
+ youtube_url instproc initialize {} {
+ next
+ if {[my help_text] eq ""} {my help_text "#xowiki.formfield-youtube_url-help_text#"}
+ }
+ youtube_url instproc pretty_value {v} {
+ if {$v eq ""} {
+ return ""
+ } elseif {[regexp [[self class] set urlre] $v _ name]} {
+ return "\n"
+ } else {
+ return "'$v' does not look like a youtube url"
+ }
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::image_url
+ #
+ ###########################################################
+
+ Class image_url -superclass text \
+ -extend_slot validator image_check \
+ -parameter {
+ href cssclass
+ {float left} width height
+ padding {padding-right 10px} padding-left padding-top padding-bottom
+ margin margin-left margin-right margin-top margin-bottom
+ border border-width position top botton left right
+ }
+ image_url instproc initialize {} {
+ next
+ if {[my help_text] eq ""} {my help_text "#xowiki.formfield-image_url-help_text#"}
+ }
+ image_url instproc entry_name {value} {
+ set value [string map [list %2e .] $value]
+ if {![regexp -nocase {/([^/]+)[.](gif|jpg|jpeg|png)} $value _ name ext]} {
+ return ""
+ }
+ return file:$name.$ext
+ }
+ image_url instproc check=image_check {value} {
+ if {$value eq ""} {return 1}
+ set entry_name [my entry_name $value]
+ if {$entry_name eq ""} {
+ my log "--img '$value' does not appear to be an image"
+ # no image?
+ return 0
+ }
+ set folder_id [[my object] set parent_id]
+ if {[::xo::db::CrClass lookup -name $entry_name -parent_id $folder_id]} {
+ my log "--img entry named $entry_name exists already"
+ # file exists already
+ return 1
+ }
+ if {[regexp {^file://(.*)$} $value _ path]} {
+ set f [open $path r]
+ fconfigure $f translation binary
+ set img [read $f]
+ close $f
+ } elseif {[catch {
+ set r [::xo::HttpRequest new -url $value -volatile]
+ set img [$r set data]
+ } errorMsg]} {
+ # cannot transfer image
+ my log "--img cannot tranfer image '$value' ($errorMsg)"
+ return 0
+ }
+ #my msg "guess mime_type of $entry_name = [::xowiki::guesstype $entry_name]"
+ set import_file [ns_tmpnam]
+ ::xowiki::write_file $import_file $img
+ set file_object [::xowiki::File new -destroy_on_cleanup \
+ -title $entry_name \
+ -name $entry_name \
+ -parent_id $folder_id \
+ -mime_type [::xowiki::guesstype $entry_name] \
+ -package_id [[my object] package_id] \
+ -creation_user [::xo::cc user_id] \
+ ]
+ $file_object set import_file $import_file
+ $file_object save_new
+ return 1
+ }
+ image_url instproc pretty_value {v} {
+ set entry_name [my entry_name $v]
+ return [my pretty_image -parent_id [[my object] parent_id] $entry_name]
+ }
+
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::include
+ #
+ ###########################################################
+
+ # note that the includelet "include" can be used for implementing symbolic links
+ # to other xowiki pages.
+ Class include -superclass text -parameter {
+ }
+
+ include instproc pretty_value {v} {
+ if {$v eq ""} { return $v }
+
+ my instvar object
+ set item_id [$object get_property_from_link_page item_id]
+ if {$item_id == 0} {
+ # Here, we could call "::xowiki::Link render" to offer the user means
+ # to create the entry like with [[..]], if he has sufficent permissions...;
+ # when $(package_id) is 0, the referenced package could not be
+ # resolved
+ return "Cannot resolve symbolic link '$v'"
+ }
+ set link_type [$object get_property_from_link_page link_type]
+ $object lappend references [list $item_id $link_type]
+
+ #
+ # resetting esp. the item-id is dangerous. Therefore we reset it immediately after the rendering
+ #
+ $item_id set_resolve_context \
+ -package_id [$object package_id] -parent_id [$object parent_id] \
+ -item_id [$object item_id]
+ set html [$item_id render]
+ #my msg "reset resolve-context"
+ $item_id reset_resolve_context
+
+ return $html
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::redirect
+ #
+ ###########################################################
+
+ Class redirect -superclass text
+ redirect instproc pretty_value {v} {
+ #ad_returnredirect -allow_complete_url $v
+ #ad_script_abort
+ return [[[my object] package_id] returnredirect $v]
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::CompoundField
+ #
+ ###########################################################
+
+ Class CompoundField -superclass FormField -parameter {
+ {components ""}
+ {CSSclass compound-field}
+ } -extend_slot validator compound
+
+ CompoundField instproc check=compound {value} {
+ #my msg "check compound in [my components]"
+ foreach c [my components] {
+ set error [$c validate [self]]
+ if {$error ne ""} {
+ set msg "[$c label]: $error"
+ my uplevel [list set errorMsg $msg]
+ #util_user_message -message "Error in compound field [$c name]: $error"
+ return 0
+ }
+ }
+ return 1
+ }
+
+ CompoundField instproc set_disabled {disable} {
+ #my msg "[my name] set disabled $disable"
+ if {$disable} {
+ my set disabled true
+ } else {
+ my unset -nocomplain disabled
+ }
+ foreach c [my components] {
+ $c set_disabled $disable
+ }
+ }
+
+ CompoundField instproc value {args} {
+ if {[llength $args] == 0} {
+ set v [my get_compound_value]
+ #my msg "[my name]: reading compound value => '$v'"
+ return $v
+ } else {
+ #my msg "[my name]: setting compound value => '[lindex $args 0]'"
+ my set_compound_value [lindex $args 0]
+ }
+ }
+
+ CompoundField instproc set_compound_value {value} {
+ if {[catch {array set {} $value} errorMsg]} {
+ # this branch could be taken, when the field was retyped
+ ns_log notice "CompoundField: error during setting compound value with $value: $errorMsg"
+ }
+ # set the value parts for each components
+ foreach c [my components] {
+ # Set only those parts, for which attribute values pairs are
+ # given. Components might have their own default values, which
+ # we do not want to overwrite ...
+ if {[info exists ([$c name])]} {
+ $c value $([$c name])
+ }
+ }
+ }
+
+ CompoundField instproc get_compound_value {} {
+ # Set the internal representation based on the components values.
+ set value [list]
+ foreach c [my components] {
+ #my msg "lappending [$c name] [$c value] "
+ lappend value [$c name] [$c value]
+ }
+ #my msg "[my name]: get_compound_value returns value=$value"
+ return $value
+ }
+
+ CompoundField instproc create_components {spec_list} {
+ #
+ # Build a component structure based on a list of specs
+ # of the form {name spec}.
+ #
+ my set structure $spec_list
+ my set components [list]
+ foreach entry $spec_list {
+ foreach {name spec} $entry break
+ #
+ # create for each component a form field
+ #
+ set c [::xowiki::formfield::FormField create [self]::$name \
+ -name [my name].$name -id [my id].$name \
+ -locale [my locale] -object [my object] \
+ -spec $spec]
+ my set component_index([my name].$name) $c
+ my lappend components $c
+ }
+ }
+
+ CompoundField instproc get_component {component_name} {
+ set key component_index([my name].$component_name)
+ if {[my exists $key]} {
+ return [my set $key]
+ }
+ error "no component named $component_name of compound field [my name]"
+ }
+
+ CompoundField instproc exists_named_sub_component args {
+ # Iterate along the argument list to check components of a deeply
+ # nested structure. For example,
+ #
+ # my check_named_sub_component a b
+ #
+ # returns 0 or one depending whether there exists a component "a"
+ # with a subcomponent "b".
+ set component_name [my name]
+ set sub [self]
+ foreach e $args {
+ append component_name .$e
+ if {![$sub exists component_index($component_name)]} {
+ return 0
+ }
+ set sub [$sub set component_index($component_name)]
+ }
+ return 1
+ }
+
+ CompoundField instproc get_named_sub_component args {
+ # Iterate along the argument list to get components of a deeply
+ # nested structure. For example,
+ #
+ # my get_named_sub_component a b
+ #
+ # returns the object of the subcomponent "b" of component "a"
+ set component_name [my name]
+ set sub [self]
+ foreach e $args {
+ append component_name .$e
+ #my msg "check $sub set component_index($component_name)"
+ set sub [$sub set component_index($component_name)]
+ }
+ return $sub
+ }
+
+ CompoundField instproc get_named_sub_component_value {{-default ""} args} {
+ if {[eval my exists_named_sub_component $args]} {
+ return [[eval my get_named_sub_component $args] value]
+ } else {
+ return $default
+ }
+ }
+
+ CompoundField instproc generate_fieldnames {{-prefix "v-"} n} {
+ set names [list]
+ for {set i 1} {$i <= $n} {incr i} {lappend names $prefix$i}
+ return $names
+ }
+
+ CompoundField instproc render_input {} {
+ #
+ # Render content within in a fieldset, but with labels etc.
+ #
+ html::fieldset [my get_attributes id {CSSclass class}] {
+ foreach c [my components] { $c render }
+ }
+ }
+
+ CompoundField instproc has_instance_variable {var value} {
+ set r [next]
+ if {$r} {return 1}
+ foreach c [my components] {
+ set r [$c has_instance_variable $var $value]
+ if {$r} {return 1}
+ }
+ return 0
+ }
+
+ CompoundField instproc convert_to_internal {} {
+ foreach c [my components] {
+ $c convert_to_internal
+ }
+ }
+
+ CompoundField instproc get_spec {} {
+ set component_specs [list]
+ foreach c [my components] {
+ lappend component_specs [$c get_spec]
+ }
+ my set style "margin: 0px; padding: 0px;"
+ set atts [my get_attributes id style]
+ return [list "fieldset" $atts $component_specs]
+ }
+
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::label
+ #
+ ###########################################################
+
+ Class label -superclass FormField -parameter {
+ {disableOutputEscaping false}
+ }
+ label instproc render_item {} {
+ # sanity check; required and label do not fit well together
+ if {[my required]} {my required false}
+ next
+ }
+ label instproc render_input {} {
+ if {[my disableOutputEscaping]} {
+ ::html::t -disableOutputEscaping [my value]
+ } else {
+ ::html::t [my value]
+ }
+ # Include labels as hidden fields to avoid surprises when
+ # switching field types to labels.
+ my set type hidden
+ next
+ }
+
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::child_pages
+ #
+ ###########################################################
+ Class child_pages -superclass label -parameter {
+ {form}
+ {publish_status all}
+ }
+ child_pages instproc initialize {} {
+ next
+ #
+ # for now, we allow just FormPages as child_pages
+ #
+ if {![my exists form]} { return }
+ my instvar object
+ my set form_objs [::xowiki::Weblog instantiate_forms \
+ -parent_id [$object parent_id] \
+ -default_lang [$object lang] \
+ -forms [my form] \
+ -package_id [$object package_id]]
+ }
+ child_pages instproc pretty_value {v} {
+ if {[my exists form_objs]} {
+ my instvar object
+ set count 0
+ foreach form [my set form_objs] {
+ incr count [$form count_usages \
+ -package_id [$object package_id] \
+ -parent_id [$object item_id] \
+ -publish_status [my publish_status]]
+ }
+ return $count
+ } else {
+ return 0-NULL
+ }
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::date
+ #
+ ###########################################################
+
+ Class date -superclass CompoundField -parameter {
+ {format "DD MONTH YYYY"}
+ {display_format "%Y-%m-%d %T"}
+ }
+ # The default of a date might be all relative dates
+ # supported by clock scan. These include "now", "tomorrow",
+ # "yesterday", "next week", .... use _ for blanks
+
+ date instproc initialize {} {
+ #my msg "DATE has value [my value]//d=[my default] format=[my format] disabled?[my exists disabled]"
+ my set widget_type date
+ my set format [string map [list _ " "] [my format]]
+ my array set defaults {year 2000 month 01 day 01 hour 00 min 00 sec 00}
+ my array set format_map {
+ SS {SS %S 1}
+ MI {MI %M 1}
+ HH24 {HH24 %H 1}
+ DD {DD %e 0}
+ MM {MM %m 1}
+ MON {mon %m 1}
+ MONTH {month %m 1}
+ YYYY {YYYY %Y 0}
+ }
+ #my msg "[my name] initialize date, format=[my format] components=[my components]"
+ foreach c [my components] {$c destroy}
+ my components [list]
+
+ foreach element [split [my format]] {
+ if {![my exists format_map($element)]} {
+ #
+ # We add undefined formats as literal texts in the edit form
+ #
+ set name $element
+ set c [::xowiki::formfield::label create [self]::$name \
+ -name [my name].$name -id [my id].$name \
+ -locale [my locale] -object [my object] \
+ -value $element]
+ $c set_disabled [my exists disabled]
+ if {[lsearch [my components] $c] == -1} {my lappend components $c}
+ continue
+ }
+ foreach {class code trim_zeros} [my set format_map($element)] break
+ #
+ # create for each component a form field
+ #
+ set name $class
+ set c [::xowiki::formfield::$class create [self]::$name \
+ -name [my name].$name -id [my id].$name \
+ -locale [my locale] -object [my object]]
+ #my msg "creating [my name].$name"
+ $c set_disabled [my exists disabled]
+ $c set code $code
+ $c set trim_zeros $trim_zeros
+ if {[lsearch [my components] $c] == -1} {my lappend components $c}
+ }
+ }
+
+ date instproc set_compound_value {value} {
+ #my msg "[my name] original value '[my value]' // passed='$value' disa?[my exists disabled]"
+ # if {$value eq ""} {return}
+ if { $value eq {} } {
+ # We need to reset component values so that
+ # instances of this class can be used as flyweight
+ # objects. Otherwise, we get side-effects when
+ # we render the input widget.
+ foreach c [my components] {
+ $c value ""
+ }
+ return
+ }
+ set value [::xo::db::tcl_date $value tz]
+ #my msg "transformed value '$value'"
+ if {$value ne ""} {
+ set ticks [clock scan [string map [list _ " "] $value]]
+ } else {
+ set ticks ""
+ }
+ my set defaults(year) [clock format $ticks -format %Y]
+ my set defaults(month) [clock format $ticks -format %m]
+ my set defaults(day) [clock format $ticks -format %e]
+ my set defaults(hour) [clock format $ticks -format %H]
+ my set defaults(min) [clock format $ticks -format %M]
+ #my set defaults(sec) [clock format $ticks -format %S]
+
+ # set the value parts for each components
+ foreach c [my components] {
+ if {[$c istype ::xowiki::formfield::label]} continue
+ if {$ticks ne ""} {
+ set value_part [clock format $ticks -format [$c set code]]
+ if {[$c set trim_zeros]} {
+ set value_part [string trimleft $value_part 0]
+ if {$value_part eq ""} {set value_part 0}
+ }
+ } else {
+ set value_part ""
+ }
+ #my msg "ticks=$ticks $c value $value_part"
+ $c value $value_part
+ }
+ }
+
+ date instproc get_compound_value {} {
+ # Set the internal representation of the date based on the components values.
+ # Internally, the ansi date format is used.
+ set year ""; set month ""; set day ""; set hour ""; set min ""; set sec ""
+ if {[my isobject [self]::YYYY]} {set year [[self]::YYYY value]}
+ if {[my isobject [self]::month]} {set month [[self]::month value]}
+ if {[my isobject [self]::mon]} {set month [[self]::mon value]}
+ if {[my isobject [self]::MM]} {set month [[self]::MM value]}
+ if {[my isobject [self]::DD]} {set day [[self]::DD value]}
+ if {[my isobject [self]::HH24]} {set hour [[self]::HH24 value]}
+ if {[my isobject [self]::MI]} {set min [[self]::MI value]}
+ if {[my isobject [self]::SS]} {set sec [[self]::SS value]}
+ if {"$year$month$day$hour$min$sec" eq ""} {
+ return ""
+ }
+ # Validation happens after the value is retrieved.
+ # To avoid errors in "clock scan", fix the year if necessary
+ if {![string is integer $year]} {set year 0}
+
+ foreach v [list year month day hour min sec] {
+ if {[set $v] eq ""} {set $v [my set defaults($v)]}
+ }
+ #my msg "$year-$month-$day ${hour}:${min}:${sec}"
+ if {[catch {set ticks [clock scan "$year-$month-$day ${hour}:${min}:${sec}"]}]} {
+ set ticks 0 ;# we assume that the validator flags these values
+ }
+ # TODO: TZ???
+ #my msg "DATE [my name] get_compound_value returns [clock format $ticks -format {%Y-%m-%d %T}]"
+ return [clock format $ticks -format "%Y-%m-%d %T"]
+ }
+
+ date instproc pretty_value {v} {
+ my instvar display_format
+ #
+ # Internally, we use the ansi date format. For displaying the date,
+ # use the specified display format and present the time localized.
+ #
+ # Drop of the value after the "." we assume to have a date in the local zone
+ regexp {^([^.]+)[.]} $v _ v
+ #return [clock format [clock scan $v] -format [string map [list _ " "] [my display_format]]]
+ if {$display_format eq "pretty-age"} {
+ return [::xowiki::utility pretty_age -timestamp [clock scan $v] -locale [my locale]]
+ } else {
+ return [lc_time_fmt $v [string map [list _ " "] [my display_format]] [my locale]]
+ }
+ }
+
+ date instproc render_input {} {
+ #
+ # render the content inline withing a fieldset, without labels etc.
+ #
+ my set style "margin: 0px; padding: 0px;"
+ html::fieldset [my get_attributes id style] {
+ foreach c [my components] { $c render_input }
+ }
+ }
+
+
+ ###########################################################
+ #
+ # ::xowiki::boolean
+ #
+ ###########################################################
+
+ Class boolean -superclass radio -parameter {
+ {default t}
+ }
+ boolean instproc value_if_nothing_is_returned_from_form {default} {
+ if {[my exists disabled]} {return $default} else {return f}
+ }
+ boolean instproc initialize {} {
+ # should be with cvs head message catalogs:
+ my options {{#acs-kernel.common_Yes# t} {#acs-kernel.common_No# f}}
+ #my options {{No f} {#acs-kernel.common_Yes# t}}
+ next
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::boolean_image
+ #
+ ###########################################################
+
+ Class create boolean_image -superclass FormField -parameter {
+ {default t}
+ {t_img_url /resources/xowiki/examples/check_richtig.png}
+ {f_img_url /resources/xowiki/examples/check_falsch.png}
+ {CSSclass img_boolean}
+ }
+ boolean_image instproc initialize {} {
+ my type hidden
+ my set widget_type boolean(hidden)
+ }
+ boolean_image instproc render_input {} {
+ my instvar t_img_url f_img_url CSSclass
+ set title [expr {[my exists __render_help_text_as_title_attr] ? [my set help_text] : ""}]
+ ::html::img \
+ -title $title \
+ -class $CSSclass \
+ -src [expr {[my value] ? $t_img_url : $f_img_url}] \
+ -onclick "toggle_img_boolean(this,'$t_img_url','$f_img_url')"
+ ::html::input -type hidden -name [my name] -value [my value]
+
+ ::xo::Page requireJS {
+ function toggle_img_boolean (element,t_img_url,f_img_url) {
+ var input = $(element).next();
+ var state = input.val()== "t";
+ if (state) {
+ input.val('f');
+ $(element).attr('src',f_img_url);
+ } else {
+ input.val('t');
+ $(element).attr('src',t_img_url);
+ }
+ }
+ }
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::scale
+ #
+ ###########################################################
+
+ Class scale -superclass radio -parameter {{n 5} {horizontal true}}
+ scale instproc initialize {} {
+ my instvar n
+ set options [list]
+ for {set i 1} {$i <= $n} {incr i} {
+ lappend options [list $i $i]
+ }
+ my options $options
+ next
+ }
+
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::form
+ #
+ ###########################################################
+
+ Class form -superclass richtext -parameter {
+ {height 200}
+ } -extend_slot validator form
+
+ form instproc check=form {value} {
+ set form $value
+ #my msg form=$form
+ dom parse -simple -html $form doc
+ $doc documentElement root
+ set rootNodeName ""
+ if {$root ne ""} {set rootNodeName [$root nodeName]}
+ return [expr {$rootNodeName eq "form"}]
+ }
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::form_constraints
+ #
+ ###########################################################
+
+ Class form_constraints -superclass textarea -parameter {
+ {rows 5}
+ } -extend_slot validator form_constraints
+ # the form_constraints checker is defined already on the ::xowiki::Page level
+
+
+ ###########################################################
+ #
+ # ::xowiki::formfield::event
+ #
+ ###########################################################
+
+ Class event -superclass CompoundField -parameter {
+ {multiday false}
+ }
+
+ event instproc initialize {} {
+ #my msg "event initialize [my exists __initialized], multi=[my multiday] state=[my set __state]"
+ if {[my set __state] ne "after_specs"} return
+ my set widget_type event
+ if {[my multiday]} {
+ set dtend_format DD_MONTH_YYYY_#xowiki.event-hour_prefix#_HH24_MI
+ set dtend_display_format %Q_%X
+ } else {
+ set dtend_format HH24_MI
+ set dtend_display_format %X
+ }
+ my create_components [subst {
+ {summary {richtext,required,editor=wym,height=150px,label=#xowiki.event-title_of_event#}}
+ {dtstart {date,required,format=DD_MONTH_YYYY_#xowiki.event-hour_prefix#_HH24_MI,
+ default=now,label=#xowiki.event-start_of_event#,display_format=%Q_%X}}
+ {dtend date,format=$dtend_format,default=now,label=#xowiki.event-end_of_event#,display_format=$dtend_display_format}
+ {location text,label=#xowiki.event-location#}
+ }]
+ my set __initialized 1
+ }
+
+ event instproc get_compound_value {} {
+ if {![my exists __initialized]} {
+ return ""
+ }
+ set dtstart [my get_component dtstart]
+ set dtend [my get_component dtend]
+ if {![my multiday]} {
+ # If the event is not a multi-day-event, the end_day is not
+ # given by the dtend widget, but is taken from dtstart.
+ set end_day [lindex [$dtstart value] 0]
+ set end_time [lindex [$dtend value] 1]
+ $dtend value "$end_day $end_time"
+ #my msg "[$dtend name] set to '$end_day $end_time' ==> $dtend, [$dtend value]"
+ }
+ next
+ }
+
+ event instproc pretty_value {v} {
+ array set {} [my value]
+ set dtstart [my get_component dtstart]
+ set dtstart_val [$dtstart value]
+ set dtstart_iso [::xo::ical clock_to_iso [clock scan $dtstart_val]]
+
+ set dtend [my get_component dtend]
+ set dtend_val [$dtend value]
+ set dtend_txt ""
+ if {$dtend_val ne ""} {
+ set dtend_iso [::xo::ical clock_to_iso [clock scan $dtend_val]]
+ set dtend_txt " - [$dtend pretty_value $dtend_val]"
+ }
+
+ set summary_txt "[[my get_component summary] value]"
+ set location [my get_component location]
+ set location_val [$location value]
+ set location_txt ""
+ if {$location_val ne ""} {
+ set location_label [$location label]
+ if {[regexp {^#(.+)#$} $location_label _ msg_key]} {
+ set location_label [lang::message::lookup [my locale] $msg_key]
+ }
+ set location_txt "$location_label: $location_val"
+ }
+
+ append result \
+ "
\
+ $location_txt \
+ "[my set log]
"
+ }
+ Importer instproc report_line {obj operation} {
+ set href [$obj pretty_link]
+ set name [[$obj package_id] external_name -parent_id [$obj parent_id] [$obj name]]
+ my append log " \n"
+ }
+ Importer instproc report {} {
+ my instvar added updated replaced inherited
+ return "$added objects newly inserted,\
+ $updated objects updated, $replaced objects replaced, $inherited inherited (update ignored)$operation $name "
+ foreach d [lsort [my array names html]] {
+ append result "
"
+ return $result
+ }
+
+ ::xowiki::Includelet proc html_to_text {string} {
+ return [string map [list "&" &] $string]
+ }
+
+ ::xowiki::Includelet proc js_name {name} {
+ return [string map [list : _ # _] $name]
+ }
+
+ ::xowiki::Includelet proc js_encode {string} {
+ string map [list \n \\n \" {\"} ' {\'}] $string
+ }
+
+ ::xowiki::Includelet proc html_encode {string} {
+ # ' is not a known entity to some validators, so we use the
+ # numerical entity here for encoding "'"
+ return [string map [list & "&" < "<" > ">" \" """ ' "'"] $string]
+ }
+
+
+ ::xowiki::Includelet proc html_id {name} {
+ # Construct a valid HTML id or name.
+ # For details, see http://www.w3.org/TR/html4/types.html
+ #
+ # For XOTcl object names, strip first the colons
+ set name [string trimleft $name :]
+
+ # make sure, the ID starts with characters
+ if {![regexp {^[A-Za-z]} $name]} {
+ set name id_$name
+ }
+
+ # replace unwanted characters
+ regsub -all {[^A-Za-z0-9_:.-]} $name _ name
+ return $name
+ }
+
+ ::xowiki::Includelet proc publish_status_clause {{-base_table ci} value} {
+ if {$value eq "all"} {
+ # legacy
+ set publish_status_clause ""
+ } else {
+ array set valid_state [list production 1 ready 1 live 1 expired 1]
+ set clauses [list]
+ foreach state [split $value |] {
+ if {![info exists valid_state($state)]} {
+ error "no such state: '$state'; valid states are: production, ready, live, expired"
+ }
+ lappend clauses "$base_table.publish_status='$state'"
+ }
+ set publish_status_clause " and ([join $clauses { or }])"
+ }
+ return $publish_status_clause
+ }
+
+ ::xowiki::Includelet proc locale_clause {
+ -revisions
+ -items
+ package_id
+ locale
+ } {
+ set default_locale [$package_id default_locale]
+ set system_locale ""
+
+ set with_system_locale [regexp {(.*)[+]system} $locale _ locale]
+ if {$locale eq "default"} {
+ set locale $default_locale
+ set include_system_locale 0
+ }
+ #my msg "--L with_system_locale=$with_system_locale, locale=$locale, default_locale=$default_locale"
+
+ set locale_clause ""
+ if {$locale ne ""} {
+ set locale_clause " and $revisions.nls_language = '$locale'"
+ if {$with_system_locale} {
+ set system_locale [lang::system::locale -package_id $package_id]
+ #my msg "system_locale=$system_locale, default_locale=$default_locale"
+ if {$system_locale ne $default_locale} {
+ set locale_clause " and ($revisions.nls_language = '$locale'
+ or $revisions.nls_language = '$system_locale' and not exists
+ (select 1 from cr_items i where i.name = '[string range $locale 0 1]:' ||
+ substring($items.name,4) and i.parent_id = $items.parent_id))"
+ }
+ }
+ }
+
+ #my msg "--locale $locale, def=$default_locale sys=$system_locale, cl=$locale_clause locale_clause=$locale_clause"
+ return [list $locale $locale_clause]
+ }
+
+ ::xowiki::Includelet instproc category_clause {category_spec {item_ref p.item_id}} {
+ # the category_spec has the syntax "a,b,c|d,e", where the values are category_ids
+ # pipe symbols are or-operations, commas are and-operations;
+ # no parenthesis are permitted
+ set extra_where_clause ""
+ set or_names [list]
+ set ors [list]
+ foreach cid_or [split $category_spec |] {
+ set ands [list]
+ set and_names [list]
+ foreach cid_and [split $cid_or ,] {
+ lappend and_names [::category::get_name $cid_and]
+ lappend ands "exists (select 1 from category_object_map \
+ where object_id = $item_ref and category_id = $cid_and)"
+ }
+ lappend or_names "[join $and_names { and }]"
+ lappend ors "([join $ands { and }])"
+ }
+ set cnames "[join $or_names { or }]"
+ set extra_where_clause "and ([join $ors { or }])"
+ #my log "--cnames $category_spec -> $cnames"
+ return [list $cnames $extra_where_clause]
+ }
+
+ ::xowiki::Includelet proc parent_id_clause {
+ {-base_table bt}
+ {-use_package_path true}
+ {-parent_id ""}
+ -base_package_id:required
+ } {
+ #
+ # Get the package path and from it, the folder_ids. The parent_id
+ # of the returned pages should be a direct child of the folder.
+ #
+ if {$parent_id eq ""} {
+ set parent_id [$base_package_id folder_id]
+ }
+ set packages [$base_package_id package_path]
+ if {$use_package_path && [llength $packages] > 0} {
+ set parent_ids [list $parent_id]
+ foreach p $packages {lappend parent_ids [$p folder_id]}
+ return "$base_table.parent_id in ([join $parent_ids ,])"
+ } else {
+ return "$base_table.parent_id = $parent_id"
+ }
+ }
+
+ ::xowiki::Includelet proc glob_clause {{-base_table ci} {-attribute name} value} {
+ # Return a clause for name matching.
+ # value uses * for matching
+ set glob [string map [list * %] $value]
+ return " and $base_table.$attribute like '$glob'"
+ }
+
+ #
+ # Other helpers
+ #
+
+ ::xowiki::Includelet proc listing {
+ -package_id
+ {-count:boolean false}
+ {-folder_id}
+ {-parent_id ""}
+ {-page_size 20}
+ {-page_number ""}
+ {-orderby ""}
+ {-use_package_path true}
+ {-extra_where_clause ""}
+ {-glob ""}
+ } {
+ if {$count} {
+ set attribute_selection "count(*)"
+ set orderby "" ;# no need to order when we count
+ set page_number "" ;# no pagination when count is used
+ } else {
+ set attribute_selection "i.name, r.title, p.page_id, r.publish_date, \
+ r.mime_type, i.parent_id, o.package_id, \
+ to_char(r.publish_date,'YYYY-MM-DD HH24:MI:SS') as formatted_date"
+ }
+ if {$page_number ne ""} {
+ set limit $page_size
+ set offset [expr {$page_size*($page_number-1)}]
+ } else {
+ set limit ""
+ set offset ""
+ }
+ set parent_id_clause [::xowiki::Includelet parent_id_clause \
+ -base_table i \
+ -use_package_path $use_package_path \
+ -parent_id $parent_id \
+ -base_package_id $package_id]
+
+ if {$glob ne ""} {
+ append extra_where_clause [::xowiki::Includelet glob_clause -base_table i $glob]
+ }
+
+ set sql [::xo::db::sql select \
+ -vars $attribute_selection \
+ -from "cr_items i, cr_revisions r, xowiki_page p, acs_objects o" \
+ -where "$parent_id_clause \
+ and r.revision_id = i.live_revision \
+ and i.item_id = o.object_id \
+ and p.page_id = r.revision_id \
+ and i.publish_status <> 'production' $extra_where_clause" \
+ -orderby $orderby \
+ -limit $limit -offset $offset]
+
+ if {$count} {
+ return [db_string [my qn count_listing] $sql]
+ } else {
+ set s [::xowiki::Page instantiate_objects -sql $sql]
+ return $s
+ }
+ }
+
+
+ #
+ # inherited methods for all includelets
+ #
+
+ ::xowiki::Includelet instproc resolve_page_name {page_name} {
+ return [[my set __including_page] resolve_included_page_name $page_name]
+ }
+
+ ::xowiki::Includelet instproc get_page_order {-source -ordered_pages -pages} {
+ my instvar page_order ordered_pages
+ #
+ # first check, if we can load the page_order from the page
+ # denoted by source
+ #
+ if {[info exists source]} {
+ set p [my resolve_page_name $source]
+ if {$p ne ""} {
+ array set ia [$p set instance_attributes]
+ if {[info exists ia(pages)]} {
+ set pages $ia(pages)
+ } elseif {[info exists ia(ordered_pages)]} {
+ set ordered_pages $ia(ordered_pages)
+ }
+ }
+ }
+
+ # compute a list of ordered_pages from pages, if necessary
+ if {[info exists ordered_pages]} {
+ foreach {order page} $ordered_pages {set page_order($page) $order}
+ } else {
+ set i 0
+ foreach page $pages {set page_order($page) [incr i]}
+ }
+ }
+
+ ::xowiki::Includelet instproc include_head_entries {} {
+ # The purpose of this method is to contain all calls to include
+ # CSS files, javascript, etc. in the HTML Head. This kind of
+ # requirements could as well be included e.g. in render, but this
+ # won't work, if "render" is cached. This method is called before
+ # render to be executed even when render is not due to caching.
+ # It is intended to be overloaded by subclasses.
+ }
+
+ ::xowiki::Includelet instproc initialize {} {
+ # This method is called at a time after init and before render.
+ # It can be used to alter specified parameter from the user,
+ # or to influence the rendering of a decoration (e.g. title etc.)
+ }
+
+ ::xowiki::Includelet instproc js_name {} {
+ return [[self class] js_name [self]]
+ }
+
+ ::xowiki::Includelet instproc screen_name {user_id} {
+ acs_user::get -user_id $user_id -array user
+ return [expr {$user(screen_name) ne "" ? $user(screen_name) : $user(name)}]
+ }
+}
+
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ ::xowiki::IncludeletClass create available-includelets \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {title "The following includelets can be used in a page"}
+ }
+
+ available-includelets instproc render {} {
+ my get_parameters
+ return [::xowiki::Includelet available_includelets]
+ }
+}
+
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ # Page Fragment Cache
+ #
+ # The following mixin-class implements page fragment caching in the
+ # xowiki-cache. Caching can be turned on for every
+ # ::xowiki::IncludeletClass instance.
+ #
+ # Fragment caching depends in the class variables
+ # - cacheable (the mixin is only registered, when cacheable is set to true)
+ # - aggregating (requires flusing when items are added/edited/deleted)
+ # - localized (dependency on locale)
+ # - personalized (dependency on userid)
+ #
+ Class create ::xowiki::includelet::page_fragment_cache -instproc render {} {
+ set c [my info class]
+ #
+ # Construct a key based on the class parameters and the
+ # actual parameters
+ #
+ set key "PF-[my package_id]-"
+ append key [expr {[$c aggregating] ? "agg" : "ind"}]
+ append key "-$c [my set __caller_parameters]"
+ if {[$c localized]} {append key -[my locale]}
+ if {[$c personalized]} {append key -[::xo::cc user_id]}
+ #
+ # Get the HTML from the rendered includelet by calling "next"
+ #
+ set HTML [ns_cache eval xowiki_cache $key next]
+ #
+ # Some side-effects might be necessary, even when the HTML output
+ # of the includelet is cached (e.g. some associative arrays,
+ # etc.). For this purpose, we provide here a means to cache
+ # additional some "includelet data", if the includelet provides
+ # it.
+ #
+ if {[catch {set data [ns_cache get xowiki_cache $key-data]}]} {
+ my cache_includelet_data $key-data
+ } else {
+ #my msg "eval $data"
+ eval $data
+ }
+ return $HTML
+ } -instproc cache_includelet_data {key} {
+ #my msg "data=[next]"
+ set data [next]
+ if {$data ne ""} {ns_cache set xowiki_cache $key $data}
+ }
+}
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ # dotlrn style includelet decoration for includelets
+ #
+ Class create ::xowiki::includelet::decoration=portlet -instproc render {} {
+ my instvar package_id name title
+ set class [namespace tail [my info class]]
+ set id [expr {[my exists id] ? "id='[my id]'" : ""}]
+ set html [next]
+ set localized_title [::xo::localize $title]
+ set link [expr {[string match "*:*" $name] ?
+ "$localized_title" :
+ $localized_title}]
+ ::xo::render_localizer
+ return [subst [[self class] set template]]
+ } -set template [expr {[apm_version_names_compare [ad_acs_version] 5.3.0] == 1 ?
+ {$my_tree_name $edit_html
"
+ } elseif {$edit_html ne ""} {
+ append content "$edit_html
"
+ }
+ set categories [list]
+ set pos 0
+ set cattree(0) [::xowiki::Tree new -volatile -orderby pos \
+ -id [my id]-$my_tree_name -name $my_tree_name]
+
+ set category_infos [::xowiki::Category get_category_infos \
+ -locale $locale -tree_id $tree_id]
+ foreach category_info $category_infos {
+ foreach {cid category_label deprecated_p level} $category_info {break}
+ set c [::xowiki::TreeNode new -orderby pos \
+ -level $level -label $category_label -pos [incr pos]]
+ set cattree($level) $c
+ set plevel [expr {$level -1}]
+ $cattree($plevel) add $c
+ set category($cid) $c
+ lappend categories $cid
+ }
+
+ if {[llength $categories] == 0} {
+ return $content
+ }
+
+ if {[info exists ordered_composite]} {
+ set items [list]
+ foreach c [$ordered_composite children] {lappend items [$c item_id]}
+
+ # If we have no item, provide a dummy one to avoid sql error
+ # later
+ if {[llength $items]<1} {set items -4711}
+
+ if {$count} {
+ set sql "category_object_map c
+ where c.object_id in ([join $items ,]) "
+ } else {
+ # TODO: the non-count-part for the ordered_composite is not
+ # tested yet. Although "ordered compostite" can be used
+ # only programmatically for now, the code below should be
+ # tested. It would be as well possible to obtain titles and
+ # names etc. from the ordered composite, resulting in a
+ # faster SQL like above.
+ set sql "category_object_map c, cr_items ci, cr_revisions r
+ where c.object_id in ([join $items ,])
+ and c.object_id = ci.item_id and
+ and r.revision_id = ci.live_revision
+ "
+ }
+ } else {
+ set sql "category_object_map c, cr_items ci, cr_revisions r, xowiki_page p \
+ where c.object_id = ci.item_id and ci.parent_id = $folder_id \
+ and ci.content_type not in ('::xowiki::PageTemplate') \
+ and c.category_id in ([join $categories ,]) \
+ and r.revision_id = ci.live_revision \
+ and p.page_id = r.revision_id \
+ and ci.publish_status <> 'production'"
+ }
+
+ if {$except_category_ids ne ""} {
+ append sql \
+ " and not exists (select * from category_object_map c2 \
+ where ci.item_id = c2.object_id \
+ and c2.category_id in ($except_category_ids))"
+ }
+ #ns_log notice "--c category_ids=$category_ids"
+ if {$category_ids ne ""} {
+ foreach cid [split $category_ids ,] {
+ append sql " and exists (select * from category_object_map \
+ where object_id = ci.item_id and category_id = $cid)"
+ }
+ }
+ append sql $locale_clause
+
+ if {$count} {
+ db_foreach [my qn get_counts] \
+ "select count(*) as nr,category_id from $sql group by category_id" {
+ $category($category_id) set count $nr
+ set s [expr {$summary ? "&summary=$summary" : ""}]
+ $category($category_id) href [ad_conn url]?category_id=$category_id$s
+ $category($category_id) open_tree
+ }
+ append content [$cattree(0) render -style [my set style]]
+ } else {
+ foreach {orderby direction} [split $order_items_by ,] break ;# e.g. "title,asc"
+ set increasing [expr {$direction ne "desc"}]
+ set order_column ", p.page_order"
+
+ db_foreach [my qn get_pages] \
+ "select ci.item_id, ci.name, ci.parent_id, r.title, category_id $order_column from $sql" {
+ if {$title eq ""} {set title $name}
+ set itemobj [Object new]
+ set prefix ""
+ set suffix ""
+ foreach var {name title prefix suffix page_order} {$itemobj set $var [set $var]}
+ $itemobj set href [::$package_id pretty_link -parent_id $parent_id $name]
+ $cattree(0) add_item \
+ -category $category($category_id) \
+ -itemobj $itemobj \
+ -orderby $orderby \
+ -increasing $increasing \
+ -open_item [expr {$item_id == $open_item_id}]
+ }
+ append content [$cattree(0) render -style [my set style]]
+ }
+ }
+ return $content
+ }
+}
+
+
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ #
+ # display recent entries by categories
+ # -gustaf neumann
+ #
+ # valid parameters from the include are
+ # tree_name: match pattern, if specified displays only the trees with matching names
+ # max_entries: show given number of new entries
+
+ ::xowiki::IncludeletClass create categories-recent \
+ -superclass ::xowiki::Includelet \
+ -cacheable true -personalized false -aggregating true \
+ -parameter {
+ {title "#xowiki.recently_changed_pages_by_categories#"}
+ {parameter_declaration {
+ {-max_entries:integer 10}
+ {-tree_name ""}
+ {-locale ""}
+ {-pretty_age "off"}
+ }}
+ }
+
+ categories-recent instproc initialize {} {
+ my set style sections
+ # When pretty age is activated, this includedlet is not suited for
+ # caching (it could make sense e.g. when the age granularity is 1
+ # minute or more). This measure here (turing off caching
+ # completely) is a little bit too much, but it is safe.
+ my get_parameters
+ if {[[my info class] cacheable] && $pretty_age ne "off"} {
+ [my info class] cacheable false
+ }
+ }
+
+ categories-recent instproc include_head_entries {} {
+ ::xowiki::Tree include_head_entries -renderer [my set style]
+ }
+
+ categories-recent instproc render {} {
+ my get_parameters
+
+ if {![my exists id]} {my set id [::xowiki::Includelet html_id [self]]}
+ set cattree [::xowiki::Tree new -volatile -id [my id]]
+
+ foreach {locale locale_clause} \
+ [::xowiki::Includelet locale_clause -revisions r -items ci $package_id $locale] break
+
+ set tree_ids [::xowiki::Category get_mapped_trees -object_id $package_id -locale $locale \
+ -names $tree_name -output tree_id]
+
+ if {$tree_ids ne ""} {
+ set tree_select_clause "and c.tree_id in ([join $tree_ids ,])"
+ } else {
+ set tree_select_clause ""
+ }
+ set sql [::xo::db::sql select \
+ -vars "c.category_id, ci.name, ci.parent_id, r.title, r.publish_date, \
+ to_char(r.publish_date,'YYYY-MM-DD HH24:MI:SS') as formatted_date" \
+ -from "category_object_map_tree c, cr_items ci, cr_revisions r, xowiki_page p" \
+ -where "c.object_id = ci.item_id and ci.parent_id = [$package_id folder_id] \
+ and r.revision_id = ci.live_revision \
+ and p.page_id = r.revision_id $tree_select_clause $locale_clause \
+ and ci.publish_status <> 'production'" \
+ -orderby "publish_date desc" \
+ -limit $max_entries]
+ db_foreach [my qn get_pages] $sql {
+ if {$title eq ""} {set title $name}
+ set itemobj [Object new]
+ set prefix ""
+ set suffix ""
+ switch -- $pretty_age {
+ 1 {set suffix " ([::xowiki::utility pretty_age -timestamp [clock scan $formatted_date] -locale [my locale]])"}
+ 2 {set suffix "([::xowiki::utility pretty_age -timestamp [clock scan $formatted_date] -locale [my locale] -levels 2])"}
+ default {set prefix "$formatted_date "}
+ }
+ if {$prefix ne ""} {set prefix "$prefix";$itemobj set encoded(prefix) 1}
+ if {$suffix ne ""} {set suffix "$suffix";$itemobj set encoded(suffix) 1}
+ foreach var {name title prefix suffix} {$itemobj set $var [set $var]}
+ $itemobj set href [::$package_id pretty_link -parent_id $parent_id $name]
+
+ if {![info exists categories($category_id)]} {
+ set categories($category_id) [::xowiki::TreeNode new \
+ -label [category::get_name $category_id $locale] \
+ -level 1]
+ $cattree add $categories($category_id)
+ }
+ $cattree add_item -category $categories($category_id) -itemobj $itemobj
+ }
+ return [$cattree render -style [my set style]]
+ }
+}
+
+
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ #
+ # display recent entries
+ #
+
+ ::xowiki::IncludeletClass create recent \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {title "#xowiki.recently_changed_pages#"}
+ {parameter_declaration {
+ {-max_entries:integer 10}
+ {-allow_edit:boolean false}
+ {-allow_delete:boolean false}
+ {-pretty_age off}
+ }}
+ }
+
+ recent instproc render {} {
+ my get_parameters
+ ::xo::Page requireCSS "/resources/acs-templating/lists.css"
+ set admin_p [::xo::cc permission -object_id $package_id -privilege admin \
+ -party_id [::xo::cc set untrusted_user_id]]
+ set show_heritage $admin_p
+
+ TableWidget t1 -volatile \
+ -set allow_edit $allow_edit \
+ -set allow_delete $allow_delete \
+ -set show_heritage $admin_p \
+ -columns {
+ Field date -label [_ xowiki.Page-last_modified]
+ if {[[my info parent] set allow_edit]} {
+ AnchorField edit -CSSclass edit-item-button -label "" -html {style "padding-right: 2px;"} -richtext 1
+ }
+ if {[[my info parent] set show_heritage]} {
+ AnchorField inherited -label "" -CSSclass inherited
+ }
+ AnchorField title -label [::xowiki::Page::slot::title set pretty_name]
+ if {[[my info parent] set allow_delete]} {
+ AnchorField delete -CSSclass delete-item-button -label "" -richtext 1
+ }
+ }
+
+ set listing [::xowiki::Includelet listing \
+ -package_id $package_id -page_number 1 -page_size $max_entries \
+ -orderby "publish_date desc"]
+
+ foreach entry [$listing children] {
+ $entry instvar parent_id formatted_date page_id {title entry_title} {name entry_name}
+ set entry_package_id [$entry set package_id]
+
+ set page_link [$entry_package_id pretty_link -parent_id $parent_id $entry_name]
+ switch -- $pretty_age {
+ 1 {set age [::xowiki::utility pretty_age -timestamp [clock scan $formatted_date] -locale [my locale]]}
+ 2 {set age [::xowiki::utility pretty_age -timestamp [clock scan $formatted_date] -locale [my locale] -levels 2]}
+ default {set age $formatted_date}
+ }
+
+ t1 add \
+ -title $entry_title \
+ -title.href $page_link \
+ -date $age
+
+ if {$allow_edit} {
+ set p [::xo::db::CrClass get_instance_from_db -item_id 0 -revision_id $page_id]
+ set edit_link [$entry_package_id make_link -link $page_link $p edit return_url]
+ #my log "page_link=$page_link, edit=$edit_link"
+ [t1 last_child] set edit.href $edit_link
+ [t1 last_child] set edit " "
+ }
+ if {$allow_delete} {
+ if {![info exists p]} {
+ set p [::xo::db::CrClass get_instance_from_db -item_id 0 -revision_id $page_id]
+ }
+ set delete_link [$entry_package_id make_link -link $page_link $p delete return_url]
+ [t1 last_child] set delete.href $delete_link
+ [t1 last_child] set delete " "
+ }
+ if {$show_heritage} {
+ if {$entry_package_id == [my package_id]} {
+ set href ""
+ set title ""
+ set alt ""
+ set class ""
+ set label ""
+ } else {
+ # provide a link to the original
+ set href $page_link
+ set label [$entry_package_id instance_name]
+ set title [_ xowiki.view_in_context [list context $label]]
+ set alt $title
+ set class "inherited"
+ }
+ [t1 last_child] set inherited $label
+ [t1 last_child] set inherited.href $href
+ [t1 last_child] set inherited.title $title
+ [t1 last_child] set inherited.CSSclass $class
+ }
+ }
+ return [t1 asHTML]
+ }
+}
+
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ #
+ # display last visited entries
+ #
+
+ ::xowiki::IncludeletClass create last-visited \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {title "#xowiki.last_visited_pages#"}
+ {parameter_declaration {
+ {-max_entries:integer 20}
+ }}
+ }
+
+ last-visited instproc render {} {
+ my get_parameters
+ ::xo::Page requireCSS "/resources/acs-templating/lists.css"
+
+ TableWidget t1 -volatile \
+ -columns {
+ AnchorField title -label [::xowiki::Page::slot::title set pretty_name]
+ }
+
+ db_foreach [my qn get_pages] \
+ [::xo::db::sql select \
+ -vars "i.parent_id, r.title,i.name, to_char(time,'YYYY-MM-DD HH24:MI:SS') as visited_date" \
+ -from "xowiki_last_visited x, xowiki_page p, cr_items i, cr_revisions r" \
+ -where "x.page_id = i.item_id and i.live_revision = p.page_id \
+ and r.revision_id = p.page_id and x.user_id = [::xo::cc set untrusted_user_id] \
+ and x.package_id = $package_id and i.publish_status <> 'production'" \
+ -orderby "visited_date desc" \
+ -limit $max_entries] \
+ {
+ t1 add \
+ -title $title \
+ -title.href [$package_id pretty_link -parent_id $parent_id $name]
+ }
+ return [t1 asHTML]
+ }
+}
+
+
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ #
+ # list the most popular pages
+ #
+
+ ::xowiki::IncludeletClass create most-popular \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {title "#xowiki.most_popular_pages#"}
+ {parameter_declaration {
+ {-max_entries:integer "10"}
+ {-interval}
+ }}
+ }
+
+ most-popular instproc render {} {
+ my get_parameters
+ ::xo::Page requireCSS "/resources/acs-templating/lists.css"
+
+ if {[info exists interval]} {
+ #
+ # If we have and interval, we cannot get report the number of visits
+ # for that interval, since we have only the aggregated values in
+ # the database.
+ #
+ my append title " in last $interval"
+
+ TableWidget t1 -volatile \
+ -columns {
+ AnchorField title -label [::xowiki::Page::slot::title set pretty_name]
+ Field users -label Visitors -html { align right }
+ }
+ set since_condition "and [::xo::db::sql since_interval_condition time $interval]"
+ db_foreach [my qn get_pages] \
+ [::xo::db::sql select \
+ -vars "count(x.user_id) as nr_different_users, x.page_id, r.title,i.name, i.parent_id" \
+ -from "xowiki_last_visited x, xowiki_page p, cr_items i, cr_revisions r" \
+ -where "x.package_id = $package_id and x.page_id = i.item_id and \
+ i.publish_status <> 'production' and i.live_revision = r.revision_id \
+ and $since_condition" \
+ -groupby "x.page_id, r.title, i.name, i.parent_id" \
+ -orderby "nr_different_users desc" \
+ -limit $max_entries ] {
+ t1 add \
+ -title $title \
+ -title.href [$package_id pretty_link -parent_id $parent_id $name] \
+ -users $nr_different_users
+ }
+ } else {
+
+ TableWidget t1 -volatile \
+ -columns {
+ AnchorField title -label [::xowiki::Page::slot::title set pretty_name]
+ Field count -label [_ xowiki.includelets-visits] -html { align right }
+ Field users -label [_ xowiki.includelet-visitors] -html { align right }
+ }
+ db_foreach [my qn get_pages] \
+ [::xo::db::sql select \
+ -vars "sum(x.count) as sum, count(x.user_id) as nr_different_users, x.page_id, r.title,i.name, i.parent_id" \
+ -from "xowiki_last_visited x, cr_items i, cr_revisions r" \
+ -where "x.package_id = $package_id and x.page_id = i.item_id and \
+ i.publish_status <> 'production' and i.live_revision = r.revision_id" \
+ -groupby "x.page_id, r.title, i.name, i.parent_id" \
+ -orderby "sum desc" \
+ -limit $max_entries] {
+ t1 add \
+ -title $title \
+ -title.href [$package_id pretty_link -parent_id $parent_id $name] \
+ -users $nr_different_users \
+ -count $sum
+ }
+ }
+ return [t1 asHTML]
+ }
+}
+
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ #
+ # list the most frequent visitors
+ #
+
+ ::xowiki::IncludeletClass create rss-client \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {title "#xowiki.rss_client#"}
+ {parameter_declaration {
+ {-url:required}
+ {-max_entries:integer "15"}
+ }}
+ }
+
+ rss-client instproc initialize {} {
+ my instvar feed
+ my get_parameters
+ my set feed [::xowiki::RSS-client new -url $url -destroy_on_cleanup]
+ if {[info command [$feed channel]] ne ""} {
+ my title [ [$feed channel] title]
+ }
+ }
+
+ rss-client instproc render {} {
+ my instvar feed
+ my get_parameters
+ if {[info command [$feed channel]] eq ""} {
+ set detail ""
+ if {[$feed exists errorMessage]} {set detail \n[$feed set errorMessage]}
+ return "No data available from $url
$detail"
+ } else {
+ set channel [$feed channel]
+ #set html "[$channel title]
"
+ set html "\n"
+ set i 0
+ foreach item [ $feed items ] {
+ append html "
\n"
+ return $html
+ }
+ }
+}
+
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ #
+ # list the most frequent visitors
+ #
+
+ ::xowiki::IncludeletClass create most-frequent-visitors \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {title "#xowiki.most_frequent_visitors#"}
+ {parameter_declaration {
+ {-max_entries:integer "15"}
+ }}
+ }
+
+ most-frequent-visitors instproc render {} {
+ my get_parameters
+ ::xo::Page requireCSS "/resources/acs-templating/lists.css"
+
+ TableWidget t1 -volatile \
+ -columns {
+ Field user -label Visitors -html { align right }
+ Field count -label Visits -html { align right }
+ }
+ db_foreach [my qn get_pages] \
+ [::xo::db::sql select \
+ -vars "sum(count) as sum, user_id" \
+ -from "xowiki_last_visited" \
+ -where "package_id = $package_id" \
+ -groupby "user_id" \
+ -orderby "sum desc" \
+ -limit $max_entries] {
+ t1 add \
+ -user [::xo::get_user_name $user_id] \
+ -count $sum
+ }
+ return [t1 asHTML]
+ }
+
+}
+
+
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ #
+ # Display unread items
+ #
+ # Currently moderately useful
+ #
+ # TODO: display of unread *revisions* should be included optionally, one has to
+ # consider what to do with auto-created stuff (put it into 'production' state?)
+ #
+
+ ::xowiki::IncludeletClass create unread-items \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {title "#xowiki.unread_items#"}
+ {parameter_declaration {
+ {-max_entries:integer 20}
+ }}
+ }
+
+ unread-items instproc render {} {
+ my get_parameters
+ ::xo::Page requireCSS "/resources/acs-templating/lists.css"
+
+ TableWidget t1 -volatile \
+ -columns {
+ AnchorField title -label [::xowiki::Page::slot::title set pretty_name]
+ }
+
+ set or_clause "or i.item_id in (
+ select x.page_id
+ from xowiki_last_visited x, acs_objects o \
+ where x.time < o.last_modified
+ and x.page_id = o.object_id
+ and x.package_id = $package_id
+ and x.user_id = [::xo::cc user_id]
+ )"
+
+ set or_clause ""
+
+ db_foreach [my qn get_pages] \
+ [::xo::db::sql select \
+ -vars "a.title, i.name, i.parent_id" \
+ -from "xowiki_page p, cr_items i, acs_objects a " \
+ -where "(i.item_id not in (
+ select x.page_id from xowiki_last_visited x
+ where x.user_id = [::xo::cc user_id] and x.package_id = $package_id
+ ) $or_clause
+ )
+ and i.live_revision = p.page_id
+ and i.parent_id = [$package_id folder_id]
+ and i.publish_status <> 'production'
+ and a.object_id = i.item_id" \
+ -orderby "a.creation_date desc" \
+ -limit $max_entries] \
+ {
+ t1 add \
+ -title $title \
+ -title.href [$package_id pretty_link -parent_id $parent_id $name]
+ }
+ return [t1 asHTML]
+ }
+}
+
+
+
+
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ #
+ # Show the tags
+ #
+
+ ::xowiki::IncludeletClass create tags \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {title "Tags"}
+ {parameter_declaration {
+ {-limit:integer 20}
+ {-summary:boolean 0}
+ {-popular:boolean 0}
+ {-page}
+ }}
+ }
+
+ tags instproc render {} {
+ my get_parameters
+ ::xo::Page requireCSS "/resources/acs-templating/lists.css"
+
+ if {$popular} {
+ set label [_ xowiki.popular_tags_label]
+ set tag_type ptag
+ set sql [::xo::db::sql select \
+ -vars "count(*) as nr,tag" \
+ -from xowiki_tags \
+ -where "package_id=$package_id" \
+ -groupby tag \
+ -orderby tag \
+ -limit $limit]
+ } else {
+ set label [_ xowiki.your_tags_label]
+ set tag_type tag
+ set sql "select count(*) as nr,tag from xowiki_tags where \
+ user_id=[::xo::cc user_id] and package_id=$package_id group by tag order by tag"
+ }
+ set entries [list]
+
+ if {![info exists page]} {set page [$package_id get_parameter weblog_page]}
+
+ set href [$package_id package_url]tag/
+ db_foreach [my qn get_counts] $sql {
+ set q [list]
+ if {$summary} {lappend q "summary=$summary"}
+ if {$popular} {lappend q "popular=$popular"}
+ set link $href$tag?[join $q &]
+ #lappend entries "$tag ($nr)"
+ lappend entries "$tag ($nr)"
+ }
+ return [expr {[llength $entries] > 0 ?
+ "
[$item description] #xowiki.weblog-more#\n"
+ if {[incr i] >= $max_entries} break
+ }
+ append html "$label
[join $entries {, }]
\n" :
+ ""}]
+ }
+
+ ::xowiki::IncludeletClass create my-tags \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {__decoration none}
+ {parameter_declaration {
+ {-summary 1}
+ }}
+ id
+ }
+
+ my-tags instproc render {} {
+ my get_parameters
+ my instvar __including_page tags
+ ::xo::Page requireJS "/resources/xowiki/get-http-object.js"
+
+ set p_link [$__including_page pretty_link]
+ set return_url "[::xo::cc url]?[::xo::cc actual_query]"
+ set weblog_page [$package_id get_parameter weblog_page weblog]
+ set save_tag_link [$package_id make_link -link $p_link $__including_page \
+ save-tags return_url]
+ set popular_tags_link [$package_id make_link -link $p_link $__including_page \
+ popular-tags return_url weblog_page]
+
+ set tags [lsort [::xowiki::Page get_tags -user_id [::xo::cc user_id] \
+ -item_id [$__including_page item_id] -package_id $package_id]]
+ set href [$package_id package_url]$weblog_page?summary=$summary&tag
+
+ set entries [list]
+
+ #foreach tag $tags {lappend entries "$tag"}
+ set href [$package_id package_url]/tag/
+ foreach tag $tags {lappend entries "$tag"}
+ set tags_with_links [join [lsort $entries] {, }]
+
+ if {![my exists id]} {my set id [::xowiki::Includelet html_id [self]]}
+ set content [subst -nobackslashes {
+ #xowiki.your_tags_label#: $tags_with_links
+ (#xowiki.edit_link#,
+ #xowiki.popular_tags_link#)
+
+
+ }]
+ return $content
+ }
+
+
+ ::xowiki::IncludeletClass create my-categories \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {__decoration none}
+ {parameter_declaration {
+ {-summary 1}
+ }}
+ }
+
+ my-categories instproc render {} {
+ my get_parameters
+ my instvar __including_page
+ set content ""
+
+ set weblog_page [$package_id get_parameter weblog_page weblog]
+ set entries [list]
+ set href [$package_id package_url]$weblog_page?summary=$summary
+ set notification_type ""
+ if {[$package_id get_parameter "with_notifications" 1] &&
+ [::xo::cc user_id] != 0} { ;# notifications require login
+ set notification_type [notification::type::get_type_id -short_name xowiki_notif]
+ }
+ if {[$package_id exists_query_parameter return_url]} {
+ set return_url [$package_id query_parameter return_url]
+ }
+ foreach cat_id [category::get_mapped_categories [$__including_page set item_id]] {
+ foreach {category_id category_name tree_id tree_name} [category::get_data $cat_id] break
+ #my log "--cat $cat_id $category_id $category_name $tree_id $tree_name"
+ set entry "$category_name ($tree_name)"
+ if {$notification_type ne ""} {
+ set notification_text "Subscribe category $category_name in tree $tree_name"
+ set notifications_return_url [expr {[info exists return_url] ? $return_url : [ad_return_url]}]
+ set notification_image \
+ ""
+
+ set cat_notif_link [export_vars -base /notifications/request-new \
+ {{return_url $notifications_return_url} \
+ {pretty_name $notification_text} \
+ {type_id $notification_type} \
+ {object_id $category_id}}]
+ append entry " " \
+ ""
+
+ }
+ lappend entries $entry
+ }
+ if {[llength $entries]>0} {
+ set content "#xowiki.categories#: [join $entries {, }]"
+ }
+ return $content
+ }
+
+ ::xowiki::IncludeletClass create my-general-comments \
+ -superclass ::xowiki::Includelet \
+ -parameter {{__decoration none}}
+
+ my-general-comments instproc render {} {
+ my get_parameters
+ my instvar __including_page
+ set item_id [$__including_page item_id]
+ set gc_return_url [$package_id url]
+ # Even, if general_comments is turned on, don't offer the
+ # link to add comments, unless the user is logged in.
+ # Otherwise, this attracts spammers and search bots
+ if {[::xo::cc user_id] != 0} {
+ set gc_link [general_comments_create_link \
+ -object_name [$__including_page title] \
+ $item_id $gc_return_url]
+ set gc_link $gc_comments
$gc_link"
+ } else {
+ return "$gc_link"
+ }
+ }
+
+ ::xowiki::IncludeletClass create digg \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {__decoration none}
+ {parameter_declaration {
+ {-description ""}
+ {-url}
+ }}
+ }
+
+ digg instproc render {} {
+ my get_parameters
+ my instvar __including_page
+ set digg_link [export_vars -base "http://digg.com/submit" {
+ {phase 2}
+ {url $url}
+ {title "[string range [$__including_page title] 0 74]"}
+ {body_text "[string range $description 0 349]"}
+ }]
+ regsub -all & $digg_link "&" digg_link
+ return ""
+ }
+
+ ::xowiki::IncludeletClass create delicious \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {__decoration none}
+ {parameter_declaration {
+ {-description ""}
+ {-tags ""}
+ {-url}
+ }}
+ }
+
+ delicious instproc render {} {
+ my get_parameters
+ my instvar __including_page
+
+ # the following opens a window, where a user can edit the posted info.
+ # however, it seems not possible to add tags this way automatically.
+ # Alternatively, one could use the api as descibed below; this allows
+ # tags, but no editing...
+ # http://farm.tucows.com/blog/_archives/2005/3/24/462869.html#adding
+
+ set delicious_link [export_vars -base "http://del.icio.us/post" {
+ {v 4}
+ {url $url}
+ {title "[string range [$__including_page title] 0 79]"}
+ {notes "[string range $description 0 199]"}
+ tags
+ }]
+ regsub -all & $delicious_link "&" delicious_link
+ return "del.icio.us"
+ }
+
+
+ ::xowiki::IncludeletClass create my-yahoo-publisher \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {__decoration none}
+ {parameter_declaration {
+ {-publisher ""}
+ {-rssurl}
+ }}
+ }
+
+ my-yahoo-publisher instproc render {} {
+ my get_parameters
+ my instvar __including_page
+
+ set publisher [ad_urlencode $publisher]
+ set feedname [ad_urlencode [$package_id get_parameter PackageTitle [$package_id instance_name]]]
+ set rssurl [ad_urlencode $rssurl]
+ set my_yahoo_link "http://us.rd.yahoo.com/my/atm/$publisher/$feedname/*http://add.my.yahoo.com/rss?url=$rssurl"
+
+ return ""
+ }
+
+
+ #
+ # my-references lists the pages which are refering to the
+ # including page
+ #
+ ::xowiki::IncludeletClass create my-references \
+ -superclass ::xowiki::Includelet \
+ -parameter {{__decoration none}}
+
+ my-references instproc render {} {
+ my get_parameters
+ my instvar __including_page
+
+ set item_id [$__including_page item_id]
+ set refs [list]
+ # The same image might be linked both, as img or file on one page,
+ # so we need DISTINCT.
+
+ db_foreach [my qn get_references] "SELECT DISTINCT page,ci.name,ci.parent_id,o.package_id as pid \
+ from xowiki_references,cr_items ci,acs_objects o \
+ where reference=$item_id and ci.item_id = page and ci.item_id = o.object_id" {
+ if {$pid eq ""} {
+ # in version less then oacs 5.2, this returns empty
+ set pid [db_string _ "select package_id from cr_folders where folder_id = :parent_id"]
+ }
+ if {$pid ne ""} {
+ ::xowiki::Package require $pid
+ lappend refs "$name"
+ }
+ }
+ set references [join $refs ", "]
+
+ array set lang {found "" undefined ""}
+ foreach i [$__including_page array names lang_links] {
+ set lang($i) [join [$__including_page set lang_links($i)] ", "]
+ }
+
+ append references " " $lang(found)
+ set result ""
+ if {$references ne " "} {
+ append result "#xowiki.references_label# $references"
+ }
+ if {$lang(undefined) ne ""} {
+ append result "#xowiki.create_this_page_in_language# $lang(undefined)"
+ }
+ return $result
+ }
+
+ #
+ # my-refers lists the pages which are refered to by the
+ # including page
+ #
+ ::xowiki::IncludeletClass create my-refers \
+ -superclass ::xowiki::Includelet \
+ -parameter {{__decoration none}}
+
+ my-refers instproc render {} {
+ my get_parameters
+ my instvar __including_page
+
+ set item_id [$__including_page item_id]
+ set refs [list]
+
+ db_foreach [my qn get_refers] "SELECT DISTINCT reference,ci.name,ci.parent_id,o.package_id as pid \
+ from xowiki_references,cr_items ci,acs_objects o \
+ where page=$item_id and ci.item_id = reference and ci.item_id = o.object_id" {
+ if {$pid eq ""} {
+ # in version less then oacs 5.2, this returns empty
+ set pid [db_string _ "select package_id from cr_folders where folder_id = :parent_id"]
+ }
+ if {$pid ne ""} {
+ ::xowiki::Package require $pid
+ lappend refs "$name"
+ }
+ }
+
+ set references [join $refs ", "]
+
+ array set lang {found "" undefined ""}
+ foreach i [$__including_page array names lang_links] {
+ set lang($i) [join [$__including_page set lang_links($i)] ", "]
+ }
+ append references " " $lang(found)
+ set result ""
+ if {$references ne " "} {
+ append result "#xowiki.references_of_label# $references"
+ }
+ if {$lang(undefined) ne ""} {
+ append result "#xowiki.create_this_page_in_language# $lang(undefined)"
+ }
+ return $result
+ }
+
+}
+
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ # presence
+ #
+ ::xowiki::IncludeletClass create presence \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {__decoration rightbox}
+ {parameter_declaration {
+ {-interval "10 minutes"}
+ {-max_users:integer 40}
+ {-show_anonymous "summary"}
+ {-page}
+ }}
+ }
+
+ # TODO make display style -decoration
+
+ presence instproc render {} {
+ my get_parameters
+
+ set summary 0
+ if {[::xo::cc user_id] == 0} {
+ switch -- $show_anonymous {
+ nothing {return ""}
+ all {set summary 0}
+ default {set summary 1}
+ }
+ }
+
+ if {[info exists page] && $page eq "this"} {
+ my instvar __including_page
+ set extra_where_clause "and page_id = [$__including_page item_id] "
+ set what " on page [$__including_page title]"
+ } else {
+ set extra_where_clause ""
+ set what " in community [$package_id instance_name]"
+ }
+
+ if {!$summary} {
+ set select_users "user_id, to_char(max(time),'YYYY-MM-DD HH24:MI:SS') as max_time from xowiki_last_visited "
+ }
+ set since_condition [::xo::db::sql since_interval_condition time $interval]
+ set where_clause "package_id=$package_id and $since_condition $extra_where_clause"
+ set when "
in last $interval"
+
+ set output ""
+
+ if {$summary} {
+ set count [db_string [my qn presence_count_users] \
+ "select count(distinct user_id) from xowiki_last_visited WHERE $where_clause"]
+ } else {
+ set values [db_list_of_lists [my qn get_users] \
+ [::xo::db::sql select \
+ -vars "user_id, to_char(max(time),'YYYY-MM-DD HH24:MI:SS') as max_time" \
+ -from xowiki_last_visited \
+ -where $where_clause \
+ -groupby user_id \
+ -orderby "max_time desc" \
+ -limit $max_users ]]
+ set count [llength $values]
+ if {$count == $max_users} {
+ # we have to check, whether there were more users...
+ set count [db_string [my qn presence_count_users] \
+ "select count(distinct user_id) from xowiki_last_visited WHERE $where_clause"]
+ }
+ foreach value $values {
+ foreach {user_id time} $value break
+ set seen($user_id) $time
+
+ regexp {^([^.]+)[.]} $time _ time
+ set pretty_time [util::age_pretty -timestamp_ansi $time \
+ -sysdate_ansi [clock_to_ansi [clock seconds]] \
+ -mode_3_fmt "%d %b %Y, at %X"]
+ set name [::xo::get_user_name $user_id]
+
+ append output " \n"
+ }
+ if {$output ne ""} {set output "$name $pretty_time $output
\n"}
+ }
+ set users [expr {$count == 0 ? "No registered users" :
+ $count == 1 ? "1 registered user" :
+ "$count registered users"}]
+ return "\n"
+ }
+ PageReorderSupport instproc page_reorder_item_id {-ID -prefix_js -page_order js_} {
+ my upvar $js_ js
+ set key __count($prefix_js)
+ if {[my exists $key]} {set p [my incr $key]} {set p [my set $key 0]}
+ set id ${ID}_${prefix_js}_$p
+ append js "YAHOO.xo_page_order_region.DDApp.cd\['$id'\] = '$page_order';\n"
+ return $id
+ }
+
+ #
+ # toc -- Table of contents
+ #
+ ::xowiki::IncludeletClass create toc \
+ -superclass ::xowiki::Includelet \
+ -instmixin PageReorderSupport \
+ -cacheable false -personalized false -aggregating true \
+ -parameter {
+ {__decoration plain}
+ {parameter_declaration {
+ {-style ""}
+ {-open_page ""}
+ {-book_mode false}
+ {-folder_mode false}
+ {-ajax false}
+ {-expand_all false}
+ {-remove_levels 0}
+ {-category_id}
+ {-locale ""}
+ {-source ""}
+ {-range ""}
+ {-allow_reorder ""}
+ }}
+ id
+ }
+
+#"select page_id, page_order, name, title, \
+# (select count(*)-1 from xowiki_page_live_revision where page_order <@ p.page_order) as count \
+# from xowiki_page_live_revision p where not page_order is NULL order by page_order asc"
+
+ toc instproc count {} {return [my set navigation(count)]}
+ toc instproc current {} {return [my set navigation(current)]}
+ toc instproc position {} {return [my set navigation(position)]}
+ toc instproc page_name {p} {return [my set page_name($p)]}
+ toc instproc cache_includelet_data {key} {
+ append data \
+ [list my array set navigation [my array get navigation]] \n \
+ [list my array set page_name [my array get page_name]] \n
+ return $data
+ }
+
+ toc proc anchor {name} {
+ # try to strip the language prefix from the name
+ regexp {^.*:([^:]+)$} $name _ name
+ # anchor is used between single quotes
+ regsub -all ' $name {\'} anchor
+ return $anchor
+ }
+
+ toc instproc build_toc {package_id locale source range} {
+ my get_parameters
+ my instvar navigation __including_page
+ array set navigation {parent "" position 0 current ""}
+
+ set extra_where_clause ""
+ if {[my exists category_id]} {
+ foreach {cnames extra_where_clause} [my category_clause [my set category_id]] break
+ }
+ foreach {locale locale_clause} \
+ [::xowiki::Includelet locale_clause -revisions p -items p $package_id $locale] break
+ #my msg locale_clause=$locale_clause
+
+ if {$source ne ""} {
+ my get_page_order -source $source
+ set page_names ('[join [my array names page_order] ',']')
+ set page_order_clause "and name in $page_names"
+ set page_order_att ""
+ } else {
+ set page_order_clause "and not page_order is NULL"
+ set page_order_att "page_order,"
+ }
+
+ if {$folder_mode} {
+ # TODO just needed for michael aram?
+ set parent_id [$__including_page item_id]
+ } else {
+ #set parent_id [$package_id folder_id]
+ set parent_id [$__including_page parent_id]
+ }
+
+ set sql [::xo::db::sql select \
+ -vars "page_id, $page_order_att name, title" \
+ -from "xowiki_page_live_revision p" \
+ -where "parent_id=$parent_id \
+ $page_order_clause \
+ $extra_where_clause $locale_clause"]
+ set pages [::xowiki::Page instantiate_objects -sql $sql]
+
+ $pages mixin add ::xo::OrderedComposite::IndexCompare
+ if {$range ne "" && $page_order_att ne ""} {
+ foreach {from to} [split $range -] break
+ foreach p [$pages children] {
+ if {[$pages __value_compare [$p set page_order] $from 0] == -1
+ || [$pages __value_compare [$p set page_order] $to 0] > 0} {
+ $pages delete $p
+ }
+ }
+ }
+
+ $pages orderby page_order
+ if {$source ne ""} {
+ # add the page_order to the objects
+ foreach p [$pages children] {
+ $p set page_order [my set page_order([$p set name])]
+ }
+ }
+
+ return $pages
+ }
+
+ toc instproc href {book_mode name} {
+ my instvar package_id __including_page
+ if {$book_mode} {
+ set href [$package_id url]#[toc anchor $name]
+ } else {
+ set href [$package_id pretty_link -parent_id [$__including_page parent_id] $name]
+ }
+ return $href
+ }
+
+ toc instproc page_number {page_order remove_levels} {
+ #my log "o: $page_order"
+ set displayed_page_order $page_order
+ for {set i 0} {$i < $remove_levels} {incr i} {
+ regsub {^[^.]+[.]} $displayed_page_order "" displayed_page_order
+ }
+ return $displayed_page_order
+ }
+
+ toc instproc build_navigation {pages} {
+ #
+ # compute associative arrays open_node and navigation (position
+ # and current)
+ #
+ my get_parameters
+ my instvar navigation page_name
+ array set navigation {position 0 current ""}
+
+ # the top node is always open
+ my set open_node() true
+ set node_cnt 0
+ foreach o [$pages children] {
+ $o instvar page_order name
+ incr node_cnt
+ set page_name($node_cnt) $name
+ if {![regexp {^(.*)[.]([^.]+)} $page_order _ parent]} {set parent ""}
+ #
+ # If we are on the provided $open_page, we remember our position
+ # for the progress bar.
+ set on_current_node [expr {$open_page eq $name} ? "true" : "false"]
+ if {$on_current_node} {
+ set navigation(position) $node_cnt
+ set navigation(current) $page_order
+ }
+ if {$expand_all} {
+ my set open_node($page_order) true
+ } elseif {$on_current_node} {
+ my set open_node($page_order) true
+ # make sure to open all nodes to the root
+ for {set p $parent} {$p ne ""} {} {
+ my set open_node($p) true
+ if {![regexp {^(.*)[.]([^.]+)} $p _ p]} {set p ""}
+ }
+ }
+ }
+ set navigation(count) $node_cnt
+ #my log OPEN=[lsort [my array names open_node]]
+ }
+
+ #
+ # ajax based code for fade-in / fade-out
+ #
+ toc instproc yui_ajax {} {
+ return "var [my js_name] = {
+
+ count: [my set navigation(count)],
+
+ getPage: function(href, c) {
+ //console.log('getPage: ' + href + ' type: ' + typeof href) ;
+
+ if ( typeof c == 'undefined' ) {
+
+ // no c given, search it from the objects
+ // console.log('search for href <' + href + '>');
+
+ for (i in this.objs) {
+ if (this.objs\[i\].ref == href) {
+ c = this.objs\[i\].c;
+ // console.log('found href ' + href + ' c=' + c);
+ var node = this.tree.getNodeByIndex(c);
+ if (!node.expanded) {node.expand();}
+ node = node.parent;
+ while (node.index > 1) {
+ if (!node.expanded) {node.expand();}
+ node = node.parent;
+ }
+ break;
+ }
+ }
+ if (typeof c == 'undefined') {
+ // console.warn('c undefined');
+ return false;
+ }
+ }
+ //console.log('have href ' + href + ' c=' + c);
+
+ var transaction = YAHOO.util.Connect.asyncRequest('GET', \
+ href + '?template_file=view-page&return_url=' + href,
+ {
+ success:function(o) {
+ var bookpage = document.getElementById('book-page');
+ var fadeOutAnim = new YAHOO.util.Anim(bookpage, { opacity: {to: 0} }, 0.5 );
+
+ var doFadeIn = function(type, args) {
+ // console.log('fadein starts');
+ var bookpage = document.getElementById('book-page');
+ bookpage.innerHTML = o.responseText;
+ var fadeInAnim = new YAHOO.util.Anim(bookpage, { opacity: {to: 1} }, 0.1 );
+ fadeInAnim.animate();
+ }
+
+ // console.log(' tree: ' + this.tree + ' count: ' + this.count);
+ // console.info(this);
+
+ if (this.count > 0) {
+ var percent = (100 * o.argument.count / this.count).toFixed(2) + '%';
+ } else {
+ var percent = '0.00%';
+ }
+
+ if (o.argument.count > 1) {
+ var link = o.argument.href;
+ var src = '/resources/xowiki/previous.png';
+ var onclick = 'return [my js_name].getPage(\"' + link + '\");' ;
+ } else {
+ var link = '#';
+ var onclick = '';
+ var src = '/resources/xowiki/previous-end.png';
+ }
+
+ // console.log('changing prev href to ' + link);
+ // console.log('changing prev onclick to ' + onclick);
+
+ document.getElementById('bookNavPrev.img').src = src;
+ document.getElementById('bookNavPrev.a').href = link;
+ document.getElementById('bookNavPrev.a').setAttribute('onclick',onclick);
+
+ if (o.argument.count < this.count) {
+ var link = o.argument.href;
+ var src = '/resources/xowiki/next.png';
+ var onclick = 'return [my js_name].getPage(\"' + link + '\");' ;
+ } else {
+ var link = '#';
+ var onclick = '';
+ var src = '/resources/xowiki/next-end.png';
+ }
+
+ // console.log('changing next href to ' + link);
+ // console.log('changing next onclick to ' + onclick);
+ document.getElementById('bookNavNext.img').src = src;
+ document.getElementById('bookNavNext.a').href = link;
+
+ document.getElementById('bookNavNext.a').setAttribute('onclick',onclick);
+ document.getElementById('bookNavRelPosText').innerHTML = percent;
+ //document.getElementById('bookNavBar').setAttribute('style', 'width: ' + percent + ';');
+ document.getElementById('bookNavBar').style.width = percent;
+
+ fadeOutAnim.onComplete.subscribe(doFadeIn);
+ fadeOutAnim.animate();
+ },
+ failure:function(o) {
+ // console.error(o);
+ // alert('failure ');
+ return false;
+ },
+ argument: {count: c, href: href},
+ scope: [my js_name]
+ }, null);
+
+ return false;
+ },
+
+ treeInit: function() {
+ [my js_name].tree = new YAHOO.widget.TreeView('[my id]');
+ [my js_name].tree.subscribe('clickEvent', function(oArgs) {
+ var m = /href=\"(\[^\"\]+)\"/.exec(oArgs.node.html);
+ [my js_name].getPage( m\[1\], oArgs.node.index);
+ });
+ [my js_name].tree.draw();
+ }
+
+ };
+
+ YAHOO.util.Event.addListener(window, 'load', [my js_name].treeInit);
+"
+ }
+
+ toc instproc yui_non_ajax {} {
+ return "
+ var [my js_name];
+ YAHOO.util.Event.onDOMReady(function() {
+ [my js_name] = new YAHOO.widget.TreeView('[my id]');
+ [my js_name].subscribe('clickEvent',function(oArgs) {
+ //console.info(oArgs);
+ var m = /href=\"(\[^\"\]+)\"/.exec(oArgs.node.html);
+ //console.info(m\[1\]);
+ //window.location.href = m\[1\];
+ return false;
+ });
+ [my js_name].render();
+ });
+ "
+ }
+
+ toc instproc render_yui_list {{-full false} pages} {
+ my instvar js
+ my get_parameters
+ my instvar navigation page_name
+
+ #
+ # Render the tree with the yui widget (with or without ajax)
+ #
+ if {$book_mode} {
+ #my log "--warn: cannot use bookmode with ajax, resetting ajax"
+ set ajax 0
+ }
+ my set ajax $ajax
+
+ if {$ajax} {
+ set js [my yui_ajax]
+ } else {
+ set js [my yui_non_ajax]
+ }
+
+ set tree [::xowiki::Tree new -destroy_on_cleanup -orderby pos -id [my id]]
+ $tree array set open_node [my array get open_node]
+ $tree add_pages -full $full -remove_levels $remove_levels \
+ -book_mode $book_mode -open_page $open_page -expand_all $expand_all \
+ -owner [self] \
+ $pages
+
+ set HTML [$tree render -style yuitree -js $js]
+ return $HTML
+ }
+
+ toc instproc parent_id {} {
+ [my set __including_page] parent_id
+ }
+
+ toc instproc render_list {{-full false} pages} {
+ my get_parameters
+
+ #
+ # Build a reduced toc tree based on pure HTML (no javascript or
+ # ajax involved). If an open_page is specified, produce an as
+ # small as possible tree and omit all non-visible nodes.
+ #
+ if {$open_page ne ""} {
+ # TODO: can we allow open_page and reorder?
+ set allow_reorder ""
+ } else {
+ set allow_reorder [my page_reorder_check_allow -with_head_entries false $allow_reorder]
+ }
+ set tree [::xowiki::Tree new -destroy_on_cleanup -orderby pos -id [my id]]
+ $tree array set open_node [my array get open_node]
+ $tree add_pages -full $full -remove_levels $remove_levels \
+ -book_mode $book_mode -open_page $open_page -expand_all $expand_all \
+ -owner [self] \
+ $pages
+
+ if {$allow_reorder ne ""} {
+ my page_reorder_init_vars -allow_reorder $allow_reorder js last_level ID min_level
+ set js "\nYAHOO.xo_page_order_region.DDApp.package_url = '[$package_id package_url]';"
+ set HTML [$tree render -style listdnd -js $js -context [list min_level $min_level]]
+ } else {
+ set HTML [$tree render -style list]
+ }
+
+ return $HTML
+ }
+
+ # TODO: maybe we could generalize this and similar convenience
+ # methods on the includelet root class.
+ toc instproc parent_id {} {
+ [my set __including_page] parent_id
+ }
+
+ toc instproc include_head_entries {} {
+ my instvar style renderer
+ switch -- $renderer {
+ yuitree {::xowiki::Tree include_head_entries -renderer yuitree -style $style}
+ list {
+ my get_parameters
+ set tree_renderer [expr {$allow_reorder eq "" ? "list" : "listdnd"}]
+ ::xowiki::Tree include_head_entries -renderer $tree_renderer -style $style
+ }
+ none {}
+ }
+ }
+
+ toc instproc initialize {} {
+ my get_parameters
+
+ set list_mode 0
+ switch -- $style {
+ "menu" {set s "menu/"; set renderer yuitree}
+ "folders" {set s "folders/"; set renderer yuitree}
+ "list" {set s ""; set list_mode 1; set renderer list}
+ "none" {set s ""; set renderer none}
+ "default" {set s ""; set renderer yuitree}
+ }
+ my set renderer $renderer
+ my set style $s
+ my set list_mode $list_mode
+ my set book_mode $book_mode
+ }
+
+ toc instproc render {} {
+ my get_parameters
+
+ if {![my exists id]} {my set id [::xowiki::Includelet html_id [self]]}
+ if {[info exists category_id]} {my set category_id $category_id}
+
+ #
+ # Collect the pages
+ #
+ set pages [my build_toc $package_id $locale $source $range]
+ #
+ # Build the general navigation structure using associative arrays
+ #
+ my build_navigation $pages
+ #
+ # Call a render on the created structure
+ #
+ if {[info command ::__xowiki__MenuBar] ne ""} {
+ ::__xowiki__MenuBar additional_sub_menu -kind folder -pages $pages -owner [self]
+ }
+ #
+ # TODO: We should call here the appropriate tree-renderer instead
+ # of the toc-specific renderers, but first we have to check, if
+ # these are fully feature-compatible.
+ #
+ if {[my set renderer] eq "none"} {
+ } elseif {[my set list_mode]} {
+ return [my render_list $pages]
+ } else {
+ return [my render_yui_list -full true $pages]
+ }
+ }
+
+ #############################################################################
+ # Selection
+ #
+ # TODO: base book (and toc) on selection
+ ::xowiki::IncludeletClass create selection \
+ -superclass ::xowiki::Includelet \
+ -instmixin PageReorderSupport \
+ -parameter {
+ {__decoration plain}
+ {parameter_declaration {
+ {-edit_links:boolean true}
+ {-pages ""}
+ {-ordered_pages ""}
+ {-source}
+ {-menu_buttons edit}
+ {-range ""}
+ }}
+ }
+
+ selection instproc render {} {
+ my instvar page_order
+ my get_parameters
+ my set package_id $package_id
+ my set edit_links $edit_links
+
+ if {[info exists source]} {
+ my get_page_order -source $source
+ } else {
+ my get_page_order -pages $pages -ordered_pages $ordered_pages
+ }
+
+ # should check for quotes in names
+ set page_names ('[join [array names page_order] ',']')
+ set pages [::xowiki::Page instantiate_objects -sql \
+ "select page_id, name, title, item_id \
+ from xowiki_page_live_revision p \
+ where parent_id = [$package_id folder_id] \
+ and name in $page_names \
+ [::xowiki::Page container_already_rendered item_id]" ]
+ foreach p [$pages children] {
+ $p set page_order $page_order([$p set name])
+ }
+
+ $pages mixin add ::xo::OrderedComposite::IndexCompare
+ if {$range ne ""} {
+ foreach {from to} [split $range -] break
+ foreach p [$pages children] {
+ if {[$pages __value_compare [$p set page_order] $from 0] == -1
+ || [$pages __value_compare [$p set page_order] $to 0] > 0} {
+ $pages delete $p
+ }
+ }
+ }
+
+ $pages orderby page_order
+ return [my render_children $pages $menu_buttons]
+ }
+
+ selection instproc render_children {pages menu_buttons} {
+ my instvar package_id edit_links
+ foreach o [$pages children] {
+ $o instvar page_order title page_id name title
+ set level [expr {[regsub {[.]} $page_order . page_order] + 1}]
+ set edit_markup ""
+ set p [::xo::db::CrClass get_instance_from_db -item_id 0 -revision_id $page_id]
+ $p set unresolved_references 0
+
+ switch [$p info class] {
+ ::xowiki::Form {
+ set content [$p render]
+ }
+ default {
+ set content [$p render -with_footer false]
+ set content [string map [list "\{\{" "\\\{\{"] $content]
+ }
+ }
+
+ set menu [list]
+ foreach b $menu_buttons {
+ if {[info command ::xowiki::includelet::$b] eq ""} {
+ set b $b-item-button
+ }
+ set html [$p include [list $b -book_mode true]]
+ if {$html ne ""} {lappend menu $html}
+ }
+ append output "
doc
+ $doc documentElement root
+
+ set fields [$root selectNodes "//div\[@class = 'wiki-menu'\]"]
+ foreach field $fields {$field delete}
+
+ set inner_html [$root asHTML]
+ set id ID[$__including_page item_id]
+ set base [$__including_page pretty_link]
+ #set id ID$item_id
+ #$root setAttribute id $id
+ set as_att_value [::xowiki::Includelet html_encode $inner_html]
+ set save_form [subst {
+
+
+ }]
+
+ return $inner_html$save_form
+ }
+}
+
+namespace eval ::xowiki::includelet {
+ #############################################################################
+ # book style
+ #
+ ::xowiki::IncludeletClass create book \
+ -superclass ::xowiki::Includelet \
+ -instmixin PageReorderSupport \
+ -parameter {
+ {__decoration plain}
+ {parameter_declaration {
+ {-category_id}
+ {-menu_buttons edit}
+ {-folder_mode false}
+ {-locale ""}
+ {-range ""}
+ {-allow_reorder ""}
+ {-with_footer "false"}
+ }}
+ }
+
+
+ book instproc render_item {
+ -menu_buttons
+ -content:required
+ -object:required
+ -level:required
+ } {
+ $object instvar page_order title name
+ set menu [list]
+ foreach b $menu_buttons {
+ if {[info command ::xowiki::includelet::$b] eq ""} {
+ set b $b-item-button
+ }
+ set html [$object include [list $b -book_mode true]]
+ if {$html ne ""} {lappend menu $html}
+ }
+ set menu [join $menu " "]
+ if {$menu ne ""} {
+ # \n" \
+
\n" \
+
[set cutoff] - [set c]
+
+ return [subst -novariables {
+
\n"
+ }
+ set items [::xowiki::FormPage get_form_entries \
+ -base_item_ids $form_item_ids -form_fields "" \
+ -always_queried_attributes "*" -initialize false \
+ -publish_status all -package_id $package_id]
+
+ set sum 0
+ foreach i [$items children] {
+ set value ""
+ if {[string match _* $property]} {
+ set varname [string range $property 1 end]
+ if {[$i exists $varname]} {set value [$i set $varname]}
+ } else {
+ array set __ia [$i set instance_attributes]
+ set varname __ia($property)
+ if {[info exists $varname]} {set value [set $varname]}
+ }
+ if {[info exists __count($value)]} {incr __count($value)} else {set __count($value) 1}
+ incr sum 1
+ }
+
+ if {$sum == 0} {
+ return "no data
\n"
+ }
+
+ if {$renderer eq "highcharts"} {
+ #
+ # experimental highcharts pie renderer
+ #
+ set percentages [list]
+ foreach {value count} [array get __count] {
+ lappend percentages $value [format %.2f [expr {$count*100.0/$sum}]]
+ }
+ set h [highcharts new -volatile -id [my js_name] \
+ -title [::xowiki::Includelet js_encode \
+ "$sum Answers for Survey '[$form_item_ids title]'"]]
+ return [$h pie [list value count] $percentages]
+
+ } else {
+ #
+ # standard table encoder
+ #
+ TableWidget t1 -volatile \
+ -columns {
+ Field value -orderby value -label value
+ Field count -orderby count -label count
+ }
+
+ foreach {att order} [split $orderby ,] break
+ t1 orderby -order [expr {$order eq "asc" ? "increasing" : "decreasing"}] $att
+ foreach {value count} [array get __count] {
+ t1 add -value $value -count $count
+ }
+ return [t1 asHTML]
+ }
+ }
+
+ #
+ # To use highcharts, download it from http://www.highcharts.com/
+ # and install it under the directory xowiki/www/resources/highcharts
+ # (you have to create the directory and unpack the zip file there).
+ #
+ ::xotcl::Class highcharts -parameter {title id}
+ highcharts instproc pie {names data} {
+ ::xo::Page requireJS "/resources/xowiki/jquery/jquery.min.js"
+ ::xo::Page requireJS "/resources/xowiki/highcharts/js/highcharts.js"
+ ::xo::Page requireJS "/resources/xowiki/highcharts/js/themes/gray.js"
+ set result "\n"
+ set title [my title]
+ set id [my id]
+ set values [list]
+ foreach {name value} $data {
+ lappend values "\['[::xowiki::Includelet js_encode $name]', $value\]"
+ }
+ set values [join $values ",\n"]
+ append result [subst -nocommands {
+
+}]
+ return $result
+ }
+
+
+ #############################################################################
+ ::xowiki::IncludeletClass create form-usages \
+ -superclass ::xowiki::Includelet \
+ -parameter {
+ {__decoration plain}
+ {parameter_declaration {
+ {-form_item_id:integer}
+ {-form}
+ {-parent_id}
+ {-package_ids ""}
+ {-orderby "_last_modified,desc"}
+ {-view_field _name}
+ {-publish_status "all"}
+ {-field_names}
+ {-hidden_field_names ""}
+ {-extra_form_constraints ""}
+ {-inherit_from_forms ""}
+ {-category_id}
+ {-unless}
+ {-where}
+ {-csv true}
+ {-voting_form}
+ {-voting_form_form ""}
+ {-voting_form_anon_instances "t"}
+ {-generate}
+ {-with_form_link true}
+ {-with_categories}
+ {-wf}
+ {-buttons "edit delete"}
+ {-renderer ""}
+ }}
+ }
+
+# {-renderer "YUIDataTableRenderer"}
+ form-usages instproc render {} {
+ my get_parameters
+
+ my instvar __including_page
+ set o $__including_page
+ ::xo::Page requireCSS "/resources/acs-templating/lists.css"
+ set return_url [::xo::cc url]?[::xo::cc actual_query]
+
+ if {[info exists parent_id]} {
+ if {$parent_id eq "self"} {
+ set parent_id [$__including_page item_id]
+ }
+ } else {
+ set parent_id [$o parent_id]
+ }
+
+ if {![info exists form_item_id]} {
+ # Start for search for form in the directory of the including
+ # form. The provided package_id and parent_id refers to the
+ # form instances, not to the forms.
+ set form_item_ids [::xowiki::Weblog instantiate_forms -parent_id $parent_id \
+ -parent_id [$o parent_id] \
+ -default_lang [$o lang] \
+ -forms $form -package_id [$o package_id]]
+ } else {
+ set form_item_ids [list $form_item_id]
+ }
+
+ set form_constraints $extra_form_constraints\n
+
+ if {$inherit_from_forms ne ""} {
+ foreach inherit_form $inherit_from_forms {
+ set inherit_form_id [::xowiki::Weblog instantiate_forms \
+ -parent_id [$o parent_id] \
+ -default_lang [$o lang] \
+ -forms $inherit_form -package_id [$o package_id]]
+ if {$inherit_form_id ne ""} {
+ set p [$inherit_form_id property form_constraints]
+ append form_constraints $p\n
+ }
+ }
+ }
+
+ foreach form_item $form_item_ids {
+ append form_constraints [$form_item get_form_constraints -trylocal true] \n
+ }
+ #my msg fc=$form_constraints
+
+ #
+ # The internal variables (instance attributes, etc) are prefixed
+ # with an underscore. Therefore, we prefix here "orderby" as
+ # well. For the provided table properties, prefixing happens in
+ # the loop below.
+ #
+ set orderby _$orderby
+
+ # load table properties; order_by won't work due to comma, but solve that later (TODO)
+ set table_properties [::xowiki::PageInstance get_list_from_form_constraints \
+ -name @table_properties \
+ -form_constraints $form_constraints]
+ foreach {attr value} $table_properties {
+ switch $attr {
+ orderby {set $attr _[::xowiki::formfield::FormField fc_decode $value]}
+ buttons - publish_status - category_id - unless -
+ where - with_categories - with_form_link - csv - view_field -
+ voting_form - voting_form_form - voting_form_anon_instances {
+ set $attr $value
+ #my msg " set $attr $value"
+ }
+ default {error "unknown table property '$attr' provided"}
+ }
+ }
+
+ if {![info exists field_names]} {
+ set fn [::xowiki::PageInstance get_short_spec_from_form_constraints \
+ -name @table \
+ -form_constraints $form_constraints]
+ set raw_field_names [split $fn ,]
+ } elseif {[string match "*,*" $field_names] } {
+ set raw_field_names [split $field_names ,]
+ } else {
+ set raw_field_names $field_names
+ }
+
+ foreach fn $hidden_field_names {
+ set __hidden($fn) 1
+ lappend raw_field_names $fn
+ }
+
+ if {$raw_field_names eq ""} {
+ set raw_field_names {_name _last_modified _creation_user}
+ }
+
+ # finally, evaluate conditions if included
+ set field_names [list]
+ foreach f $raw_field_names {
+ set _ [string trim [::xowiki::formfield::FormField get_single_spec \
+ -object $o -package_id $package_id $f]]
+ if {$_ ne ""} {lappend field_names $_}
+ }
+
+ foreach form_item $form_item_ids {
+ set form_fields [::xowiki::FormPage get_table_form_fields \
+ -base_item $form_item \
+ -field_names $field_names \
+ -form_constraints $form_constraints]
+ #$form_item show_fields $form_fields
+ foreach f $form_fields {set __ff([$f name]) $f}
+ }
+ # if {[info exists __ff(_creation_user)]} {$__ff(_creation_user) label "By User"}
+
+ # TODO: wiki-substitution is just foced in here. Maybe it makes
+ # more sense to use it as a default for _text, but we have to
+ # check all the nested cases to avoid double-substitutions.
+ if {[info exists __ff(_text)]} {$__ff(_text) set wiki 1}
+
+ foreach b $buttons {set use_button($b) 1}
+
+ set cols ""
+ if {[info exists use_button(edit)]} {
+ append cols {AnchorField _edit -CSSclass edit-item-button -label "" \
+ -html {style "padding: 2px;"} -no_csv 1 -richtext 1} \n
+ }
+ if {[info exists use_button(view)]} {
+ append cols {AnchorField _view -CSSclass view-item-button -label "" \
+ -html {style "padding: 2px;"} -no_csv 1 -richtext 1} \n
+ }
+ foreach fn $field_names {
+ if {[info exists __hidden($fn)]} continue
+ append cols [list AnchorField _$fn \
+ -label [$__ff($fn) label] \
+ -richtext 1 \
+ -orderby _$fn \
+ ] \n
+ }
+ if {[info exists use_button(delete)]} {
+ #append cols [list ImageField_DeleteIcon _delete -label "" -no_csv 1] \n
+ append cols [list AnchorField _delete -CSSclass delete-item-button -label "" -no_csv 1 -richtext 1] \n
+ }
+
+ set cmd [list TableWidget t1 -volatile -columns $cols]
+ if {$renderer ne ""} {
+ lappend cmd -renderer $renderer
+ } elseif {[info command ::xo::Table::YUIDataTableRenderer] ne ""} {
+ lappend cmd -renderer YUIDataTableRenderer
+ }
+ eval $cmd
+
+ #
+ # Sorting is done for the time being in Tcl. This has the advantage
+ # that page_order can be sorted with the special mixin and that
+ # instance attributes can be used for sorting as well.
+ #
+ foreach {att order} [split $orderby ,] break
+ if {$att eq "__page_order"} {
+ t1 mixin add ::xo::OrderedComposite::IndexCompare
+ }
+ #my msg "order=[expr {$order eq {asc} ? {increasing} : {decreasing}}] $att"
+ t1 orderby -order [expr {$order eq "asc" ? "increasing" : "decreasing"}] $att
+
+ #
+ # Compute filter clauses
+ #
+ set init_vars [list]
+ array set uc {tcl false h "" vars "" sql ""}
+ if {[info exists unless]} {
+ array set uc [::xowiki::FormPage filter_expression $unless ||]
+ set init_vars [concat $init_vars $uc(vars)]
+ }
+ array set wc {tcl true h "" vars "" sql ""}
+ if {[info exists where]} {
+ array set wc [::xowiki::FormPage filter_expression $where &&]
+ set init_vars [concat $init_vars $wc(vars)]
+ }
+ #my msg uc=[array get uc]
+ #my msg wc=[array get wc]
+
+ #
+ # get an ordered composite of the base set (currently including extra_where clause)
+ #
+ #my log "exists category_id [info exists category_id]"
+ set extra_where_clause ""
+ if {[info exists category_id]} {
+ foreach {cnames extra_where_clause} [my category_clause $category_id bt.item_id] break
+ }
+
+ set items [::xowiki::FormPage get_form_entries \
+ -base_item_ids $form_item_ids \
+ -parent_id $parent_id \
+ -form_fields $form_fields \
+ -publish_status $publish_status \
+ -extra_where_clause $extra_where_clause \
+ -h_where [array get wc] \
+ -from_package_ids $package_ids \
+ -package_id $package_id]
+
+ if {[info exists with_categories]} {
+ if {$extra_where_clause eq ""} {
+ set base_items $items
+ } else {
+ # difference to variable items: just the extra_where_clause
+ set base_items [::xowiki::FormPage get_form_entries \
+ -base_item_ids $form_item_ids \
+ -parent_id $parent_id \
+ -form_fields $form_fields \
+ -publish_status $publish_status \
+ -h_where [array get wc] \
+ -from_package_ids $package_ids \
+ -package_id $package_id]
+ }
+ }
+ #my log "queries done"
+ if {[info exists wf]} {
+ set wf_link [$package_id pretty_link -parent_id $parent_id $wf]
+ }
+
+ foreach p [$items children] {
+ $p set package_id $package_id
+ array set __ia $init_vars
+ array set __ia [$p instance_attributes]
+ if {[expr $uc(tcl)]} continue
+ #if {![expr $wc(tcl)]} continue ;# already handled in get_form_entries
+
+ set page_link [$p pretty_link]
+
+ if {[info exists wf]} {
+ set view_link $wf_link?m=create-or-use&p.form=[$p name]
+ } else {
+ set view_link $page_link
+ }
+ t1 add
+ set __c [t1 last_child]
+
+ if {[info exists use_button(edit)]} {
+ $__c set _edit " "
+ $__c set _edit.title #xowiki.edit#
+ #set template_file view-default
+ $__c set _edit.href [$package_id make_link -link $page_link $p edit return_url template_file]
+ }
+ if {[info exists use_button(delete)]} {
+ $__c set _delete " "
+ $__c set _delete.title #xowiki.delete#
+ $__c set _delete.href [$package_id make_link -link $page_link $p delete return_url]
+ }
+ if {[info exists use_button(view)]} {
+ $__c set _view " "
+ $__c set _view.title #xowiki.view#
+ $__c set _view.href $view_link
+ } elseif {![info exists use_button(no-view)]} {
+ #
+ # Set always a view link, if we have no view button ...
+ #
+ if {[info exists __ff($view_field)]} {
+ # .... on $view_field) (per default: _name) ....
+ $__c set _$view_field.href $view_link
+ } else {
+ # .... otherwise on the first form_field
+ $__c set _[lindex $field_names 0].href $view_link
+ }
+ }
+
+ # set always last_modified for default sorting
+ $__c set __last_modified [$p set last_modified]
+
+
+ foreach __fn $field_names {
+ $__ff($__fn) object $p
+ $__c set _$__fn [$__ff($__fn) pretty_value [$p property $__fn]]
+ }
+ $__c set __name [$package_id external_name -parent_id [$p parent_id] [$p name]]
+ }
+
+ #
+ # If there are multiple includelets on a single page,
+ # we have to identify the right one for e.g. producing the
+ # csv table. Therefore, we compute an includelet_key
+ #
+ my instvar name
+ set includelet_key ""
+ foreach var {name form_item_ids form publish_states field_names unless} {
+ if {[info exists $var]} {append includelet_key $var : [set $var] ,}
+ }
+
+ if {[info exists voting_form]} {
+ # if the user provided a voting form name without a language prefix,
+ # add one.
+ if {![regexp {^..:} $voting_form]} {
+ set obj [my set __including_page]
+ set voting_form [$obj lang]:$voting_form
+ }
+ }
+
+ set given_includelet_key [::xo::cc query_parameter includelet_key ""]
+ if {$given_includelet_key ne ""} {
+ if {$given_includelet_key eq $includelet_key && [info exists generate]} {
+ if {$generate eq "csv"} {
+ return [t1 write_csv]
+ } elseif {$generate eq "voting_form"} {
+ return [my generate_voting_form $voting_form $voting_form_form t1 $field_names $voting_form_anon_instances]
+ }
+ }
+ return ""
+ }
+
+ set links [list]
+ set base [$form_item pretty_link]
+ set label [$form_item name]
+
+ if {$with_form_link} {
+ append html [_ xowiki.entries_using_form [list form "$label"]]
+ }
+ append html [t1 asHTML]
+
+ if {$csv} {
+ set csv_href "[::xo::cc url]?[::xo::cc actual_query]&includelet_key=[ns_urlencode $includelet_key]&generate=csv"
+ lappend links "csv"
+ }
+ if {[info exists voting_form]} {
+ set href "[::xo::cc url]?[::xo::cc actual_query]&includelet_key=[ns_urlencode $includelet_key]&generate=voting_form"
+ lappend links " Generate Voting Form $voting_form"
+ }
+ append html [join $links ,]
+ #my log "render done"
+
+ if {[info exists with_categories]} {
+ set category_html [$o include [list categories -count 1 -tree_name $with_categories \
+ -ordered_composite $base_items]]
+ return "[$entry title]
"
+ }
+ return $listing
+ }
+
+ yui-carousel instproc form_images {
+ -package_id
+ -parent_id
+ {-form "en:photo.form"}
+ {-glob ""} {-width ""} {-height ""}
+ } {
+ set form_item_ids [::xowiki::Weblog instantiate_forms -parent_id $parent_id -forms $form -package_id $package_id]
+ if {$form_item_ids eq ""} {error "could not find en:photo.form"}
+ set form_item_id [lindex $form_item_ids 0]
+
+ set items [::xowiki::FormPage get_form_entries \
+ -base_item_ids $form_item_ids -form_fields "" \
+ -publish_status all \
+ -always_queried_attributes * \
+ -parent_id $parent_id \
+ -package_id $package_id]
+ #my msg "parent-id=$parent_id, glob=$glob entries=[llength [$items children]]"
+
+ foreach entry [$items children] {
+ # order?
+ set image_name [$entry property image]
+ if {$glob ne "" && ![string match $glob $image_name]} {
+ $items delete $entry
+ continue
+ }
+ if {![info exists entry_field_names]} {
+ set entry_field_names [$entry field_names]
+ set entry_form_fields [::xowiki::FormPage get_table_form_fields \
+ -base_item $form_item_id -field_names $entry_field_names \
+ -form_constraints [$form_item_id set form_constraints]]
+ foreach fn $entry_field_names f $entry_form_fields {set ff($fn) $f}
+ }
+ $entry load_values_into_form_fields $entry_form_fields
+ foreach f $entry_form_fields {$f object $entry}
+ if {[info exists ff(image)]} {
+ if {$width ne ""} {$ff(image) width $width}
+ if {$height ne ""} {$ff(image) height $height}
+ if {$width ne "" && $height ne ""} {
+ $ff(image) set geometry "${width}x${height}"
+ }
+ $ff(image) label [$entry property _title]
+ }
+ $entry set html [$entry render_content]
+ #my log html=[$entry set html]
+ }
+ return $items
+ }
+
+ yui-carousel instproc render {} {
+ my get_parameters
+
+ set ajaxhelper 1
+ ::xowiki::Includelet require_YUI_CSS -ajaxhelper $ajaxhelper carousel/assets/skins/sam/carousel.css
+ ::xowiki::Includelet require_YUI_JS -ajaxhelper $ajaxhelper "yahoo-dom-event/yahoo-dom-event.js"
+ ::xowiki::Includelet require_YUI_JS -ajaxhelper $ajaxhelper "connection/connection-min.js"
+ ::xowiki::Includelet require_YUI_JS -ajaxhelper $ajaxhelper "animation/animation-min.js"
+ ::xowiki::Includelet require_YUI_JS -ajaxhelper $ajaxhelper "element/element-min.js"
+ ::xowiki::Includelet require_YUI_JS -ajaxhelper $ajaxhelper "carousel/carousel-min.js"
+ ::xo::Page set_property body class "yui-skin-sam "
+
+ if {![regexp {^(.*)x(.*)$} $item_size _ item_width item_height]} {
+ error "invalid item size '$item_size'; use e.g. 300x240"
+ }
+
+ if {[info exists image_size]} {
+ if {![regexp {^(.*)x(.*)$} $image_size _ width height]} {
+ error "invalid image size '$image_size'; use e.g. 300x240"
+ }
+ } elseif {$auto_size} {
+ set width $item_width
+ set height $item_height
+ } else {
+ set width ""
+ set height ""
+ }
+
+ set ID container_[::xowiki::Includelet html_id [self]]
+ set play_interval [expr {int($play_interval * 1000)}]
+
+ ::xo::Page requireJS [subst {
+ YAHOO.util.Event.onDOMReady(function (ev) {
+ var carousel = new YAHOO.widget.Carousel("$ID",{
+ isCircular: true, numVisible: $num_visible,
+ autoPlayInterval: $play_interval, animation: {speed: 1.0}
+ });
+ carousel.render(); // get ready for rendering the widget
+ carousel.show(); // display the widget
+
+ });
+ }]
+
+ ::xo::Page requireStyle [subst {
+
+ \#$ID {
+ margin: 0 auto;
+ }
+
+ .yui-carousel-element .yui-carousel-item-selected {
+ opacity: 1;
+ }
+
+ .yui-carousel-element li {
+ height: ${item_height}px;
+ width: ${item_width}px;
+ }
+
+ .yui-skin-sam .yui-carousel-nav ul li {
+ margin: 0;
+ }}]
+
+ set parent_id [[my set __including_page] parent_id]
+ if {[info exists folder]} {
+ set folder_page [$package_id get_page_from_item_ref -parent_id $parent_id $folder]
+ if {$folder_page eq ""} {
+ error "no such folder '$folder'"
+ } else {
+ set parent_id [$folder_page item_id]
+ }
+ }
+
+ set content "\n"
+ if {$form ne ""} {
+ set images [my form_images -package_id $package_id -parent_id $parent_id \
+ -form $form -glob $glob -width $width -height $height]
+ } else {
+ set images [my images -package_id $package_id -parent_id $parent_id \
+ -glob $glob -width $width -height $height]
+ }
+ foreach entry [$images children] {
+ append content "
\n"
+
+ set state [expr {[$page set last_modified] eq [$page set creation_date] ? "New" : "Updated"}]
+ set instance_name [::$package_id instance_name]
+
+ set notif_user_id [expr {[$page exists modifying_user] ? [$page set modifying_user] : [$page set creation_user]}]
+
+ #ns_log notice "--n per directory [$page set title] ($state)"
+ notification::new \
+ -type_id [notification::type::get_type_id -short_name xowiki_notif] \
+ -object_id [$page set package_id] \
+ -response_id [$page set revision_id] \
+ -notif_subject "\[$instance_name\] [$page set title] ($state)" \
+ -notif_text $text \
+ -notif_html $html \
+ -notif_user $notif_user_id
+
+ #ns_log notice "--n find categories [$page set title] ($state)"
+
+ foreach cat_id [category::get_mapped_categories [$page set item_id] ] {
+ set tree_id [category::get_tree $cat_id]
+ array unset cat
+ array unset label
+ foreach category_info [::xowiki::Category get_category_infos -tree_id $tree_id] {
+ foreach {category_id category_label deprecated_p level} $category_info {break}
+ set cat($level) $category_id
+ set label($level) $category_label
+ if {$category_id == $cat_id} break
+ }
+ foreach level [array names cat] {
+ #ns_log notice "--n category $cat($level) $label($level): [$page set title] ($state)"
+ notification::new \
+ -type_id [notification::type::get_type_id -short_name xowiki_notif] \
+ -object_id $cat($level) \
+ -response_id [$page set revision_id] \
+ -notif_subject "\[$instance_name\] $label($level): [$page set title] ($state)" \
+ -notif_text $text \
+ -notif_html $html \
+ -notif_user $notif_user_id
+ }
+ }
+ }
+
+
+ ad_proc -private process_reply { reply_id} {
+ handles a reply to an xowiki notif
+
+ @author Deds Castillo (deds@i-manila.com.ph)
+ @creation-date 2006-06-08
+
+ } {
+ # DEDS: need to decide on what to do with this
+ # do we publish it as comment?
+ # for now, drop it
+ return "f"
+ }
+}
+::xo::library source_dependent
+
Index: openacs-4/packages/xowiki/tcl/package-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/package-procs.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/xowiki/tcl/package-procs.tcl 13 Sep 2012 16:05:28 -0000 1.261
@@ -0,0 +1,2422 @@
+::xo::library doc {
+ XoWiki - package specific methods
+
+ @creation-date 2006-10-10
+ @author Gustaf Neumann
+ @cvs-id $Id: package-procs.tcl,v 1.261 2012/09/13 16:05:28 victorg Exp $
+}
+
+namespace eval ::xowiki {
+
+ ::xo::PackageMgr create ::xowiki::Package \
+ -superclass ::xo::Package \
+ -pretty_name "XoWiki" \
+ -package_key xowiki \
+ -parameter {
+ {folder_id 0}
+ {force_refresh_login false}
+ }
+ # {folder_id "[::xo::cc query_parameter folder_id 0]"}
+
+ if {[apm_version_names_compare [ad_acs_version] 5.2] <= -1} {
+ error "We require at least OpenACS Version 5.2; current version is [ad_acs_version]"
+ }
+
+ Package ad_proc get_package_id_from_page_id {
+ {-revision_id 0}
+ {-item_id 0}
+ } {
+ Obtain the package_id from either the item_id or the revision_id of a page
+ } {
+ if {$revision_id} {
+ set object_id $revision_id
+ } elseif {$item_id} {
+ set object_id $item_id
+ } else {
+ error "Either item_id or revision_id must be provided"
+ }
+ return [db_string [my qn get_pid] "select package_id from acs_objects where object_id = :object_id"]
+ }
+
+ Package ad_proc instantiate_page_from_id {
+ {-revision_id 0}
+ {-item_id 0}
+ {-user_id -1}
+ {-parameter ""}
+ } {
+ Instantiate a page in situations, where the context is not set up
+ (e.g. we have no package object). This call is convenient
+ when testing e.g. from the developer shell
+ } {
+ set package_id [my get_package_id_from_page_id \
+ -item_id $item_id \
+ -revision_id $revision_id]
+ ::xo::Package initialize \
+ -export_vars false \
+ -package_id $package_id \
+ -init_url false -actual_query "" \
+ -parameter $parameter \
+ -user_id $user_id
+ set page [::xo::db::CrClass get_instance_from_db -item_id $item_id -revision_id $revision_id]
+ ::$package_id set_url -url [$page pretty_link]
+ return $page
+ }
+
+ Package ad_proc get_url_from_id {{-item_id 0} {-revision_id 0}} {
+ Get the full URL from a page in situations, where the context is not set up.
+ @see instantiate_page_from_id
+ } {
+ set page [::xowiki::Package instantiate_page_from_id \
+ -item_id $item_id -revision_id $revision_id]
+ return [::[$page package_id] url]
+ }
+
+ #
+ # URL and naming management
+ #
+ Package instproc normalize_name {string} {
+ set string [string trim $string]
+ regsub -all {[\#/\\]} $string _ string
+ # if subst_blank_in_name is turned on, turn spaces into _
+ if {[my get_parameter subst_blank_in_name 1]} {
+ regsub -all { +} $string "_" string
+ }
+ #my log "normalize name '$string' // [my get_parameter subst_blank_in_name 1]"
+ #return [ns_urldecode $string]
+ return $string
+ }
+
+ Package instproc default_locale {} {
+ if {[my get_parameter use_connection_locale 0]} {
+ # we return the connection locale (if not connected the system locale)
+ set locale [::xo::cc locale]
+ } else {
+ # return either the package locale or the site-wide locale
+ set locale [lang::system::locale -package_id [my id]]
+ }
+ return $locale
+ }
+
+ Package instproc default_language {} {
+ return [string range [my default_locale] 0 1]
+ }
+
+ Package array set www-file {
+ admin 1
+ diff 1
+ doc 1
+ edit 1
+ error-template 1
+ portlet 1 portlet-ajax 1 portlets 1
+ prototypes 1
+ ressources 1
+ revisions 1
+ view-default 1 view-links 1 view-plain 1 oacs-view 1 oacs-view2 1 oacs-view3 1
+ view-book 1 view-book-no-ajax 1 view-oacs-docs 1
+ download 1
+ }
+
+ Package instproc get_lang_and_name {-path -name {-default_lang ""} vlang vlocal_name} {
+ my upvar $vlang lang $vlocal_name local_name
+ if {[info exists path]} {
+ #
+ # Determine lang and name from a path with slashes
+ #
+ if {[regexp {^pages/(..)/(.*)$} $path _ lang local_name]} {
+ } elseif {[regexp {^([a-z][a-z])/(.*)$} $path _ lang local_name]} {
+
+ # TODO we should be able to get rid of this by using a canonical /folder/ in
+ # case of potential conflicts, like for file....
+
+ # check if we have a LANG - FOLDER "conflict"
+ set item_id [::xo::db::CrClass lookup -name $lang -parent_id [my folder_id]]
+ if {$item_id} {
+ my msg "We have a lang-folder 'conflict' (or a two-char folder) with folder: $lang"
+ set local_name $path
+ if {$default_lang eq ""} {set default_lang [my default_language]}
+ set lang $default_lang
+ }
+
+ } elseif {[regexp {^(file|image|swf|download/file|download/..|tag)/(.*)$} $path _ lang local_name]} {
+ } else {
+ set local_name $path
+ if {$default_lang eq ""} {set default_lang [my default_language]}
+ set lang $default_lang
+ }
+ } elseif {[info exists name]} {
+ #
+ # Determine lang and name from a names as it stored in the database
+ #
+ if {![regexp {^(..):(.*)$} $name _ lang local_name]} {
+ if {![regexp {^(file|image|swf):(.*)$} $name _ lang local_name]} {
+ set local_name $name
+ if {$default_lang eq ""} {set default_lang [my default_language]}
+ set lang $default_lang
+ }
+ }
+ }
+ }
+
+ Package instproc get_page_from_super {-folder_id:required name} {
+ set package [self]
+ set inherit_folders [FormPage get_super_folders $package $folder_id]
+
+ foreach item_ref $inherit_folders {
+ set folder [::xo::cc cache [list $package get_page_from_item_ref $item_ref]]
+ if {$folder eq ""} {
+ my log "Error: Could not resolve parameter folder page '$item_ref' of FormPage [self]."
+ } else {
+ set item_id [::xo::db::CrClass lookup -name $name -parent_id [$folder item_id]]
+ if { $item_id != 0 } {
+ return $item_id
+ }
+ }
+ }
+ return 0
+ }
+
+
+ Package instproc get_parent_and_name {-path:required -lang:required -parent_id:required vparent vlocal_name} {
+ my upvar $vparent parent $vlocal_name local_name
+ if {[regexp {^([^/]+)/(.+)$} $path _ parent local_name]} {
+
+ # try without a prefix
+ set p [my lookup -name $parent -parent_id $parent_id]
+
+ if {$p == 0} {
+ # check if page is inherited
+ set p2 [my get_page_from_super -folder_id $parent_id $parent]
+ if { $p2 != 0 } {
+ set p $p2
+ }
+
+ }
+
+ if {$p == 0} {
+ # pages are stored with a lang prefix
+ set p [my lookup -name ${lang}:$parent -parent_id $parent_id]
+ #my log "check with prefix '${lang}:$parent' returned $p"
+
+ if {$p == 0 && $lang ne "en"} {
+ # try again with prefix "en"
+ set p [my lookup -name en:$parent -parent_id $parent_id]
+ #my log "check with en 'en:$parent' returned $p"
+ }
+ }
+
+ if {$p != 0} {
+ if {[regexp {^([^/]+)/(.+)$} $local_name _ parent2 local_name2]} {
+ set p2 [my get_parent_and_name -path $local_name -lang $lang -parent_id $p parent local_name]
+ #my log "recursive call for '$local_name' parent_id=$p returned $p2"
+ if {$p2 != 0} {
+ set p $p2
+ }
+ }
+ }
+
+ if {$p != 0} {
+ return $p
+ }
+ }
+ set parent ""
+ # a trailing slash indicates a directory, remove it from the path
+ set local_name [string trimright $path /]
+ return $parent_id
+ }
+
+ Package instproc get_page_from_name {{-parent_id ""} {-assume_folder false} -name:required} {
+ # Check if an instance with this name exists in the current package.
+ if {$assume_folder} {
+ set lookup_name $name
+ } else {
+ my get_lang_and_name -name $name lang stripped_name
+ set lookup_name $lang:$stripped_name
+ }
+ set item_id [my lookup -parent_id $parent_id -name $lookup_name]
+ if {$item_id != 0} {
+ return [::xo::db::CrClass get_instance_from_db -item_id $item_id]
+ }
+ return ""
+ }
+
+ Package instproc folder_path {{-parent_id ""} {-context_url ""} {-folder_ids ""}} {
+ #
+ # handle different parent_ids
+ #
+ if {$parent_id eq "" || $parent_id == [my folder_id]} {
+ return ""
+ }
+ #
+ # The item might be in a folder along the folder path. so it
+ # will be found by the object resolver. For the time being, we
+ # do nothing more about this.
+ #
+ #
+ if { $context_url ne {} } {
+ set parts [split $context_url /]
+ set index [expr {[llength $parts]-1}]
+ }
+
+ if { $context_url ne {} } {
+ set context_id [my get_parent_and_name -path $context_url -lang "" -parent_id $parent_id parent local_name]
+ #my msg "context_url $context_url folder_ids $folder_ids context_id $context_id"
+ }
+
+ set path ""
+ while {1} {
+ set fo [::xo::db::CrClass get_instance_from_db -item_id $parent_id]
+ if { $context_url ne {} } {
+ set context_name [lindex $parts $index]
+ if {1 && $parent_id in $folder_ids} {
+ #my msg "---- parent $parent_id in $folder_ids"
+ set context_id [$context_id item_id]
+ set fo [::xo::db::CrClass get_instance_from_db -item_id $context_id]
+ } else {
+ #my msg "context_url $context_url, parts $parts, context_name $context_name // parts $parts // index $index / folder $fo"
+
+ if { [$fo name] ne $context_name } {
+ set context_folder [my get_page_from_name -parent_id $parent_id -assume_folder true -name $context_name]
+ if {$context_folder eq ""} {
+ my msg "my get_page_from_name -parent_id $parent_id -assume_folder true -name $context_name ==> EMPTY"
+ my msg "Cannot lookup '$context_name' in package folder $parent_id [$parent_id name]"
+
+ set new_path [join [lrange $parts 0 $index] /]
+ set p2 [my get_parent_and_name -path [join [lrange $parts 0 $index] /] -lang "" -parent_id $parent_id parent local_name]
+ my msg "p2=$p2 new_path=$new_path '$local_name' ex=[nsf::object::exists $p2] [$p2 name]"
+
+ }
+ my msg "context_name [$context_folder serialize]"
+ set context_id [$context_folder item_id]
+ set fo [::xo::db::CrClass get_instance_from_db -item_id $context_id]
+ }
+ incr index -1
+ }
+ }
+
+ #my get_lang_and_name -name [$fo name] lang stripped_name
+ #set path $stripped_name/$path
+ set path [$fo name]/$path
+ if {[my folder_id] == [$fo parent_id]} break
+ if {[$fo parent_id] < 0} break
+ set parent_id [$fo parent_id]
+ }
+ return $path
+ }
+
+
+ Package ad_instproc external_name {
+ {-parent_id ""}
+ name
+ } {
+ Generate a name with a potentially inserted parent name
+
+ @param parent_id parent_id (for now just for download)
+ @param name name of the wiki page
+ } {
+ set folder [my folder_path -parent_id $parent_id]
+ if {$folder ne ""} {
+ # Return the stripped name for sub-items, the parent has already
+ # the language prefix
+ #my get_lang_and_name -name $name lang stripped_name
+ return $folder$name
+ }
+ return $name
+
+ }
+
+ Package ad_instproc pretty_link {
+ {-anchor ""}
+ {-query ""}
+ {-absolute:boolean false}
+ {-siteurl ""}
+ {-lang ""}
+ {-parent_id ""}
+ {-download false}
+ {-context_url ""}
+ {-folder_ids ""}
+ name
+ } {
+ Generate a (minimal) link to a wiki page with the specified name.
+ Pratically all links in the xowiki systems are generated through this
+ function.
+
+ @param anchor anchor to be added to the link
+ @param absolute make an absolute link (including protocol and host)
+ @param lang use the specified 2 character language code (rather than computing the value)
+ @param download create download link (without m=download)
+ @param parent_id parent_id (for now just for download)
+ @param name name of the wiki page
+ } {
+ #my msg "input name=$name, lang=$lang"
+ set default_lang [my default_language]
+
+ my get_lang_and_name -default_lang $lang -name $name lang name
+
+ set host [expr {$absolute ? ($siteurl ne "" ? $siteurl : [ad_url]) : ""}]
+ if {$anchor ne ""} {set anchor \#$anchor}
+ if {$query ne ""} {set query ?$query}
+ #my log "--LINK $lang == $default_lang [expr {$lang ne $default_lang}] $name"
+ set package_prefix [my get_parameter package_prefix [my package_url]]
+ if {$package_prefix eq "/" && [string length $lang]>2} {
+ # don't compact the the path for images etc. to avoid conflicts
+ # with e.g. //../image/*
+ set package_prefix [my package_url]
+ }
+ #my msg "lang=$lang, default_lang=$default_lang, name=$name, parent_id=$parent_id, package_prefix=$package_prefix"
+
+ if {$parent_id eq -100} {
+ return ${host}${package_prefix}$query$anchor
+ }
+
+ if {[ns_info name] eq "NaviServer"} {
+ set encoded_name [ns_urlencode -part path -- $name]
+ } else {
+ set encoded_name [::xowiki::utility urlencode $name]
+ }
+
+ #set encoded_name [string map [list %2d - %5f _ %2e .] [ns_urlencode $name]]
+ set folder [my folder_path -parent_id $parent_id -context_url $context_url -folder_ids $folder_ids]
+ #my msg "folder_path = $folder, default_lang [my default_language]"
+
+ # if {$folder ne ""} {
+ # # if folder has a different language than the content, we have to provide a prefix....
+ # regexp {^(..):} $folder _ default_lang
+ # }
+
+ #my log "h=${host}, prefix=${package_prefix}, folder=$folder, name=$encoded_name anchor=$anchor download=$download"
+ #my msg folder=$folder,lang=$lang,default_lang=$default_lang
+ if {$download} {
+ #
+ # use the special download (file) syntax
+ #
+ set url ${host}${package_prefix}download/file/$folder$encoded_name$query$anchor
+ } elseif {$lang ne $default_lang || [[self class] exists www-file($name)]} {
+ #
+ # If files are physical files in the www directory, add the
+ # language prefix
+ #
+ set url ${host}${package_prefix}${lang}/$folder$encoded_name$query$anchor
+ } else {
+ #
+ # Use the short notation without language prefix
+ #
+ set url ${host}${package_prefix}$folder$encoded_name$query$anchor
+ }
+ #my msg "final url=$url"
+ return $url
+ }
+
+ Package instproc init {} {
+ #my log "--R creating + folder_object"
+ next
+ my require_folder_object
+ my set policy [my get_parameter -check_query_parameter false security_policy ::xowiki::policy1]
+ #my proc destroy {} {my log "--P "; next}
+ }
+
+ Package ad_instproc get_parameter {{-check_query_parameter true} {-type ""} attribute {default ""}} {
+ resolves configurable parameters according to the following precedence:
+ (1) values specifically set per page {{set-parameter ...}}
+ (2) query parameter
+ (3) form fields from the parameter_page FormPage
+ (4) standard OpenACS package parameter
+ } {
+ set value [::xo::cc get_parameter $attribute]
+ if {$check_query_parameter && $value eq ""} {set value [string trim [my query_parameter $attribute]]}
+ if {$value eq "" && $attribute ne "parameter_page"} {
+ #
+ # Try to get the parameter from the parameter_page. We have to
+ # be very cautious here to avoid recursive calls (e.g. when
+ # resolve_page_name needs as well parameters such as
+ # use_connection_locale or subst_blank_in_name, etc.).
+ #
+ set pp [my get_parameter parameter_page ""]
+ if {$pp ne ""} {
+ if {![regexp {/?..:} $pp]} {
+ my log "Error: Name of parameter page '$pp' of package [my id] must contain a language prefix"
+ } else {
+ set page [::xo::cc cache [list [self] get_page_from_item_ref $pp]]
+ if {$page eq ""} {
+ my log "Error: Could not resolve parameter page '$pp' of package [my id]."
+ }
+ #my msg pp=$pp,page=$page-att=$attribute
+
+ if {$page ne "" && [$page exists instance_attributes]} {
+ array set __ia [$page set instance_attributes]
+ if {[info exists __ia($attribute)]} {
+ set value $__ia($attribute)
+ #my log "got value='$value'"
+ }
+ }
+ }
+ }
+ }
+ #if {$value eq ""} {set value [::[my folder_id] get_payload $attribute]}
+ if {$value eq ""} {set value [next $attribute $default]}
+ if {$type ne ""} {
+ # to be extended and generalized
+ switch $type {
+ word {if {[regexp {\W} $value]} {error "value '$value' contains invalid character"}}
+ default {error "requested type unknown: $type"}
+ }
+ }
+ #my log " $attribute returns '$value'"
+ return $value
+ }
+
+ Package instproc resolve_package_path {path name_var} {
+ #
+ # In case, we can resolve the path against an xowiki instance,
+ # require the package, set the provided name of the object and
+ # return the package_id. If we cannot resolve the name, turn 0.
+ #
+ my upvar $name_var name
+
+ # Set output variable always to some value
+ set name $path
+
+ if {[regexp {^/(/.*)$} $path _ path]} {
+ array set "" [site_node::get_from_url -url $path]
+ if {$(package_key) eq "acs-subsite"} {
+ # the main site
+ return 0
+ }
+ set package_id $(package_id)
+ set package_class [::xo::PackageMgr get_package_class_from_package_key $(package_key)]
+ if {$package_class ne ""} {
+ # we found an xo::Package, but is it an xowiki package?
+ set classes [concat $package_class [$package_class info heritage]]
+ if {[lsearch $classes ::xowiki::Package] > -1} {
+ # yes, it is an xowiki::package, compute the name and return the package_id
+ ::xowiki::Package require $package_id
+ set name [string range $path [string length $(url)] end]
+ return $package_id
+ }
+ }
+ } elseif {!([string match "http*://*" $path] || [string match "ftp://*" $path])} {
+ return [my id]
+ }
+
+ return 0
+ }
+
+ Package instproc get_package_id_from_page_name {{-default_lang ""} page_name} {
+ #
+ # Return package id + remaining page name
+ #
+ set package_id [my id]
+ if {[regexp {^/(/[^/]+/)(.*)$} $page_name _ url page_name]} {
+ set provided_name $page_name
+ array set "" [site_node::get_from_url -url $url]
+ if {$(package_id) eq ""} {return ""}
+ if {$(name) ne ""} {set package_id $(package_id)}
+ ::xowiki::Package require $package_id
+ my get_lang_and_name -default_lang $default_lang -path $page_name lang stripped_name
+ set page_name $lang:$stripped_name
+ set url $(url)
+ set search 0
+ } else {
+ set url [my url]/
+ set provided_name $page_name
+ set search 1
+ }
+ #my msg [self args]->[list package_id $package_id page_name $page_name url $url provided_name $provided_name search $search]
+ return [list package_id $package_id page_name $page_name url $url provided_name $provided_name search $search]
+ }
+
+ Package instproc resolve_page_name {{-default_lang ""} page_name} {
+ #
+ # This is a very simple version for resolving page names in an
+ # package instance. It can be called either with a full page
+ # name with a language prefix (as stored in the CR) for the
+ # current package, or with a path (starting with a //) pointing to
+ # an xowiki instance followed by the page name.
+ #
+ # Examples
+ # ... resolve_page_name en:index
+ # ... resolve_page_name //xowiki/en:somepage
+ #
+ # The method returns either the page object or empty ("").
+ #
+ return [my get_page_from_item_ref -allow_cross_package_item_refs true -default_lang $default_lang $page_name]
+ #array set "" [my get_package_id_from_page_name $page_name]
+ }
+
+ Package instproc resolve_page_name_and_init_context {{-lang} page_name} {
+ # todo: currently only used from
+ # Page->resolve_included_page_name. maybe, it could be replaced by
+ # get_page_from_name or get_page_from_item_ref
+ set page ""
+ #
+ # take a local copy of the package_id, since it is possible
+ # that the variable package_id might changed to another instance.
+ #
+ set package_id [my id]
+ array set "" [my get_package_id_from_page_name $page_name]
+ if {$(package_id) != $package_id} {
+ #
+ # Handle cross package resolve requests
+ #
+ # Note, that package::initialize might change the package id.
+ # Preserving the package-url is just necessary, if for some
+ # reason the same package is initialized here with a different
+ # url. This could be done probably with a flag to initialize,
+ # but we get below the object name from the package_id...
+ #
+ #my log "cross package request $page_name"
+ #
+ set last_package_id $package_id
+ set last_url [my url]
+ #
+ # TODO: We assume here that the package is an xowiki package.
+ # The package might be as well a subclass of xowiki...
+ # For now, we fixed the problem to perform reclassing in
+ # ::xo::Package init and calling a per-package instance
+ # method "initialize"
+ #
+ ::xowiki::Package initialize -parameter {{-m view}} -url $(url)$(provided_name) \
+ -actual_query ""
+ #my log "url=$url=>[$package_id serialize]"
+
+ if {$package_id != 0} {
+ #
+ # For the resolver, we create a fresh context to avoid recursive loops, when
+ # e.g. revision_id is set through a query parameter...
+ #
+ set last_context [expr {[$package_id exists context] ? [$package_id context] : "::xo::cc"}]
+ $package_id context [::xo::Context new -volatile]
+ set object_name [$package_id set object]
+ #my log "cross package request got object=$object_name"
+ #
+ # A user might force the language by preceding the
+ # name with a language prefix.
+ #
+ #my log "check '$object_name' for lang prefix"
+ if {![regexp {^..:} $object_name]} {
+ if {![info exists lang]} {
+ set lang [my default_language]
+ }
+ set object_name ${lang}:$object_name
+ }
+ set page [$package_id resolve_page -simple true $object_name __m]
+ $package_id context $last_context
+ }
+ $last_package_id set_url -url $last_url
+
+ } else {
+ # It is not a cross package request
+ set last_context [expr {[$package_id exists context] ? [$package_id context] : "::xo::cc"}]
+ $package_id context [::xo::Context new -volatile]
+ set page [$package_id resolve_page -use_package_path $(search) $(page_name) __m]
+ $package_id context $last_context
+ }
+ #my log "returning $page"
+ return $page
+ }
+
+
+ Package instproc show_page_order {} {
+ return [my get_parameter display_page_order 1]
+ }
+
+ #
+ # conditional links
+ #
+ Package ad_instproc make_link {{-with_entities 0} -privilege -link object method args} {
+ Creates conditionally a link for use in xowiki. When the generated link
+ will be activated, the specified method of the object will be invoked.
+ make_link checks in advance, wether the actual user has enough
+ rights to invoke the method. If not, this method returns empty.
+
+ @param Object The object to which the link refers to. If it is a package_id it will base \
+ to the root_url of the package_id. If it is a page, it will base to the page_url
+ @param method Which method to use. This will be appended as "m=method" to the url.
+
+ Examples for methods:
+
+
+
+ @param args List of attributes to be append to the link. Every element
+ can be an attribute name, or a "name value" pair. Behaves like export_vars.
+
+ @return The link or empty
+ @see export_vars
+ } {
+ my instvar id
+
+ set computed_link ""
+ #my msg "obj=$object, [$object info class]"
+ if {[$object istype ::xowiki::Package]} {
+ set base [my package_url]
+ if {[info exists link]} {
+ set computed_link [uplevel export_vars -base [list $base$link] [list $args]]
+ } else {
+ lappend args [list $method 1]
+ set computed_link [uplevel export_vars -base [list $base] [list $args]]
+ }
+ } elseif {[$object istype ::xowiki::Page]} {
+ if {[info exists link]} {
+ set base $link
+ } else {
+ set base [my url]
+ #my msg "base = '[my url]'"
+ }
+ lappend args [list m $method]
+ set computed_link [uplevel export_vars -base [list $base] [list $args]]
+ #my msg "computed_link = '$computed_link'"
+ }
+ if {$with_entities} {
+ regsub -all & $computed_link "&" computed_link
+ }
+
+ # provide links based in untrusted_user_id
+ set party_id [::xo::cc set untrusted_user_id]
+ if {[info exists privilege]} {
+ #my log "-- checking priv $privilege for [self args] from id $id"
+ set granted [expr {$privilege eq "public" ? 1 :
+ [::xo::cc permission -object_id $id -privilege $privilege -party_id $party_id] }]
+ } else {
+ # determine privilege from policy
+ #my msg "-- check permissions from $id of object $object $method"
+ if {[catch {
+ set granted [my check_permissions \
+ -user_id $party_id \
+ -package_id $id \
+ -link $computed_link $object $method]
+ } errorMsg ]} {
+ my log "error in check_permissions: $errorMsg"
+ set granted 0
+ }
+ #my msg "--p $id check_permissions $object $method ==> $granted"
+ }
+ #my log "granted=$granted $computed_link"
+ if {$granted} {
+ return $computed_link
+ }
+ return ""
+ }
+
+ Package instproc make_form_link {-form {-parent_id ""} -name -nls_language -return_url} {
+ my instvar id
+ # use the same instantiate_forms as everywhere; TODO: will go to a different namespace
+ set form_id [lindex [::xowiki::Weblog instantiate_forms \
+ -parent_id $parent_id \
+ -forms $form \
+ -package_id $id] 0]
+ #my log "instantiate_forms -parent_id $parent_id -forms $form => $form_id "
+ if {$form_id ne ""} {
+ if {$parent_id eq ""} {unset parent_id}
+ set form_link [$form_id pretty_link]
+ #my msg "$form -> $form_id -> $form_link -> [my make_link -with_entities 0 -link $form_link $form_id \
+ # create-new return_url title parent_id name nls_language]"
+ return [my make_link -with_entities 0 -link $form_link $form_id \
+ create-new return_url title parent_id name nls_language]
+ }
+ }
+
+ Package instproc create_new_snippet {
+ {-object_type ::xowiki::Page}
+ provided_name
+ } {
+ my get_lang_and_name -path $provided_name lang local_name
+ set name ${lang}:$local_name
+ set new_link [my make_link [my id] edit-new object_type return_url name]
+ if {$new_link ne ""} {
+ return ""
+ set title "[$i set title] ([llength $contrib($c)])"
+ foreach j $contrib($c) {
+ set stamp [clock format [$j set clock] -format "%X %Z" -gmt true]
+ append event "
" \n
+ }
+ set stamp [clock format [$i set clock] -format "%b %d %Y %X %Z" -gmt true]
+ set user [::xo::get_user_name [$i set creation_user]]
+ append result [my tag -atts [list \
+ start $stamp \
+ title $title \
+ link [$package_id pretty_link \
+ -parent_id [$i set parent_id] \
+ [$i set name]]] \
+ event $event] \n
+ }
+ append result \n
+ return $result
+ }
+}
+
+namespace eval ::xowiki {
+ # This is the class representing an RSS client
+ Class create RSS-client -parameter url
+
+ # Constructor for a given URI
+ RSS-client instproc init {} {
+ set XML [my load]
+ if {$XML ne ""} {
+ my parse $XML
+ }
+ }
+
+ RSS-client instproc load { } {
+ set r [::xo::HttpRequest new -url [my url] -volatile]
+ #my msg "statuscode = [$r set status_code], content_type=[$r set content_type]"
+ #set f [open /tmp/feed w]; fconfigure $f -translation binary; puts $f [$r set data]; close $f
+ if {[$r exists status] && [$r set status] eq "canceled"} {
+ my set errorMessage [$r set cancel_message]
+ }
+ return [$r set data]
+ # the following does not appear to be necessary due to changes in http-client-procs.
+ #set charset utf-8
+ #regexp {^<\?xml\s+version\s*=\s*\S+\s+encoding\s*=\s*[\"'](\S+)[\"']} $xml _ charset
+ #ns_log notice "charse=$charset,xml=$xml"
+ #return [encoding convertfrom [string tolower $charset] $xml]
+ }
+
+ RSS-client instproc parse {data} {
+ set doc [ dom parse $data ]
+ set root [ $doc documentElement ]
+
+ switch [RSS-client getRSSVersion $doc] {
+ 0.91 - 0.92 - 0.93 - 2.0 {
+ my array set xpath {
+ title {/rss/channel/title/text()}
+ link {/rss/channel/link/text()}
+ imgNode {/rss/channel/image/title}
+ imgTitle {/rss/channel/image/title/text()}
+ imgLink {/rss/channel/image/url/text()}
+ imgWidth {/rss/channel/image/width/text()}
+ imgHeight {/rss/channel/image/height/text()}
+ stories {/rss/channel/item}
+ itemTitle {title/text()}
+ itemLink {link/text()}
+ itemPubDate {pubDate/text()}
+ itemDesc {description/text()}
+ }
+ }
+ 1.0 {
+ my array set xpath {
+ title {/rdf:RDF/*[local-name()='channel']/*[local-name()='title']/text()}
+ link {/rdf:RDF/*[local-name()='channel']/*[local-name()='link']/text()}
+ imgNode {/rdf:RDF/*[local-name()='image']}
+ imgTitle {/rdf:RDF/*[local-name()='image']/*[local-name()='title']/text()}
+ imgLink {/rdf:RDF/*[local-name()='image']/*[local-name()='url']/text()}
+ imgWidth {/rdf:RDF/*[local-name()='image']/*[local-name()='width']/text()}
+ imgHeight {/rdf:RDF/*[local-name()='image']/*[local-name()='height']/text()}
+ stories {/rdf:RDF/*[local-name()='item']}
+ itemTitle {*[local-name()='title']/text()}
+ itemLink {*[local-name()='link']/text()}
+ itemPubDate {*[local-name()='pubDate']/text()}
+ itemDesc {*[local-name()='description']/text()}
+ }
+
+ }
+ default {
+ my set errorMessage "Unsupported RSS schema [RSS-client getRSSVersion $doc]"
+ return
+ #error "Unsupported schema [RSS-client getRSSVersion $doc]"
+ }
+ }
+
+ # Channel
+ set cN [ $root child 1 channel ]
+ set channel [::xowiki::RSS-client::channel create [self]::channel -root $cN]
+
+ # Items
+ my set items {}
+ set stories [$root selectNodes [my set xpath(stories)] ]
+ foreach iN $stories {
+ my lappend items [::xowiki::RSS-client::item new -childof [self] -node $iN ]
+ }
+ }
+
+ # returns the XPath Query for a given type
+ RSS-client instproc xpath { key } {
+ return [my set xpath($key)]
+ }
+
+ # returns the channel object
+ RSS-client instproc channel {} {
+ return [self]::channel
+ }
+
+ # returns a list of items
+ RSS-client instproc items {} {
+ return [my set items]
+ }
+
+ # detects the RSS version of the document
+ RSS-client proc getRSSVersion {doc} {
+ set root [$doc documentElement]
+ switch [$root nodeName] {
+ rss {
+ if {[$root hasAttribute version]} {
+ return [$root getAttribute version]
+ }
+ # Best guess as most stuff is optional...
+ return 0.92
+ }
+ rdf:RDF {
+ return 1.0
+ }
+ default {
+ return 0
+ }
+ }
+ }
+
+ # this namespace contains some utility methods
+ RSS-client proc node_uri {node xpath} {
+ set n [$node selectNode $xpath]
+ if {$n ne ""} {
+ # Only if there is a lonely &, quote it back to an entity.
+ return [string map { & %26 } [$n nodeValue]]
+ } else {
+ return ""
+ }
+ }
+
+ RSS-client proc node_text {node xpath} {
+ set n [$node selectNode $xpath]
+ if {$n ne ""} {
+ return [$n nodeValue]
+ } else {
+ return ""
+ }
+ }
+
+ # this class is used to contain rss items
+ Class create RSS-client::item -parameter node
+ RSS-client::item instforward xpath {%my info parent} %proc
+
+ # get the title
+ RSS-client::item instproc title { } {
+ return [::xowiki::RSS-client node_text [my node] [my xpath itemTitle]]
+ }
+
+ # get the link
+ RSS-client::item instproc link {} {
+ return [::xowiki::RSS-client node_uri [my node] [my xpath itemLink]]
+ }
+
+ # get the description
+ RSS-client::item instproc description {} {
+ return [::xowiki::RSS-client node_text [my node] [my xpath itemDesc]]
+ }
+
+ # return the publication date as string
+ RSS-client::item instproc pubDate {} {
+ return [::xowiki::RSS-client node_text [my node] [my xpath itemPubDate]]
+ }
+
+
+ # this class contains information on the channel
+ Class create RSS-client::channel -parameter root
+ RSS-client::channel instforward xpath {%my info parent} %proc
+
+ # get the title
+ RSS-client::channel instproc title { } {
+ return [::xowiki::RSS-client node_text [my root] [my xpath title]]
+ }
+
+ # get the image link
+ RSS-client::channel instproc imgLink {} {
+ return [::xowiki::RSS-client node_uri [my root] [my xpath imgLink]]
+ }
+
+ # get the image title
+ RSS-client::channel instproc imgTitle {} {
+ return [::xowiki::RSS-client node_text [my root] [my xpath imgTitle]]
+ }
+
+ # get the image width
+ RSS-client::channel instproc imgWidth {} {
+ return [::xowiki::RSS-client node_text [my root] [my xpath imgWidth]]
+ }
+ # get the image height
+ RSS-client::channel instproc imgHeight {} {
+ return [::xowiki::RSS-client node_text [my root] [my xpath imgHeight]]
+ }
+
+
+}
+
+::xo::library source_dependent
+
Index: openacs-4/packages/xowiki/tcl/template-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/template-procs.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/xowiki/tcl/template-procs.tcl 13 Sep 2012 16:05:28 -0000 1.4
@@ -0,0 +1,53 @@
+###
+### just for backward compatibility for oacs 5.1 -gustaf neumann
+###
+
+if {[apm_version_names_compare [ad_acs_version] 5.2] <= -1} {
+
+
+ad_proc -public ::template::adp_include {
+ {-uplevel 1}
+ src
+ varlist
+} {
+ return a the output of a tcl/adp pair as a string. adp_level is
+ set to the calling procedure so that pass by reference works.
+ and example of using this is in the search indexer for various content
+ types:
+
+ bookshelf::book::get -book_id $book_id -array bookdata
+ set body [template::adp_include /packages/bookshelf/lib/one-book \
+ [list &book "bookdata" base $base style feed]]
+
+
+ The [list &book "bookdata" ...] tells adp_include to pass the book array by reference to the adp in
+clude, where it is
+ refered to via @book.field@.
+
+ @param uplevel how far up the stack should the adp_level be set to
+ (default is the calling procedures level)
+ @param src should be the path to the tcl/adp pair relative to the server root, as
+ with the src attribute to the include tag.
+ @param varlist a list of {key value key value ... } varlist can also be &var foo
+ for things passed by reference (arrays and multirows)
+
+ @return the string generated by the tcl/adp pair.
+
+ @author Jeff Davis davis@xarg.net
+ @creation-date 2004-06-02
+
+ @see template::adp_parse
+} {
+ # set the stack frame at which the template is being parsed so that
+ # other procedures can reference variables cleanly
+ variable parse_level
+ lappend parse_level [expr {[info level] - $uplevel}]
+
+ set __adp_out [template::adp_parse [template::util::url_to_file $src] $varlist]
+
+ # pop off parse level
+ template::util::lpop parse_level
+
+ return $__adp_out
+}
+}
\ No newline at end of file
Index: openacs-4/packages/xowiki/tcl/tree-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/tree-procs.tcl,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/xowiki/tcl/tree-procs.tcl 13 Sep 2012 16:05:28 -0000 1.10
@@ -0,0 +1,406 @@
+::xo::library doc {
+ Classes for creating, manageing and rendering trees
+
+ @creation-date 2009-05-29
+ @author Gustaf Neumann
+ @cvs-id $Id: tree-procs.tcl,v 1.10 2012/09/13 16:05:28 victorg Exp $
+}
+
+namespace eval ::xowiki {
+ #
+ # ::xowiki::Tree
+ #
+ # This class manages the creation and rendering of the nodes of the
+ # tree. It provides a name and id for rending in HTML.
+
+ Class Tree \
+ -superclass ::xo::OrderedComposite \
+ -parameter {
+ {name ""}
+ id
+ }
+
+ #
+ # Class methods
+ #
+ Tree proc renderer {style} {
+ set renderer TreeRenderer=$style
+ if {![my isclass $renderer]} {
+ error "No such renderer $renderer (avalialble [info commands ::xowiki::TreeRenderer=*]"
+ }
+ return $renderer
+ }
+
+ Tree proc include_head_entries {{-renderer mktree} args} {
+ eval [my renderer $renderer] include_head_entries $args
+ }
+
+ #
+ # Instance methods
+ #
+ Tree instproc init {} {
+ # If there is no id specified, use the name as id.
+ if {![my exists id]} {my id [my name]}
+ }
+
+ Tree instproc add_item {
+ -category
+ -orderby
+ -itemobj
+ {-increasing:boolean true}
+ {-open_item:boolean false}
+ } {
+ set items ${category}::items
+ if {![my isobject $items]} {
+ ::xo::OrderedComposite create $items
+ if {[info exists orderby]} {
+ if {$orderby eq "page_order"} {
+ $items mixin add ::xo::OrderedComposite::IndexCompare
+ }
+ set direction [expr {$increasing ? "increasing" : "decreasing"}]
+ $items orderby -order $direction $orderby
+ }
+ }
+ $items add $itemobj
+ if {$open_item} {
+ $category open_tree
+ $itemobj set open_item 1
+ }
+ }
+ Tree instproc open_tree {} {;}
+ Tree instproc render {{-style mktree} {-js ""} {-context ""}} {
+ set renderer [[self class] renderer $style]
+ $renderer set context $context
+ $renderer set js $js
+ TreeNode instmixin $renderer
+ set content [$renderer render [self]]
+ TreeNode instmixin ""
+ if {[$renderer set js] ne ""} {
+ append content "\n\n"
+ }
+ return $content
+ }
+
+ Tree instproc add_pages {
+ {-full false}
+ {-remove_levels 0}
+ {-book_mode false}
+ {-open_page ""}
+ {-expand_all false}
+ -owner
+ pages
+ } {
+ my instvar package_id
+ set tree(-1) [self]
+ my set open_node($tree(-1)) 1
+ set pos 0
+ foreach o [$pages children] {
+ $o instvar page_order title name
+ if {![regexp {^(.*)[.]([^.]+)} $page_order _ parent]} {set parent ""}
+ set page_number [$owner page_number $page_order $remove_levels]
+
+ set level [regsub -all {[.]} [$o set page_order] _ page_order_js]
+ if {$full || [my exists open_node($parent)] || [my exists open_node($page_order)]} {
+ set href [$owner href $book_mode $name]
+ set is_current [expr {$open_page eq $name}]
+ set is_open [expr {$is_current || $expand_all}]
+ set c [::xowiki::TreeNode new -orderby pos -pos [incr pos] -level $level \
+ -object $o -owner [self] \
+ -label $title -prefix $page_number -href $href \
+ -highlight $is_current \
+ -expanded $is_open \
+ -open_requests 1]
+ set tree($level) $c
+ for {set l [expr {$level - 1}]} {![info exists tree($l)]} {incr l -1} {}
+ $tree($l) add $c
+ if {$is_open} {$c open_tree}
+ }
+ }
+ return $tree(-1)
+ }
+
+ #
+ # ::xowiki::TreeNode
+ #
+ # The TreeNode represents an n-ary node storing its child nodes in
+ # an ordered composite. In addition to its children, every node may
+ # have items associated. For example, a tree of categories can have
+ # associated categorized items, which can be added via the method
+ # "add_item".
+ #
+ Class TreeNode -superclass Tree -parameter {
+ level label pos {open_requests 0} count {href ""}
+ object owner li_id ul_id ul_class
+ {prefix ""} {expanded false} {highlight false}
+ }
+
+ TreeNode instproc open_tree {} {
+ my open_requests 1
+ my expanded true
+ if {[my exists __parent]} {[my set __parent] open_tree}
+ }
+
+ TreeNode instproc some_child_has_items {} {
+ foreach i [my children] {
+ if {[my isobject ${i}::items]} {return 1}
+ if {[$i some_child_has_items]} {return 1}
+ }
+ return 0
+ }
+
+ TreeNode instproc render {} {
+ set content ""
+ if {[my isobject [self]::items]} {
+ foreach i [[self]::items children] {
+ append cat_content [my render_item -highlight [$i exists open_item] $i ]
+ }
+ foreach c [my children] {append cat_content [$c render] \n}
+ append content [my render_node -open [expr {[my open_requests]>0}] $cat_content]
+ } elseif {[my open_requests]>0 || [my some_child_has_items]} {
+ set cat_content ""
+ foreach c [my children] {append cat_content [$c render] \n}
+ append content [my render_node -open true $cat_content]
+
+ }
+ return $content
+ }
+
+ #
+ # The rendering of trees is performed via rendering classes. All
+ # renderers are created and configured via the meta-class
+ # TreeRenderer. This meta-class defines the common attributes and
+ # behavior of all TreeRenders.
+ #
+ # In particular, the TreeRenders are defined to work with xowiki's
+ # page fragment caching. Via page fragment caching, the result of
+ # the rendering of includedlets is cached. However, the renderer
+ # might require additional CSS or JavaScript code, which has to be
+ # included for the cached HTML fragment as well. Therefore, the
+ # method "include_head_entries" is provided, which is called
+ # independently from HTML generation.
+ #
+ #
+
+ Class create TreeRenderer -superclass Class \
+ -parameter {
+ {subtree_wrapper_class}
+ {li_expanded_atts ""}
+ {highlight_atts {"style = 'font-weight:bold;'"}}
+ }
+ TreeRenderer instproc include_head_entries {args} {
+ # to be overloaded
+ }
+ TreeRenderer instproc render {tree} {
+ set content ""
+ foreach c [$tree children] {append content [$c render] \n}
+ return $content
+ }
+
+ #
+ # The renderers should provide the following methods as procs
+ #
+ # - include_head_entries {args}
+ # - render {tree}
+ #
+ # (both are optional) and the following methods as instprocs
+ #
+ # - render_node {{-open:boolean false} cat_content}
+ # - render_item {{-highlight:boolean false} item}
+ #
+ # The last two methods are required.
+
+ # Below are the currently defined tree renderers. We use as naming
+ # convention TreeRenderer=
+
+
+
+
+
+
+@content;noquote@
+
Index: openacs-4/packages/xowiki/www/edit.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/Attic/edit.adp,v
diff -u -N
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/xowiki/www/edit.adp 13 Sep 2012 16:05:29 -0000 1.6
@@ -0,0 +1,24 @@
+@title@ (@page_context@)
@title@
@title@ (@page_context@)
@title@
@title@ (@page_context@)
@title@
+
+Contributors
+@gc_comments;noquote@
@gc_link;noquote@
+$msg"} +test proc hint msg {ns_write "$msg
cvs -z3 -d:pserver:anonymous@cvs.tdom.org:/usr/local/pubcvs co tdom
+
+
+ | + + |
+#xowiki.choose_image#: |
+||
#xowiki.width#: | #xowiki.height#: | + |
+#xowiki.image_width_hint# + | +
+
|
+ |||||||
+
|
+
@event@+
Abstract: @_text@
@detail_link@
@event@+
Abstract: @_text@
@detail_link@
Creator: | +@creator@ | +
Version: | +5.4 | +
Date: | +{{creation-date -format "%B %d, %Y"}}
+ |
+
{{toc -style list -decoration plain -book_mode 1 -expand_all 1}} +
+{{book -menu_buttons ""}} +
+ +} Index: openacs-4/packages/xowiki/www/prototypes/book.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/prototypes/book.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/prototypes/book.page 13 Sep 2012 16:05:37 -0000 1.7 @@ -0,0 +1,14 @@ +::xowiki::Page new -title "Book" -text { +{{set-parameter template_file view-default}} + +>>left-col25<< +{{toc -decoration plain -book_mode 1 -expand_all 1}} +>><< + +>>right-col75<< +Creator: @creator@
+{{book -menu_buttons "edit copy create delete"}} +>><< + +} Index: openacs-4/packages/xowiki/www/prototypes/categories-portlet.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/prototypes/categories-portlet.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/prototypes/categories-portlet.page 13 Sep 2012 16:05:37 -0000 1.7 @@ -0,0 +1,114 @@ +# -*- tcl-*- +# $Id: categories-portlet.page,v 1.7 2012/09/13 16:05:37 victorg Exp $ +::xowiki::Object new -title "Categories" -text { + + # display the category tree with associated pages + # -gustaf neumann + # + # valid parameters from the adp include are + # tree_name: match pattern, if specified displays only the trees + # with matching names + # no_tree_name: if specified, tree names are not displayed + # open_page: name (e.g. en:iMacs) of the page to be opened initially + # tree_style: boolean, default: true, display based on mktree + + my initialize -parameter { + {-tree_name ""} + {-tree_style:boolean 1} + {-no_tree_name:boolean 0} + {-count:boolean 0} + {-summary:boolean 0} + {-open_page ""} + {-category_ids ""} + {-except_category_ids ""} + } + + #if {![info exists name]} {set name "Categories"} + + my proc content {} { + my get_parameters + set folder_id [$package_id folder_id] + + set open_item_id [expr {$open_page ne "" ? + [::xo::db::CrClass lookup -name $open_page -parent_id $folder_id] : 0}] + + set content "" + + set tree_ids [::xowiki::Category get_mapped_trees -object_id $package_id \ + -names $tree_name -output {tree_id tree_name}] + + foreach tree $tree_ids { + foreach {tree_id my_tree_name ...} $tree {break} + if {!$no_tree_name} { + append content "[t1 asHTML]
" + } + +} + + + Index: openacs-4/packages/xowiki/www/prototypes/folder.form.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/prototypes/folder.form.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/prototypes/folder.form.page 13 Sep 2012 16:05:37 -0000 1.11 @@ -0,0 +1,11 @@ +# -*- tcl-*- +::xowiki::Form new \ + -set name en:folder.form \ + -title "Folder Form" \ + -set anon_instances t \ + -set text {{{child-resources}}} \ + -set form {} \ + -set form_constraints {extra_menu_entries:menuentries _nls_language:omit _name:required} + + + Index: openacs-4/packages/xowiki/www/prototypes/ical.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/prototypes/ical.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/prototypes/ical.page 13 Sep 2012 16:05:37 -0000 1.7 @@ -0,0 +1,89 @@ +# -*- tcl-*- +# $Id: ical.page,v 1.7 2012/09/13 16:05:37 victorg Exp $ +::xowiki::Object new -title "News" -text { + # + # A sample News object. + # + my initialize -parameter { + {-page_size:integer 100} + {-page_number:integer 1} + {-summary:boolean 0} + {-date ""} + {-tag ""} + {-ptag ""} + {-entries_of en:announcement-talk|en:announcement-workshop} + } + + # + # The following definition is the default rendering per + # news entry. This is executed in the context of every displayed page. + # + Class create IcalEntryRenderer -instproc render {} { + array set {} [my instance_attributes] + array set event $(event) + #my msg [my instance_attributes] + set dtstart [::xo::ical clock_to_utc [clock scan $event(event.dtstart)]] + set dtend [::xo::ical clock_to_utc [clock scan $event(event.dtend)]] + set body "SUMMARY:[::xo::ical text_to_ical -remove_tags true $event(event.summary)]" + foreach f {location} { + set key event(event.$f) + if {[info exists $key] && [set $key] ne ""} { + append body \n[string toupper $f]:[::xo::ical text_to_ical -remove_tags true [set $key]] + } + } + + return [subst {BEGIN:VEVENT +DTSTART:$dtstart +DTEND:$dtend +$body +URL:[my pretty_link -absolute true] +END:VEVENT +}] + } + + # + # The following definition is the renderer for the aggregated content. + # This is executed in the context of the whole weblog object + # + Class create IcalRenderer -instproc render {} { + return [subst {BEGIN:VCALENDAR +VERSION:2.0 +[next]END:VCALENDAR +}] +} + + my proc content {} { + my get_parameters + # + # this is not a HTML page, decativate master and provide content-type + # + ::xo::cc set_parameter master 0 + ::xo::cc set_parameter content-type text/plain + + # use the above defined custom renderers + set renderer [self]::IcalRenderer + set entry_renderer [self]::IcalEntryRenderer + + set w [::xowiki::Weblog new -destroy_on_cleanup \ + -package_id $package_id \ + -page_size $page_size \ + -page_number $page_number \ + -summary $summary \ + -date $date \ + -category_id [ns_queryget category_id] \ + -tag $tag \ + -ptag $ptag \ + -entry_renderer $entry_renderer \ + -entry_flag __no_form_page_footer \ + -entries_of $entries_of \ + ] + + $w set __page [my info parent] + $w mixin add $renderer + return [$w render] + } + +} + + + Index: openacs-4/packages/xowiki/www/prototypes/import-archive.form.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/prototypes/import-archive.form.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/prototypes/import-archive.form.page 13 Sep 2012 16:05:37 -0000 1.3 @@ -0,0 +1,14 @@ +# -*- tcl-*- +::xowiki::Form new \ + -set name en:import-archive.form \ + -title "Import Archive Form" \ + -set anon_instances t \ + -set text {@archive@} \ + -set form {} \ + -set form_constraints { + archive:import_archive,cleanup=true,label=#xowiki.formfield-import_archive-label# + _name:hidden _nls_language:hidden _page_order:omit _title:omit _creator:omit _description:omit + } + + + Index: openacs-4/packages/xowiki/www/prototypes/index.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/prototypes/index.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/prototypes/index.page 13 Sep 2012 16:05:37 -0000 1.5 @@ -0,0 +1,27 @@ +::xowiki::Page new -title "Index Page" -text { + ++This is the default start page of XoWiki. You can edit this page and save it to provide a personalized look of the XoWiki instance. You can as well provide a different index page through configuration. +You can also view the contents of the Wiki in a weblog style. +For more details, consult the [[http://media.wu-wien.ac.at/download/xowiki-doc/|XoWiki documentation]]. +
++A user can define notifications +for the whole XoWiki instance (by clicking on the notifications button +in the menu bar or for categories (by clicking on the letter symbol +next to the category entries at the bottom of the page) +
+>>left-col<< +{{recent -max_entries 25}} +>><< +>>right-col<< +{{last-visited -title "Last Visited" -max_entries 10 }} +@_title@
@image@
} \ + -set form {} \ + -set form_constraints {image:image} Index: openacs-4/packages/xowiki/www/prototypes/podcast.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/prototypes/podcast.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/prototypes/podcast.page 13 Sep 2012 16:05:38 -0000 1.6 @@ -0,0 +1,31 @@ +# -*- tcl-*- +# $Id: podcast.page,v 1.6 2012/09/13 16:05:38 victorg Exp $ +::xowiki::Object new -title "XoWiki Podcast" -text { + + my initialize -parameter { + {-name_filter ""} + {-days ""} + } + + proc content {} { + my get_parameters + + ::xo::cc set_parameter master 0 + ::xo::cc set_parameter content-type text/xml + # -siteurl http://localhost:8053 + + set f [::xowiki::Podcast new -destroy_on_cleanup \ + -package_id $package_id \ + -name_filter $name_filter \ + -title [[my info parent] set title] \ + -description [[my info parent] set description] \ + -author [[my info parent] creator] \ + -subtitle "A Sample Collection of Podcast Items" \ + -days $days] + + return [$f render] + } +} + + + Index: openacs-4/packages/xowiki/www/prototypes/sitemap.xml.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/prototypes/sitemap.xml.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/prototypes/sitemap.xml.page 13 Sep 2012 16:05:38 -0000 1.3 @@ -0,0 +1,7 @@ +# -*- tcl-*- +::xowiki::Object new -title "sitemap.xml" -set publish_status production -text { + proc content {} { [my package_id] google-sitemap } +} + + + Index: openacs-4/packages/xowiki/www/prototypes/sitemapindex.xml.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/prototypes/sitemapindex.xml.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/prototypes/sitemapindex.xml.page 13 Sep 2012 16:05:39 -0000 1.3 @@ -0,0 +1,7 @@ +# -*- tcl-*- +::xowiki::Object new -title "sitemap.xml" -set publish_status production -text { + proc content {} { ::xowiki::Package google-sitemapindex } +} + + + Index: openacs-4/packages/xowiki/www/prototypes/weblog-portlet.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/prototypes/weblog-portlet.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/prototypes/weblog-portlet.page 13 Sep 2012 16:05:39 -0000 1.20 @@ -0,0 +1,123 @@ +# -*- tcl-*- +# $Id: weblog-portlet.page,v 1.20 2012/09/13 16:05:39 victorg Exp $ +::xowiki::Object new -title "Weblog" -text { + # + # A sample Weblog object. + # + my initialize -parameter { + {-page_size:integer 10} + {-page_number:integer 1} + {-summary:boolean 0} + {-category_id ""} + {-date ""} + {-tag ""} + {-ptag ""} + {-entries_of ""} + } + + # + # The following definition is the default rendering per + # weblog entry. This is executed in the context of every displayed page. + # + Class create EntryRenderer -instproc render {} { + my instvar package_id name title creator creation_user pretty_date description + my log "--W entry [self] [my name] package_id $package_id" + [my set __parent] instvar weblog_obj + + # We get the instance_attributes, if these are available. For the + # time being, we have these only in full mode (no summary) + set link [my detail_link] + set show_more [expr {[$weblog_obj summary] && [my exists text] && [my text] ne ""}] + set more [expr {$show_more ? + " \[#xowiki.weblog-more#\]" : ""}] + append more "" + set my_footer [my htmlFooter] + + append content "Created by $creator, " \ + "last modified by [::xo::get_user_name $creation_user] " \ + "$pretty_date
" \ + $description $more $my_footer \n\ + "' + + 'Type the text here' + + '
' + }, + { + title: 'Strange Template', + image: 'template2.gif', + description: 'A template that defines two colums, each one with a title, and some text.', + html: + '' +
+ 'Title 1' + + ' | ' +
+ '' + + ' | ' +
+ 'Title 2' + + ' | ' +
+ '
' + + 'Text 1' + + ' | ' + + '' + + ' | ' + + 'Text 2' + + ' | ' + + '
' + + 'More text goes here.' + + '
' + }, + { + title: 'Text and Table', + image: 'template3.gif', + description: 'A title with some text and a table.', + html: + '' + + ' | ' + + ' | ' + + ' |
' + + ' | ' + + ' | ' + + ' |
' + + ' | ' + + ' | ' + + ' |
' + + 'Type the text here' + + '
' + + '