::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.239.2.11 2019/05/12 20:02:46 gustafn 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 "//yui.yahooapis.com/$version/build/$path"
security::csp::require style-src yui.yahooapis.com
}
}
::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 "//yui.yahooapis.com/$version/build/$path"
security::csp::require script-src yui.yahooapis.com
}
}
::xowiki::Includelet proc describe_includelets {includelet_classes} {
#: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
lassign $p name value
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] [ns_quotehtml $v]"
}
}
#append result "\n"
}
}
}
append result "}}\n"
set :html([namespace tail $cl]) $result
:describe_includelets [$cl info subclass]
}
}
::xowiki::Includelet proc available_includelets {} {
if {[array exists :html]} {:array unset html}
:describe_includelets [::xowiki::Includelet info subclass]
set result "
"
foreach d [lsort [array names :html]] {
append result "
" [set :html($d)] "
" \n
}
append result "
"
return $result
}
::xowiki::Includelet proc html_to_text {string} {
return [string map [list "&" &] $string]
}
::xowiki::Includelet proc js_name {name} {
return ID[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} {
set table_prefix ""
if {$base_table ne ""} {
set table_prefix "$base_table."
}
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 "${table_prefix}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
}
#: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]
#: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))"
}
}
}
#: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 ,] {
if {![string is integer -strict $cid_and]} {
ad_return_complaint 1 "invalid category id '[ns_quotehtml $cid_and]'"
ad_script_abort
}
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 }])"
}
if {$ors eq "()"} {
set cnames ""
} else {
set cnames [join $or_names { or }]
set extra_where_clause "and ([join $ors { or }])"
}
#:log "--cnames $category_spec -> $cnames // <$extra_where_clause>"
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::dc 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 [::xo::dc get_value 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 [${:__including_page} resolve_included_page_name $page_name]
}
::xowiki::Includelet instproc get_page_order {-source -ordered_pages -pages} {
#
# first check, if we can load the page_order from the page
# denoted by source
#
if {[info exists source]} {
set p [: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} {
set screen_name [acs_user::get_user_info -user_id $user_id -element screen_name]
if {$screen_name eq ""} {
set screen_name [person::get_person_info -person_id $user_id -element name]
}
return $screen_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 {} {
: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 flushing 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 [:info class]
#
# Construct a key based on the class parameters and the
# actual parameters
#
set key "PF-${:package_id}-"
append key [expr {[$c aggregating] ? "agg" : "ind"}]
append key "-$c ${:__caller_parameters}"
if {[$c localized]} {append key -[:locale]}
if {[$c personalized]} {append key -[::xo::cc user_id]}
#
# Get the HTML from the rendered includelet by calling "next"
#
set HTML [::xowiki::cache eval -partition_key ${:package_id} $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 [::xowiki::cache get -partition_key ${:package_id} $key-data]}]} {
:cache_includelet_data $key-data
} else {
#:msg "eval $data"
{*}$data
}
return $HTML
} -instproc cache_includelet_data {key} {
#:msg "data=[next]"
set data [next]
if {$data ne ""} {
::xowiki::cache set -partition_key ${:package_id} $key $data
}
}
}
namespace eval ::xowiki::includelet {
#############################################################################
# dotlrn style includelet decoration for includelets
#
Class create ::xowiki::includelet::decoration=portlet -instproc render {} {
set name ${:name}
set title ${:title}
set package_id ${:package_id}
set class [namespace tail [:info class]]
set id [expr {[info exists :id] ? "id='[:id]'" : ""}]
set html [next]
set localized_title [::xo::localize $title]
set link [expr {[string match "*:*" $name] ?
"[ns_quotehtml $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 ?
{
$link
$html
} : {
$link
[next]
}
}]
Class create ::xowiki::includelet::decoration=edit -instproc render {} {
set name ${:name}
set title ${:title}
set package_id ${:package_id}
set class [namespace tail [:info class]]
set id [expr {[info exists :id] ? "id='[:id]'" : ""}]
set html [next]
set localized_title [::xo::localize $title]
set edit_button [:include [list edit-item-button -book_mode true]]
set link [expr {[string match "*:*" $name] ?
"[ns_quotehtml $localized_title]" :
$localized_title}]
return [subst [[self class] set template]]
} -set template {
$edit_button
$html
}
Class create ::xowiki::includelet::decoration=plain -instproc render {} {
set class [namespace tail [:info class]]
set id [expr {[info exists :id] ? "id='[:id]'" : ""}]
return "
[next]
"
}
Class create ::xowiki::includelet::decoration=rightbox -instproc render {} {
set class [namespace tail [:info class]]
set id [expr {[info exists :id] ? "id='[:id]'" : ""}]
return "
[next]
"
}
}
namespace eval ::xowiki::includelet {
::xowiki::IncludeletClass create get \
-superclass ::xowiki::Includelet \
-parameter {
{__decoration none}
{parameter_declaration {
{-variable}
{-form_variable}
{-source ""}
}}
} -instproc render {} {
:get_parameters
if {![info exists variable] && ![info exists form_variable]} {
return "either -variable or -form_variable must be specified"
}
set page [:resolve_page_name $source]
if {[info exists variable] && [$page exists $variable]} {
return [$page set $variable]
}
if {[info exists form_variable] && [$page exists instance_attributes]} {
set __ia [$page set instance_attributes]
if {[dict exists $__ia $form_variable]} {
return [dict get $__ia $form_variable]
}
}
if {[info exists variable]} {
return "no such variable $variable defined in page [$page set name]"
}
return "no such form_variable $form_variable defined in page [$page set name]"
}
::xowiki::IncludeletClass create creation-date \
-superclass ::xowiki::Includelet \
-parameter {
{__decoration none}
{parameter_declaration {
{-source ""}
{-format "%m-%d-%Y"}
}}
} -instproc render {} {
:get_parameters
set page [:resolve_page_name $source]
set time [$page set creation_date]
regexp {^([^.]+)[.]} $time _ time
return [lc_time_fmt [clock format [clock scan $time] -format "%Y-%m-%d %H:%M:%S"] $format [:locale]]
#return [clock format [clock scan $time] -format $format]
}
#############################################################################
# rss button
#
::xowiki::IncludeletClass create rss-button \
-superclass ::xowiki::Includelet \
-parameter {
{__decoration plain}
{parameter_declaration {
{-span "10d"}
{-name_filter}
{-entries_of}
{-title}
}}
}
rss-button instproc render {} {
:get_parameters
set parent_ids [${:__including_page} parent_id]
set href [export_vars -base [::$package_id package_url] {{rss $span} parent_ids name_filter title entries_of}]
::xo::Page requireLink -rel alternate -type application/rss+xml -title RSS -href $href
return "RSS"
}
#############################################################################
# bookmarklet button
#
::xowiki::IncludeletClass create bookmarklet-button \
-superclass ::xowiki::Includelet \
-parameter {
{__decoration none}
{parameter_declaration {
{-siteurl ""}
{-label ""}
}}
}
bookmarklet-button instproc render {} {
:get_parameters
set parent_id [${:__including_page} parent_id]
set url [::$package_id pretty_link -absolute 1 -siteurl $siteurl -parent_id $parent_id news-item]
if {$label eq ""} {set label "Add to [::$package_id instance_name]"}
if {![info exists :id]} {set :id [::xowiki::Includelet html_id [self]]}
template::add_event_listener \
-id [:id] \
-script [subst {
d=document;w=window;t='';
if(d.selection){t=d.selection.createRange().text;}
else if(d.getSelection){t=d.getSelection();}
else if(w.getSelection){t=w.getSelection();}
void(open('$url?m=create-new&title='+escape(d.title)+
'&detail_link='+escape(d.location.href)+'&text='+escape(t),'_blank',
'scrollbars=yes,width=700,height=575,status=yes,resizable=yes,scrollbars=yes'));
}]
return "[ns_quotehtml $label]"
}
#############################################################################
# set-parameter "includelet"
#
::xowiki::IncludeletClass create set-parameter \
-superclass ::xowiki::Includelet \
-parameter {{__decoration none}}
set-parameter instproc render {} {
:get_parameters
set pl ${:__caller_parameters}
if {[llength $pl] % 2 == 1} {
error "no even number of parameters '$pl'"
}
foreach {att value} $pl {
::xo::cc set_parameter $att $value
}
return ""
}
}
namespace eval ::xowiki::includelet {
#############################################################################
# valid parameters for he categories includelet 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
::xowiki::IncludeletClass create categories \
-superclass ::xowiki::Includelet \
-cacheable true -personalized false -aggregating true \
-parameter {
{title "#xowiki.categories#"}
{parameter_declaration {
{-tree_name ""}
{-tree_style:boolean 1}
{-no_tree_name:boolean 0}
{-count:boolean 0}
{-summary:boolean 0}
{-locale ""}
{-open_page ""}
{-order_items_by "title,asc"}
{-style "mktree"}
{-category_ids ""}
{-except_category_ids ""}
{-allow_edit false}
{-ordered_composite}
}}
}
categories instproc initialize {} {
:get_parameters
if {!$tree_style} {
set style sections
}
set :style $style
}
categories instproc include_head_entries {} {
::xowiki::Tree include_head_entries -renderer ${:style}
}
categories instproc category_tree_edit_button {-object_id:integer -locale {-allow_edit false} -tree_id:integer} {
set allow_p [::xo::cc permission -object_id $object_id -privilege admin -party_id [::xo::cc set untrusted_user_id]]
if {$allow_edit && $allow_p} {
set package ::${:package_id}
if {[info exists tree_id]} {
#
# If a tree_id is given, edit directly the category tree ...
#
set href "[$package package_url]?edit-category-tree&object_id=$object_id&tree_id=$tree_id"
return [${:__including_page} include \
[list edit-item-button -link $href -title [_ xowiki.Edit_category] -target _blank]]
} else {
#
# ... otherwise, manage categories (allow defining new category trees, map/unmap, etc.)
#
set href "[$package package_url]?manage-categories&object_id=$object_id"
return [${:__including_page} include \
[list edit-item-button -link $href -title [_ xowiki.Manage_categories] -target _blank]]
}
}
return ""
}
categories instproc category_tree_missing {{-name ""} -edit_html} {
# todo i18n
if {$name eq ""} {
#set msg "No category tree found."
# maybe it is better to stay quiet in case, no category name was provided
set msg ""
} else {
set msg "No category tree with name '$name' found."
}
::${:package_id} flush_page_fragment_cache -scope agg
set html "
[ns_quotehtml $msg]
"
if {$edit_html ne ""} {
return "$html Manage Categories? $edit_html"
}
return $html
}
categories instproc render {} {
:get_parameters
set content ""
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}]
lassign [::xowiki::Includelet locale_clause -revisions r -items ci $package_id $locale] \
locale locale_clause
set trees [::xowiki::Category get_mapped_trees -object_id $package_id -locale $locale \
-names $tree_name \
-output {tree_id tree_name}]
#:msg "[llength $trees] == 0 && $tree_name"
if {[llength $trees] == 0 && $tree_name ne ""} {
# we have nothing left from mapped trees, maybe the tree_names are not mapped;
# try to get these
foreach name $tree_name {
#set tree_id [lindex [category_tree::get_id $tree_name $locale] 0]
set tree_id [lindex [category_tree::get_id $tree_name] 0]
if {$tree_id ne ""} {
lappend trees [list $tree_id $name]
}
}
}
set edit_html [:category_tree_edit_button -object_id $package_id -allow_edit $allow_edit]
if {[llength $trees] == 0} {
return [:category_tree_missing -name $tree_name -edit_html $edit_html]
}
if {![info exists :id]} {
set :id [::xowiki::Includelet html_id [self]]
}
foreach tree $trees {
lassign $tree tree_id my_tree_name ...
set edit_html [:category_tree_edit_button -object_id $package_id \
-allow_edit $allow_edit -tree_id $tree_id]
#append content "
$edit_html
\n"
if {!$no_tree_name} {
append content "
[ns_quotehtml $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 [: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 {
lassign $category_info cid category_label deprecated_p level
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 r.revision_id = ci.live_revision
and ci.publish_status <> 'production'
"
}
} 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 ,] {
set or_ids [split $cid |]
foreach or_id $or_ids {
if {![string is integer $or_id]} {
ad_return_complaint 1 "invalid category_id"
ad_script_abort
}
}
append sql " and exists (select * from category_object_map \
where object_id = ci.item_id and category_id in ([join $or_ids ,]))"
}
}
append sql $locale_clause
if {$count} {
::xo::dc foreach 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 ${:style}]
} else {
lassign [split $order_items_by ,] orderby direction ;# e.g. "title,asc"
set increasing [expr {$direction ne "desc"}]
set order_column ", p.page_order"
::xo::dc foreach 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 ${: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 {} {
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 (turning off caching
# completely) is a little bit too much, but it is safe.
:get_parameters
if {[[:info class] cacheable] && $pretty_age ne "off"} {
[:info class] cacheable false
}
}
categories-recent instproc include_head_entries {} {
::xowiki::Tree include_head_entries -renderer ${:style}
}
categories-recent instproc render {} {
:get_parameters
if {![info exists :id]} {set :id [::xowiki::Includelet html_id [self]]}
set cattree [::xowiki::Tree new -volatile -id [:id]]
lassign [::xowiki::Includelet locale_clause -revisions r -items ci $package_id $locale] \
locale locale_clause
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::dc 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]
::xo::dc foreach 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 [:locale]])"}
2 {set suffix "([::xowiki::utility pretty_age -timestamp [clock scan $formatted_date] -locale [: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 ${: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 {} {
: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 create t1 -volatile \
-set allow_edit $allow_edit \
-set allow_delete $allow_delete \
-set show_heritage $admin_p \
-columns {
Field create date -label [_ xowiki.Page-last_modified]
if {[[:info parent] set allow_edit]} {
AnchorField create edit -CSSclass edit-item-button -label "" -html {style "padding-right: 2px;"} -richtext 1
}
if {[[:info parent] set show_heritage]} {
AnchorField create inherited -label "" -CSSclass inherited
}
AnchorField create title -label [::xowiki::Page::slot::title set pretty_name]
if {[[:info parent] set allow_delete]} {
AnchorField create 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 [:locale]]}
2 {set age [::xowiki::utility pretty_age -timestamp [clock scan $formatted_date] -locale [: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]
#: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 == ${: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 {} {
:get_parameters
::xo::Page requireCSS "/resources/acs-templating/lists.css"
TableWidget create t1 -volatile \
-columns {
AnchorField create title -label [::xowiki::Page::slot::title set pretty_name]
}
xo::dc foreach get_pages \
[::xo::dc 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 {} {
: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.
#
append :title " in last $interval"
TableWidget create t1 -volatile \
-columns {
AnchorField create title -label [::xowiki::Page::slot::title set pretty_name]
Field create users -label [_ xowiki.includelet-visitors] -html { align right }
}
set since_condition [::xo::dc since_interval_condition time $interval]
xo::dc foreach get_pages \
[::xo::dc 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, 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 create t1 -volatile \
-columns {
AnchorField create title -label [::xowiki::Page::slot::title set pretty_name]
Field create count -label [_ xowiki.includelets-visits] -html { align right }
Field create users -label [_ xowiki.includelet-visitors] -html { align right }
}
xo::dc foreach get_pages \
[::xo::dc 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 {} {
:get_parameters
set :feed [::xowiki::RSS-client new -url $url -destroy_on_cleanup]
if {[info commands [${:feed} channel]] ne ""} {
:title [ [${:feed} channel] title]
}
}
rss-client instproc render {} {
:get_parameters
if {[info commands [${:feed} channel]] eq ""} {
set detail ""
if {[${:feed} exists errorMessage]} {set detail \n[${:feed} set errorMessage]}
return "No data available from $url [ns_quotehtml $detail]"
} else {
set channel [${:feed} channel]
#set html "
[ns_quotehtml [$channel title]]
"
set html "
\n"
set i 0
foreach item [ ${:feed} items ] {
append html "
[ns_quotehtml [$item title]] \
[ns_quotehtml [$item description]] #xowiki.weblog-more#\n"
if {[incr i] >= $max_entries} break
}
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 {} {
:get_parameters
::xo::Page requireCSS "/resources/acs-templating/lists.css"
TableWidget create t1 -volatile \
-columns {
Field create user -label [_ xowiki.includelet-visitors] -html { align right }
Field create count -label [_ xowiki.includelets-visits] -html { align right }
}
::xo::dc foreach most-frequent-visistors \
[::xo::dc 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 {} {
:get_parameters
::xo::Page requireCSS "/resources/acs-templating/lists.css"
TableWidget create t1 -volatile \
-columns {
AnchorField create title -label [::xowiki::Page::slot::title set pretty_name]
}
set user_id [::xo::cc user_id]
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 = :user_id
)"
set or_clause ""
set folder_id [::$package_id folder_id]
::xo::dc foreach unread-items \
[::xo::dc 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 = :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 {} {
: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::dc 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 user_id [::xo::cc user_id]
set sql "select count(*) as nr,tag from xowiki_tags where \
user_id = :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/
::xo::dc foreach get_tag_counts $sql {
set q [list]
if {$summary} {lappend q "summary=[ad_urlencode_query $summary]"}
if {$popular} {lappend q "popular=[ad_urlencode_query $popular]"}
set link $href$tag?[join $q &]
lappend entries "[ns_quotehtml $tag] ([ns_quotehtml $nr])"
#lappend entries "[ns_quotehtml $tag] [ns_quotehtml $nr]"
}
return [expr {[llength $entries] > 0 ?
"
[ns_quotehtml $label]
[join $entries {, }]
\n" :
""}]
}
::xowiki::IncludeletClass create my-tags \
-superclass ::xowiki::Includelet \
-parameter {
{__decoration none}
{parameter_declaration {
{-summary 1}
}}
id
}
my-tags instproc render {} {
:get_parameters
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]
set :tags [lsort [::xowiki::Page get_tags -user_id [::xo::cc user_id] \
-item_id [${:__including_page} item_id] -package_id $package_id]]
set entries [list]
foreach tag ${:tags} {
set href [export_vars -base [::$package_id package_url]/tag/$tag {summary}]
lappend entries "[ns_quotehtml $tag]"
}
set tags_with_links [join [lsort $entries] {, }]
if {![info exists :id]} {
set :id [::xowiki::Includelet html_id [self]]
}
set content [subst {
#xowiki.your_tags_label#: $tags_with_links
(#xowiki.edit_link#,
#xowiki.popular_tags_link#)
}]
template::add_event_listener \
-id ${:id}-edit-tags-control \
-script [subst {document.getElementById("${:id}-edit_tags").style.display="block";}]
template::add_event_listener \
-id ${:id}-popular-tags-control \
-script [subst {get_popular_tags("[ns_quotehtml $popular_tags_link]","${:id}");}]
return $content
}
::xowiki::IncludeletClass create my-categories \
-superclass ::xowiki::Includelet \
-parameter {
{__decoration none}
{parameter_declaration {
{-summary 1}
}}
}
my-categories instproc render {} {
:get_parameters
set content ""
set weblog_page [::$package_id get_parameter weblog_page weblog]
set entries [list]
set href [export_vars -base [::$package_id package_url]$weblog_page {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]] {
lassign [category::get_data $cat_id] category_id category_name tree_id tree_name
#:log "--cat $cat_id $category_id $category_name $tree_id $tree_name"
set label [ns_quotehtml "$category_name ($tree_name)"]
set entry "[ns_quotehtml $label]"
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 {} {
:get_parameters
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_link
} else {
set gc_link ""
}
set gc_comments [general_comments_get_comments $item_id $gc_return_url]
if {$gc_comments ne ""} {
return "
#general-comments.Comments#
$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 {} {
:get_parameters
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]"}
}]
return ""
}
::xowiki::IncludeletClass create delicious \
-superclass ::xowiki::Includelet \
-parameter {
{__decoration none}
{parameter_declaration {
{-description ""}
{-tags ""}
{-url}
}}
}
delicious instproc render {} {
:get_parameters
# The following snippet 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
# described below; this supports 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
}]
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 {} {
:get_parameters
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 referring to the
# including page
#
::xowiki::IncludeletClass create my-references \
-superclass ::xowiki::Includelet \
-parameter {{__decoration none}}
my-references instproc render {} {
:get_parameters
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.
xo::dc foreach -prepare integer 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 [::xo::dc get_value 5.2 {select package_id from cr_folders where folder_id = :parent_id}]
}
if {$pid ne ""} {
::xowiki::Package require $pid
lappend refs "[ns_quotehtml $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 referred to by the
# including page
#
::xowiki::IncludeletClass create my-refers \
-superclass ::xowiki::Includelet \
-parameter {{__decoration none}}
my-refers instproc render {} {
:get_parameters
set item_id [${:__including_page} item_id]
set refs [list]
::xo::dc foreach 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 [::xo::dc get_value 5.2 {select package_id from cr_folders where folder_id = :parent_id}]
}
if {$pid ne ""} {
::xowiki::Package require $pid
lappend refs "[ns_quotehtml $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
}
#
# unresolved-references lists the pages with unresolved references
# in the current xowiki/xowf package. This is intended for use by admins.
#
::xowiki::IncludeletClass create unresolved-references \
-superclass ::xowiki::Includelet \
-parameter {{__decoration none}}
unresolved-references instproc render {} {
:get_parameters
set items [xo::dc list _ {
select object_id from acs_objects
where package_id = :package_id
and object_type = 'content_item'}]
set pages_with_unresolved_items {}
foreach i $items {
#
# Do not try to re-instantiate the including page and the
# package root folder.
#
if {$i eq [${:__including_page} item_id]
|| $i eq [::$package_id folder_id]} {
continue
}
set page [::xo::db::CrClass get_instance_from_db -item_id $i]
#
# Skip as well Objects
#
if {[$page info class] eq "::xowiki::Object"} continue
#
# Render the page to obtain the references. This will as well
# update references (e.g. after importing the dump).
#
$page render -update_references true -with_footer false
#
# Some (dynammic) pages are volatile, skip these as well
#
if {[info commands $page] ne ""} {
set unresolved [$page references get unresolved]
if {$unresolved ne ""} {
#ns_log notice "[$page name] contains unresolved: <$unresolved>"
set entry "[ns_quotehtml [$page name]] contains [join $unresolved {, }]"
lappend pages_with_unresolved_items $entry
} else {
# $page destroy
}
}
}
if {[llength $pages_with_unresolved_items] > 0} {
#
# Return the pages with unresolved references in form of an
# unordered list.
#
return
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 {
Form Name:
}]
template::add_event_listener \
-id $id-control \
-script [subst {document.getElementById("$id").style.display="inline";}]
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 commands ::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 ""} {
#
not allowed in h*: style='float: right; position: relative; top: -32px
set menu "$menu"
}
set label "$page_order $title"
append output \
"" $menu \
"[ns_quotehtml $label]" \
$content
}
book instproc render_items {
-pages:required
{-cnames ""}
{-allow_reorder ""}
-menu_buttons
{-with_footer "false"}
} {
set output ""
if {$cnames ne ""} {
append output "
Filtered by categories: $cnames
"
}
:page_reorder_init_vars -allow_reorder $allow_reorder js last_level ID min_level
set renderer default
foreach o [$pages children] {
$o instvar page_order page_id
set level [expr {[regsub -all {[.]} $page_order _ page_order_js] + 1}]
if {$allow_reorder ne ""} {
#
# Build a (nested) list structure mirroring the hierarchy
# implied by the page_order. In essence, we provide CSS
# classes for the ULs and provide IDs for ULs and LI elements,
# and pass the associated page_order to javascript.
#
if {![regexp {^(.*)[.][^.]+$} $page_order _ prefix]} {set prefix ""}
# First, insert the appropriate opening and closing of ULs. We
# could handle here prefix changes as well as different lists
# (e.g. 1.1 1.2 2.1)
#
if {$last_level != $level} {
for {set l $last_level} {$l > $level} {incr l -1} {append output "\n" }
for {set l $last_level} {$l < $level} {incr l} {
regsub -all {[.]} $prefix _ prefix_js
append output [:page_reorder_open_ul -min_level $min_level -ID $ID -prefix_js $prefix_js $l]
}
set last_level $level
set last_prefix $prefix
}
# Pass the page_order for the element to JavaScript and add
# the li element for the section.
set item_id [:page_reorder_item_id -ID $ID -prefix_js $prefix_js -page_order $page_order js]
append output "
"
}
set p [::xo::db::CrClass get_instance_from_db -item_id 0 -revision_id $page_id]
$p references clear
#$p set render_adp 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]
}
}
append output [:render_item \
-menu_buttons $menu_buttons \
-content $content \
-object $p \
-level $level]
if {$with_footer} {
append output [$p htmlFooter -content $content]
}
}
if {$allow_reorder ne ""} {
for {set l $last_level} {$l > 0} {incr l -1} {append output "\n" }
append output "\n"
}
return $output
}
book instproc render_images {-addClass pages} {
#
# Return a list of the rendered images in HTML markup. The page
# content is reduced to a bare image. Note that this function
# does not return "pages" not containing images.
#
set imageList {}
foreach o [$pages children] {
set p [::xo::db::CrClass get_instance_from_db -item_id 0 -revision_id [$o set page_id]]
set html [$p render -with_footer false]
if {[regsub -nocase {^(.*)(]+>)(.*)$} $html {\2} html] < 1} continue
if {[info exists addClass]} {
regsub -nocase {class\s*=\s*'([^']+)'} $html "class='\\1 $addClass'" html
}
lappend imageList $html
}
return $imageList
}
book instproc render {} {
:get_parameters
lappend ::xowiki_page_item_id_rendered [${:__including_page} item_id]
${:__including_page} set __is_book_page 1
set allow_reorder [:page_reorder_check_allow $allow_reorder]
set extra_where_clause ""
set cnames ""
if {[info exists category_id]} {
lassign [:category_clause $category_id] cnames extra_where_clause
}
lassign [::xowiki::Includelet locale_clause -revisions p -items p $package_id $locale] \
locale locale_clause
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 pages [::xowiki::Page instantiate_objects -sql \
"select page_id, page_order, name, title, item_id \
from xowiki_page_live_revision p \
where parent_id = $parent_id \
and not page_order is NULL $extra_where_clause \
$locale_clause \
[::xowiki::Page container_already_rendered item_id]" ]
$pages mixin add ::xo::OrderedComposite::IndexCompare
$pages orderby page_order
#
# filter range
#
if {$range ne ""} {
lassign [split $range -] from to
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
}
}
}
if {[llength [$pages children]] < 1} {
#
# Provide a hint why not pages were found
#
set p [::xo::db::CrClass get_instance_from_db -item_id $parent_id]
set output "
No pages with parent object [$p name], page_order not NULL and an appropriate publish status found
\n"
} else {
set output [:render_items \
-menu_buttons $menu_buttons \
-with_footer $with_footer \
-pages $pages \
-cnames $cnames \
-allow_reorder $allow_reorder]
}
return $output
}
}
namespace eval ::xowiki::includelet {
#############################################################################
# display a sequence of pages via W3C slidy
#
::xowiki::IncludeletClass create slidy \
-superclass ::xowiki::includelet::book
slidy instproc render_items {
-pages:required
{-cnames ""}
{-allow_reorder ""}
-menu_buttons
{-with_footer "false"}
} {
if {$cnames ne "" || $allow_reorder ne "" || $with_footer != "false"} {
error "ignoring cnames, allow_reorder, and with_footer for the time being"
}
set output ""
foreach o [$pages children] {
set p [::xo::db::CrClass get_instance_from_db -item_id 0 -revision_id [$o set page_id]]
append output "
\n" [$p render -with_footer false] "\n
\n"
}
ns_return 200 text/html [subst {
[${:__including_page} title]
$output
}]
ad_script_abort
}
}
namespace eval ::xowiki::includelet {
#############################################################################
# display a sequence of pages via jQuery Carousel
#
::xowiki::IncludeletClass create jquery-carousel \
-superclass ::xowiki::includelet::book
jquery-carousel instproc render_items {
-pages:required
{-cnames ""}
{-allow_reorder ""}
-menu_buttons
{-with_footer "false"}
} {
if {$cnames ne "" || $allow_reorder ne "" || $with_footer != "false"} {
error "ignoring cnames, allow_reorder, and with_footer for the time being"
}
set id [:js_name]
append output \
"
\n" \
[join [:render_images $pages] "
\n
"]
\
"
\n"
::xo::Page requireJS urn:ad:js:jquery
::xo::Page requireJS "/resources/xowiki/jquery.carousel.min.js"
::xo::Page requireJS [subst -novariables {
$(function(){
$("#[set id]").carousel( );
});
}]
return $output
}
}
namespace eval ::xowiki::includelet {
#############################################################################
# Display a sequence of images via the jQuery plugin
#
# Infinite Carousel
#
# http://www.catchmyfame.com/2009/08/27/jquery-infinite-carousel-plugin-1-2-released/
#
# This includelet works only with images
#
# Install: obtain jQuery plugin
#
# http://www.catchmyfame.com/jquery/jquery.infinitecarousel2.zip
#
# and install its files under packages/xowiki/resources/infiniteCarousel:
#
# infiniteCarousel/images/caption.gif
# infiniteCarousel/images/leftright.gif
# infiniteCarousel/images/playpause.gif
# infiniteCarousel/jquery.infinitecarousel2.js
# infiniteCarousel/jquery.infinitecarousel2.min.js
#
::xowiki::IncludeletClass create jquery-infinite-carousel \
-superclass ::xowiki::includelet::book
jquery-infinite-carousel instproc render_items {
-pages:required
{-cnames ""}
{-allow_reorder ""}
-menu_buttons
{-with_footer "false"}
} {
if {$cnames ne "" || $allow_reorder ne "" || $with_footer != "false"} {
error "ignoring cnames, allow_reorder, and with_footer for the time being"
}
set id [:js_name]
append output \
"
\n" \
[join [:render_images $pages] "
\n
"]
\
"
\n"
::xo::Page requireJS urn:ad:js:jquery
::xo::Page requireJS "/resources/xowiki/infiniteCarousel/jquery.infinitecarousel2.min.js"
::xo::Page requireJS [subst -novariables {
$(function(){
$("#[set id]").infiniteCarousel({
displayTime: 6000,
textholderHeight : .25,
imagePath: '/resources/xowiki/infiniteCarousel/images/',
});
});}]
return $output
}
}
namespace eval ::xowiki::includelet {
#############################################################################
# Display a sequence of images via 3D Cloud Carousel
#
# This includelet works only with images.
#
# Install: get the jQuery plugins cloud-carousel and mousewheel from
#
# http://www.professorcloud.com/mainsite/carousel.htm
# https://github.com/brandonaaron/jquery-mousewheel/downloads
#
# and install these files under
#
# packages/xowiki/resources/cloud-carousel.1.0.5.min.js
# packages/xowiki/resources/jquery.mousewheel.min.js
#
# The following elements might be used in the page containing the includelet:
#
#
#
#
#
#
::xowiki::IncludeletClass create jquery-cloud-carousel \
-superclass ::xowiki::includelet::book
jquery-cloud-carousel instproc render_items {
-pages:required
{-cnames ""}
{-allow_reorder ""}
-menu_buttons
{-with_footer "false"}
} {
if {$cnames ne "" || $allow_reorder ne "" || $with_footer != "false"} {
error "ignoring cnames, allow_reorder, and with_footer for the time being"
}
set id [:js_name]
append output \
"
\n"
}
set edgesHTML ""; set c 0
foreach p [lsort -index 1 -decreasing -integer $edges] {
lassign $p edge weight width
lassign [split $edge ,] a b
#:log "--G $a -> $b check $c > $max_edges, $weight < $cutoff"
if {[incr c] > $max_edges} break
if {$weight < $cutoff} continue
append edgesHTML "g.addEdge(\$('$a'), \$('$b'), $weight, 0, $width);\n"
}
# [lsort -index 1 -decreasing -integer $edges] [set cutoff] - [set c]
return [subst -novariables {
[set nodesHTML]
}]
}
}
namespace eval ::xowiki::includelet {
::xowiki::IncludeletClass create collab-graph \
-superclass ::xowiki::includelet::graph \
-parameter {
{parameter_declaration {
{-max_edges 70}
{-cutoff 0.1}
{-show_anonymous "message"}
-user_id
}}
}
collab-graph instproc render {} {
:get_parameters
if {$show_anonymous ne "all" && [::xo::cc user_id] eq "0"} {
return "You must login to see the [namespace tail [self class]]"
}
if {![info exists user_id]} {set user_id [::xo::cc user_id]}
set folder_id [::$package_id folder_id]
::xo::dc foreach get_collaborators {
select count(revision_id), item_id, creation_user
from cr_revisions r, acs_objects o
where item_id in
(select distinct i.item_id from
acs_objects o, acs_objects o2, cr_revisions cr, cr_items i
where o.object_id = i.item_id and o2.object_id = cr.revision_id
and o2.creation_user = :user_id and i.item_id = cr.item_id
and i.parent_id = :folder_id order by item_id
)
and o.object_id = revision_id
and creation_user is not null
group by item_id, creation_user} {
lappend i($item_id) $creation_user $count
set count_var user_count($creation_user)
if {![info exists $count_var]} {set $count_var 0}
incr $count_var $count
set user($creation_user) "[::xo::get_user_name $creation_user] ([set $count_var])"
if {![info exists activities($creation_user)]} {set activities($creation_user) 0}
incr activities($creation_user) $count
}
set result "
Collaboration Graph for [::xo::get_user_name $user_id] in this wiki"
if {[array size i] < 1} {
append result "
No collaborations found
"
} else {
foreach x [array names i] {
foreach {u1 c1} $i($x) {
foreach {u2 c2} $i($x) {
if {$u1 < $u2} {
set var collab($u1,$u2)
if {![info exists $var]} {set $var 0}
incr $var $c1
incr $var $c2
}
}
}
}
set max 50
foreach x [array names collab] {
if {$collab($x) > $max} {set max $collab($x)}
}
set edges [list]
foreach x [array names collab] {
lappend edges [list $x $collab($x) [expr {$collab($x)*5.0/$max}]]
}
append result "($activities($user_id) contributions)\n"
append result [:graphHTML \
-nodes [array get user] -edges $edges \
-max_edges $max_edges -cutoff $cutoff \
-base collab -attrib user_id]
}
return $result
}
::xowiki::IncludeletClass create activity-graph \
-superclass ::xowiki::includelet::graph \
-parameter {
{parameter_declaration {
{-max_edges 70}
{-cutoff 0.1}
{-max_activities:integer 100}
{-show_anonymous "message"}
}}
}
activity-graph instproc render {} {
:get_parameters
if {$show_anonymous ne "all" && [::xo::cc user_id] eq "0"} {
return "You must login to see the [namespace tail [self class]]"
}
set tmp_table_name XOWIKI_TMP_ACTIVITY
#:msg "tmp exists [::xo::db::require exists_table $tmp_table_name]"
set tt [::xo::db::temp_table new \
-name $tmp_table_name \
-query [::xo::dc select \
-vars "i.item_id, revision_id, creation_user" \
-from "cr_revisions cr, cr_items i, acs_objects o" \
-where "cr.item_id = i.item_id \
and i.parent_id = [::$package_id folder_id] \
and o.object_id = revision_id" \
-orderby "revision_id desc" \
-limit $max_activities] \
-vars "item_id, revision_id, creation_user"]
set total 0
::xo::dc foreach get_activities "
select count(revision_id) as count, item_id, creation_user
from $tmp_table_name
where creation_user is not null
group by item_id, creation_user
" {
lappend i($item_id) $creation_user $count
incr total $count
set count_var user_count($creation_user)
if {![info exists $count_var]} {set $count_var 0}
incr $count_var $count
set user($creation_user) "[::xo::get_user_name $creation_user] ([set $count_var])"
}
$tt destroy
if {[array size i] == 0} {
append result "
No activities found
"
} elseif {[array size user] == 1} {
set user_id [lindex [array names user] 0]
append result "
Collaborations in last $total activities by [array size user] Users in this wiki
"
foreach x [array names i] {
foreach {u1 c1} $i($x) {
foreach {u2 c2} $i($x) {
if {$u1 < $u2} {
set var collab($u1,$u2)
if {![info exists $var]} {set $var 0}
incr $var $c1
incr $var $c2
}
}
}
}
set max 0
foreach x [array names collab] {
if {$collab($x) > $max} {set max $collab($x)}
}
set edges [list]
foreach x [array names collab] {
lappend edges [list $x $collab($x) [expr {$collab($x)*5.0/$max}]]
}
append result [:graphHTML \
-nodes [array get user] -edges $edges \
-max_edges $max_edges -cutoff $cutoff \
-base collab -attrib user_id]
}
return $result
}
::xowiki::IncludeletClass create timeline \
-superclass ::xowiki::Includelet \
-parameter {
{parameter_declaration {
-user_id
{-data timeline-data}
{-interval1 DAY}
{-interval2 MONTH}
}}
}
timeline instproc render {} {
:get_parameters
::xo::Page requireJS "/resources/ajaxhelper/yui/yahoo/yahoo.js"
::xo::Page requireJS "/resources/ajaxhelper/yui/event/event.js"
::xo::Page requireJS "/resources/xowiki/timeline/api/timeline-api.js"
set stamp [clock format [clock seconds] -format "%b %d %Y %X %Z" -gmt true]
if {[info exists user_id]} {append data "?user_id=$user_id"}
return [subst -nocommands -nobackslashes {
}]
}
::xowiki::IncludeletClass create user-timeline \
-superclass timeline \
-parameter {
{parameter_declaration {
-user_id
{-data timeline-data}
{-interval1 DAY}
{-interval2 MONTH}
}}
}
user-timeline instproc render {} {
:get_parameters
if {![info exists user_id]} {set user_id [::xo::cc user_id]]}
::xo::cc set_parameter user_id $user_id
next
}
}
namespace eval ::xowiki::includelet {
#############################################################################
Class create form-menu-button \
-parameter {
form
method
link
package_id
parent_id
base
return_url
{label_suffix ""}
}
form-menu-button instproc render {} {
if {![info exists :link]} {
if {${:parent_id} != [::${:package_id} folder_id]} {
set parent_id ${:parent_id}
}
if {[info exists :return_url]} {set return_url ${:return_url}}
set :link [::${:package_id} make_link -link ${:base} ${:form} ${:method} return_url parent_id]
}
if {${:link} eq ""} {
return ""
}
set msg_key [namespace tail [:info class]]
set label [_ xowiki.$msg_key [list form_name [${:form} name]]]${:label_suffix}
return "[ns_quotehtml $label]"
}
Class create form-menu-button-new -superclass form-menu-button -parameter {
{method create-new}
}
Class create form-menu-button-answers -superclass form-menu-button -parameter {
{method list}
}
form-menu-button-answers instproc render {} {
array set "" [list publish_status all]
array set "" [::xowiki::PageInstance get_list_from_form_constraints \
-name @table_properties \
-form_constraints [[:form] get_form_constraints -trylocal true]]
set count [[:form] count_usages \
-package_id ${:package_id} -parent_id ${:parent_id} \
-publish_status $(publish_status)]
:label_suffix " ($count)"
next
}
Class create form-menu-button-form -superclass form-menu-button -parameter {
{method view}
}
::xowiki::IncludeletClass create form-menu \
-superclass ::xowiki::Includelet \
-parameter {
{__decoration none}
{parameter_declaration {
{-form_item_id:integer}
{-parent_id}
{-form}
{-buttons {new answers}}
{-button_objs}
{-return_url}
}}
}
form-menu instproc render {} {
:get_parameters
#:msg form-menu-[info exists form_item_id] buttons=$buttons
if {![info exists form_item_id]} {
set form_item_id [::xowiki::Weblog instantiate_forms \
-forms $form \
-parent_id [${:__including_page} parent_id] \
-package_id [${:__including_page} package_id]]
if {$form_item_id eq ""} {
# we could throw an error as well...
:msg "could not locate form '$form' for parent_id [${:__including_page} parent_id]"
return ""
}
}
if {[info exists parent_id]} {
if {$parent_id eq "self"} {
set parent_id [${:__including_page} item_id]
}
} else {
#set parent_id [::$package_id folder_id]
set parent_id [${:__including_page} parent_id]
}
if {![info exists button_objs]} {
set button_objs {}
foreach b $buttons {
if {[llength $b]>1} {
lassign $b button id
} else {
lassign [list $b $form_item_id] button id
}
set form [::xo::db::CrClass get_instance_from_db -item_id $id]
#
# "Package require" is just a part of "Package initialize"
# creating the package object if needed....
#
set form_package_id [$form package_id]
if {$form_package_id eq ""} {
#
# When the package_id is empty, the page might be from a
# site-wide page. Resolve the form page to the local context
#
$form set_resolve_context -package_id $package_id -parent_id $parent_id
set form_package_id $package_id
}
::xowiki::Package require $form_package_id
set obj [form-menu-button-$button new -volatile -package_id $package_id \
-base [$form pretty_link] \
-form $form -parent_id $parent_id]
if {[info exists return_url]} {$obj return_url $return_url}
lappend button_objs $obj
}
}
set links [list]
foreach b $button_objs { lappend links [$b render] }
return "
[join $links { · }]
\n"
}
#############################################################################
::xowiki::IncludeletClass create form-stats \
-superclass ::xowiki::Includelet \
-parameter {
{__decoration plain}
{parameter_declaration {
{-form}
{-parent_id}
{-property _state}
{-orderby "count,desc"}
{-renderer "table"}
}}
}
form-stats instproc render {} {
:get_parameters
set o ${:__including_page}
if {![info exists parent_id]} {set parent_id [$o parent_id]}
set form_item_ids [::xowiki::Weblog instantiate_forms \
-forms $form -package_id $package_id \
-parent_id $parent_id]
if {[llength $form_item_ids] != 1} {
return "no such form $form \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 {
set instance_attributes [$i set instance_attributes]
if {[dict exists $instance_attributes $property]} {
set value [dict get $instance_attributes $property]
}
}
if {[info exists __count($value)]} {incr __count($value)} else {set __count($value) 1}
incr sum 1
}
if {$sum == 0} {
return "[_ xowiki.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 [:js_name] \
-title [::xowiki::Includelet js_encode \
"$sum $total_text [_ xowiki.Answers_for_Survey] '[$form_item_ids title]'"]]
return [$h pie [list value count] $percentages]
} else {
#
# standard table encoder
#
TableWidget create t1 -volatile \
-columns {
Field create value -orderby value -label value
Field create count -orderby count -label count
}
lassign [split $orderby ,] att order
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 urn:ad:js:jquery
::xo::Page requireJS "/resources/xowiki/highcharts/js/highcharts.js"
::xo::Page requireJS "/resources/xowiki/highcharts/js/themes/gray.js"
set result "\n"
set title [:title]
if {![info exists :id]} {set :id [::xowiki::Includelet html_id [self]]}
set id [: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}
{-bulk_actions ""}
{-buttons "edit delete"}
{-renderer ""}
}}
}
# {-renderer "YUIDataTableRenderer"}
form-usages instproc render {} {
:get_parameters
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]
} elseif {$parent_id eq "*"} {
set query_parent_id $parent_id
set parent_id [$o parent_id]
}
} else {
set parent_id [$o parent_id]
}
if {![info exists query_parent_id]} {
set query_parent_id $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 \
-default_lang [$o lang] \
-forms $form -package_id [$o package_id]]
if {$form_item_ids eq ""} {
return -code error "could not load form '$form' (default-language [$o lang])"
}
} else {
set form_item_ids [list $form_item_id]
}
set form_constraints $extra_form_constraints\n
set inherit_form_ids {}
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 ""} {
if {[::$inherit_form_id istype ::xowiki::FormPage]} {
set p [::$inherit_form_id property form_constraints]
} else {
set p [::$inherit_form_id form_constraints]
}
append form_constraints $p\n
lappend inherit_form_ids $inherit_form_id
}
}
}
foreach form_item $form_item_ids {
append form_constraints [$form_item get_form_constraints -trylocal true] \n
}
#:log fc=$form_constraints
# 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 {
# All labels of the following switch statement are used
# as variable names. Take care when adding new labels not to
# overwrite existing variables.
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
#: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 $_}
}
if {[llength $inherit_form_ids] > 0} {
set item_ids $inherit_form_ids
} else {
set item_ids $form_item_ids
}
foreach form_item $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}
#foreach f $form_fields {ns_log notice "form <[$form_item name]: field [$f name] label [$f label]"}
}
# if {[info exists __ff(_creation_user)]} {$__ff(_creation_user) label "By User"}
# TODO: wiki-substitution is just forced 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 ""
# we currently support only 'export' as bulk action
set bulk_action_cols ""
foreach bulk_action $bulk_actions {
if {$bulk_action eq "export"} {
append actions [subst {Action bulk-delete -label [_ xowiki.export] -tooltip [_ xowiki.export] \
-url [::$package_id package_url]admin/export}]\n
}
}
if {[llength $bulk_actions] > 0} {
append cols [subst {BulkAction create objects -id ID -actions {$actions}}] \n
append cols {HiddenField create ID} \n
}
if {[info exists use_button(publish_status)]} {
append cols {ImageAnchorField create _publish_status -orderby _publish_status.src -src "" \
-width 8 -height 8 -title "Toggle Publish Status" \
-alt "publish status" -label [_ xowiki.publish_status] \
-CSSclass publish-status-item-button \
-html {style "padding: 2px;text-align: center;"}} \n
}
if {[info exists use_button(edit)]} {
append cols {AnchorField create _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 create _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 create $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 create _delete -CSSclass delete-item-button -label "" -no_csv 1 -richtext 1] \n
}
set cmd [list TableWidget create t1 -volatile -columns $cols]
if {$renderer ne ""} {
lappend cmd -renderer $renderer
} else {
switch [parameter::get_global_value -package_key xowiki -parameter PreferredCSSToolkit -default bootstrap] {
bootstrap {set renderer BootstrapTableRenderer}
default {set renderer YUIDataTableRenderer}
}
lappend cmd -renderer $renderer
}
{*}$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.
#
lassign [split $orderby ,] att order
set sortable 1
if {$att ni $field_names} {
# if {[ns_conn isconnected]} {
# set user_agent [string tolower [ns_set get [ns_conn headers] User-Agent]]
# if {[string match "*bingbot*" $user_agent] || [string match "*turnitin*" $user_agent]} {
# # search engines like bingbot might still have old buggy pages in their indices;
# # don't generate errors on non existing attributes starting with "__*"
# set sortable 0
# }
# }
ad_log warning "Ignore invalid sorting criterion '$att'"
util_user_message -message "Ignore invalid sorting criterion '$att'"
set sortable 0
}
if {$sortable} {
if {$att eq "_page_order"} {
t1 mixin add ::xo::OrderedComposite::IndexCompare
}
#: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 [list {*}$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 [list {*}$init_vars {*}$wc(vars)]
}
#:msg uc=[array get uc]
#:msg wc=[array get wc]
#
# get an ordered composite of the base set (currently including extra_where clause)
#
#:log "exists category_id [info exists category_id]"
set extra_where_clause ""
if {[info exists category_id]} {
lassign [:category_clause $category_id item_id] cnames extra_where_clause
}
set items [::xowiki::FormPage get_form_entries \
-base_item_ids $form_item_ids \
-parent_id $query_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 $query_parent_id \
-form_fields $form_fields \
-publish_status $publish_status \
-h_where [array get wc] \
-from_package_ids $package_ids \
-package_id $package_id]
}
}
#:log "queries done"
if {[info exists wf]} {
set wf_link [::$package_id pretty_link -parent_id $parent_id -path_encode false $wf]
}
set this_url [ad_return_url]
foreach p [$items children] {
$p set package_id $package_id
set __ia [dict merge $init_vars [$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 [export_vars -base $wf_link {{m create-or-use} {p.form "[$p name]"}}]
} else {
set view_link $page_link
}
t1 add
set __c [t1 last_child]
if {[llength $bulk_actions] > 0} {
# xowiki/www/admin/export expects a path to the object
# relative to the package url
set url [[$p package_id] folder_path -parent_id [$p parent_id]][$p name]
$__c set ID $url
}
if {[info exists use_button(publish_status)]} {
$__c set _publish_status " "
$__c set _publish_status.title #xowiki.publish_status#
if {[$p set publish_status] eq "ready"} {
set image active.png
set state "production"
} else {
set image inactive.png
set state "ready"
}
set url [export_vars -base [::$package_id package_url]admin/set-publish-state \
{state {revision_id "[$p set revision_id]"} {return_url $this_url}}]
$__c set _publish_status.src /resources/xowiki/$image
$__c set _publish_status.href $url
}
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
#
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 ${:__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 [: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 "[ns_quotehtml $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 ,]
#: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 "
$category_html
$html\n"
}
return $html
}
form-usages instproc generate_voting_form {form_name form_form t1 field_names voting_form_anon_instances} {
#:msg "generate_voting anon=$voting_form_anon_instances"
set form "\n"
lappend table_field_names _last_modified _creation_user
# Check, of we have a form for editing the generated form. If yes, we will
# instantiate a form page from it.
set form_form_id 0
if {$form_form ne ""} {
set form_form_id [::xo::db::CrClass lookup -name $form_form -parent_id [::${:package_id} folder_id]]
}
# The normal form requires for rich-text the 2 element list as content
if {$form_form_id == 0} { set form [list $form text/html] }
set item_id [::xo::db::CrClass lookup -name $form_name -parent_id [::${:package_id} folder_id]]
if {$item_id == 0} {
if {$form_form_id == 0} {
set f [::xowiki::Form new \
-package_id ${:package_id} \
-parent_id [::${:package_id} folder_id] \
-name $form_name \
-anon_instances $voting_form_anon_instances \
-form $form \
-form_constraints "@fields:scale,n=7,inline=true @cr_fields:hidden @categories:off\n\
@table:[join $table_field_names ,]" \
]
} else {
set f [::xowiki::FormPage new \
-page_template $form_form_id \
-package_id ${:package_id} \
-parent_id [::${:package_id} folder_id] \
-name $form_name]
$f set_property anon_instances $voting_form_anon_instances
$f set_property form $form
$f set_property form_constraints "@fields:scale,n=7,inline=true @cr_fields:hidden @categories:off\n\
@table:[join $table_field_names ,]"
}
$f save_new
set form_href [$f pretty_link]
$f destroy
set action created
} else {
::xo::db::CrClass get_instance_from_db -item_id $item_id
if {$form_form_id == 0} {
::$item_id form $form
} else {
::$item_id set_property form $form
}
::$item_id save
set form_href [::$item_id pretty_link]
set action updated
}
return "#xowiki.form-$action# [ns_quotehtml $form_name]"
}
}
namespace eval ::xowiki::includelet {
#############################################################################
#
# Show an iframe as includelet
#
::xowiki::IncludeletClass create iframe \
-superclass ::xowiki::Includelet \
-parameter {
{parameter_declaration {
{-title ""}
{-url:required}
{-width "100%"}
{-height "500px"}
}}
}
iframe instproc render {} {
:get_parameters
if {$title eq ""} {set title $url}
set content ""
append content "