Index: openacs-4/packages/acs-tcl/tcl/defs-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/defs-procs.tcl,v diff -u -r1.81 -r1.82 --- openacs-4/packages/acs-tcl/tcl/defs-procs.tcl 11 Feb 2019 09:56:49 -0000 1.81 +++ openacs-4/packages/acs-tcl/tcl/defs-procs.tcl 3 Sep 2024 15:37:34 -0000 1.82 @@ -8,25 +8,16 @@ @cvs-id $Id$ } -ad_proc -public ad_acs_version_no_cache {} { - The OpenACS version of this instance. Uses the version name - of the acs-kernel package. - - @author Peter Marklund -} { - apm_version_get -package_key acs-kernel -array kernel - - return $kernel(version_name) -} ad_proc -public ad_acs_version {} { The OpenACS version of this instance. Uses the version name of the acs-kernel package. @author Peter Marklund } { - set key ::acs::version - if {[info exists $key]} {return [set $key]} - set $key [util_memoize ad_acs_version_no_cache] + return [acs::per_thread_cache eval -key acs-tcl.acs_version { + apm_version_get -package_key acs-kernel -array kernel + set kernel(version_name) + }] } ad_proc -public ad_acs_release_date {} { @@ -45,40 +36,48 @@ @return The e-mail address of a technical person who can fix problems } { - return [parameter::get -package_id [ad_acs_kernel_id] -parameter HostAdministrator] + return [parameter::get -package_id $::acs::kernel_id -parameter HostAdministrator] } ad_proc -public ad_outgoing_sender {} { @return The email address that will sign outgoing alerts } { - return [parameter::get -package_id [ad_acs_kernel_id] -parameter OutgoingSender] + return [parameter::get -package_id $::acs::kernel_id -parameter OutgoingSender] } ad_proc -public ad_system_name {} { This is the main name of the Web service that you're offering on top of the OpenACS Web Publishing System. } { - return [parameter::get -package_id [ad_acs_kernel_id] -parameter SystemName] + return [parameter::get -package_id $::acs::kernel_id -parameter SystemName] } ad_proc -public ad_pvt_home {} { This is the URL of a user's private workspace on the system, usually [subsite]/pvt/home.tcl } { - return "[subsite::get_element -element url -notrailing][parameter::get -package_id [ad_acs_kernel_id] -parameter HomeURL]" + return "[subsite::get_element -element url -notrailing][parameter::get -package_id $::acs::kernel_id -parameter HomeURL]" } ad_proc -public ad_admin_home {} { - Returns the directory for the admin home. + Returns the directory for the admin home. } { return "[subsite::get_element -element url]admin" } -# is this accurate? (rbm, aug 2002) - -ad_proc -public ad_package_admin_home { package_key } { +ad_proc -deprecated ad_package_admin_home { package_key } { @return directory for the especified package's admin home. + + # is this accurate? (rbm, aug 2002) + + DEPRECATED: a package URL may not have anything to do with the + package key. Furthermore, the admin pages are normally located in + "-package-/admin" and not in "/admin/-package-". + One is better off generating package URLs by way of the site_nodes. + + @see site_node::get_url + @see site_node::get_from_object_id } { return "[ad_admin_home]/$package_key" } @@ -87,39 +86,39 @@ This is the name that will be used for the user's workspace (usually "Your Workspace"). @return the name especified for the user's workspace in the HomeName kernel parameter. } { - return [lang::util::localize [parameter::get -package_id [ad_acs_kernel_id] -parameter HomeName]] + return [lang::util::localize [parameter::get -package_id $::acs::kernel_id -parameter HomeName]] } ad_proc -public ad_pvt_home_link {} { - @return the html fragment for the /pvt link + @return the HTML fragment for the /pvt link } { - return "[ad_pvt_home_name]" + return "[ad_pvt_home_name]" } ad_proc -public ad_site_home_link {} { @return a link to the user's workspace if the user is logged in. Otherwise, a link to the page root. } { if { [ad_conn user_id] != 0 } { - return "[subsite::get_element -element name]" + return "[subsite::get_element -element name]" } else { # we don't know who this person is - return "[subsite::get_element -element name]" + return "[subsite::get_element -element name]" } } ad_proc -public ad_system_owner {} { Person who owns the service this person would be interested in user feedback, etc. } { - return [parameter::get -package_id [ad_acs_kernel_id] -parameter SystemOwner] + return [parameter::get -package_id $::acs::kernel_id -parameter SystemOwner] } ad_proc -public ad_publisher_name {} { A human-readable name of the publisher, suitable for legal blather. } { - return [parameter::get -package_id [ad_acs_kernel_id] -parameter PublisherName] + return [parameter::get -package_id $::acs::kernel_id -parameter PublisherName] } ad_proc -public ad_url {} { @@ -128,20 +127,22 @@ @see util::configured_location @see util_current_location } { - return [parameter::get -package_id [ad_acs_kernel_id] -parameter SystemURL] + return [parameter::get -package_id $::acs::kernel_id -parameter SystemURL] } ad_proc -public acs_community_member_page {} { - @return the url for the community member page + @return the URL for the community member page } { - return "[subsite::get_element -element url -notrailing][parameter::get \ - -package_id [ad_acs_kernel_id] -parameter CommunityMemberURL]" + set url [parameter::get \ + -package_id $::acs::kernel_id \ + -parameter CommunityMemberURL] + return "[subsite::get_element -element url -notrailing]$url" } ad_proc -public acs_community_member_url { {-user_id:required} } { - @return the url for the community member page of a particular user + @return the URL for the community member page of a particular user } { return [export_vars -base [acs_community_member_page] user_id] } @@ -154,8 +155,8 @@ @see acs_community_member_url } { if {$label eq ""} { - acs_user::get -user_id $user_id -array user - set label "$user(first_names) $user(last_name)" + set user [acs_user::get -user_id $user_id] + set label "[dict get $user first_names] [dict get $user last_name]" } set href [acs_community_member_url -user_id $user_id] return [subst {$label}] @@ -164,9 +165,12 @@ ad_proc -public acs_community_member_admin_url { {-user_id:required} } { - @return the url for the community member admin page of a particular user + @return the URL for the community member admin page of a particular user } { - return [export_vars -base [parameter::get -package_id [ad_acs_kernel_id] -parameter CommunityMemberAdminURL] { user_id }] + set url [parameter::get \ + -package_id $::acs::kernel_id \ + -parameter CommunityMemberAdminURL] + return [export_vars -base $url { user_id }] } ad_proc -public acs_community_member_admin_link { @@ -206,7 +210,7 @@ } { Return a page complaining about the user's input (as opposed to an error in our software, for which ad_return_error - is more appropriate) + is more appropriate) @param exception_count Number of exceptions. Used to say either 'a problem' or 'some problems'. @@ -216,11 +220,17 @@ -package_key "acs-tcl" \ -parameter "ReturnComplaint" \ -default "/packages/acs-tcl/lib/ad-return-complaint"] - ns_return 422 text/html [ad_parse_template \ - -params [list [list exception_count $exception_count] \ - [list exception_text $exception_text]] \ - $complaint_template] + try { + set html [ad_parse_template \ + -params [list [list exception_count $exception_count] \ + [list exception_text $exception_text]] \ + $complaint_template] + } on error {} { + set html [lang::util::localize $exception_text] + } + ns_return 422 text/html $html + # raise abortion flag, e.g., for templating set ::request_aborted [list 422 "Problem with Your Input"] } @@ -243,8 +253,10 @@ -package_key "acs-tcl" \ -parameter "ReturnError" \ -default "/packages/acs-tcl/lib/ad-return-error"] - set page [ad_parse_template -params [list [list title $title] [list explanation $explanation]] $error_template] - if {$status > 399 + set page [ad_parse_template \ + -params [list [list title $title] [list explanation $explanation]] \ + $error_template] + if {$status >= 400 && [string match {*; MSIE *} [ns_set iget [ad_conn headers] User-Agent]] && [string length $page] < 512 } { append page [string repeat " " [expr {513 - [string length $page]}]] @@ -265,7 +277,11 @@ along with the given title and explanation. Should be used when an unexpected error is detected while processing a page. } { - ad_return_exception_page 500 $title $explanation + if {[ns_conn isconnected]} { + ad_return_exception_page 500 $title $explanation + } else { + ns_log error "ad_return_error called without a connection: $title\n$explanation" + } } ad_proc ad_return_warning { @@ -317,10 +333,11 @@ The call_adp_break_p argument is essential if you are calling this from an ADP page and want to avoid the performance hit of continuing to parse and run. - - This proc is dangerous, and needs to be rewritten. See: - http://openacs.org/forums/message-view?message_id=203381 } { + # Note: on AOLServer, ns_server was seemingly dangerous. This + # should not affect NaviServer though, see + # http://openacs.org/forums/message-view?message_id=203381 + # first let's figure out how many are running and queued set this_connection_url [ad_conn url] set n_matches 0 @@ -333,7 +350,8 @@ } } if { $n_matches > $max_simultaneous_copies } { - ad_return_warning "Too many copies" "This is an expensive page for our server, which is already running the same program on behalf of some other users. Please try again at a less busy hour." + ad_return_warning "Too many copies" \ + "This is an expensive page for our server, which is already running the same program on behalf of some other users. Please try again at a less busy hour." # blow out of the caller as well if {$call_adp_break_p} { # we were called from an ADP page; we have to abort processing @@ -345,47 +363,21 @@ return 1 } -ad_proc ad_pretty_mailing_address_from_args { - line1 - line2 - city - state - postal_code - country_code -} { - Returns a prettily formatted address with country name, given - an address. - - @author Unknown - @author Roberto Mello -} { - set lines [list] - if { $line2 eq "" } { - lappend lines $line1 - } elseif { $line1 eq "" } { - lappend lines $line2 - } else { - lappend lines $line1 - lappend lines $line2 - } - lappend lines "$city, $state $postal_code" - if { $country_code ne "" && $country_code ne "us" } { - lappend lines [ad_country_name_from_country_code $country_code] - } - return [join $lines "\n"] -} - - -# for pages that have optional decoration - -ad_proc ad_decorate_top { +ad_proc -deprecated ad_decorate_top { simple_headline potential_decoration } { Use this for pages that might or might not have an image defined in ad.ini; if the second argument isn't the empty string, ad_decorate_top will make a one-row table for the top of the page + + DEPRECATED: use the template system, e.g. master and slave tags to + achieve better control of headers. + + @see /doc/acs-templating/tagref/master + @see /doc/acs-templating/tagref/slave + @see /doc/acs-templating/tagref/include } { if { $potential_decoration eq "" } { return $simple_headline @@ -406,27 +398,38 @@ } if { $package_id eq "" } { - if { [catch { - set package_id [ad_acs_kernel_id] - }] } { - set package_id 0 - } + set package_id $::acs::kernel_id } return $package_id } -ad_proc -public ad_parameter_from_file { +ad_proc -public ad_parameter_from_configuration_file { name {package_key ""} } { - This proc returns the value of a parameter that has been set in the - parameters/ad.ini file. + Return the value of a parameter that has been set in the + configuration file. It is possible to set - Note: The use of the parameters/ad.ini file is discouraged. Some sites - need it to provide instance-specific parameter values that are independent of the contents of the - apm_parameter tables. + Example snippets of the configuration file: +
+       ns_section ns/server/$server/acs {
+           ns_param CSPEnabledP 1
+           ns_param PasswordHashAlgorithm "argon2-12288-3-1 scram-sha-256  salted-sha1"
+       }
+       ns_section ns/server/$server/acs/acs-templating {
+           ns_param UseHtmlAreaForRichtextP 2
+       }
+       ns_section ns/server/$server/acs/xowiki {
+           ns_param MenuBar 1
+       }
+    
+ Note that kernel parameters have no package key included in the + section name of the configuration file (see above). @param name The name of the parameter. + @param package_key package key of the package from + which the parameter value is to be retrieved. When the + package_key is omitted, the kernel parameters are assumed @return The parameter of the object or if it doesn't exist, the default. } { @@ -440,56 +443,232 @@ return [ns_config "ns/server/[ns_info server]/acs/$package_key" $name] } +ad_proc -public -deprecated ad_parameter_from_file { + name + {package_key ""} +} { + Old version of ad_parameter_from_configuration_file -ad_proc -private ad_parameter_cache { - -set - -delete:boolean - -global:boolean - key - parameter_name + @see ad_parameter_from_configuration_file } { + return [ad_parameter_from_configuration_file $name $package_key] +} - Manages the cache for ad_parameter. - @param set Use this flag to indicate a value to set in the cache. - @param delete Delete the value from the cache - @param global If true, global param, false, instance param - @param key Specifies the key for the cache'd parameter, either the package instance - id (instance parameter) or package key (global parameter). - @param parameter_name Specifies the parameter name that is being cached. - @return The cached value. -} { - if {$delete_p} { - if {[nsv_exists ad_param_$key $parameter_name]} { - nsv_unset ad_param_$key $parameter_name + +# +# There are three implementation of "ad_parameter_cache": +# 1) for cachingmode none +# 2) via "nsv_dict" (cluster aware) +# 3) via "nsv" (not cluster aware) + +if {[ns_config "ns/parameters" cachingmode "per-node"] eq "none"} { + # + # If caching mode is "none", the "ad_parameter_cache" is + # essentially a no-op stub, but it is used for interface + # compatibility. + # + # TODO: One should essentially define more more cachetype for + # nsv_caching in acs-cache-procs to reduce redundancy and for + # providing higher orthogonality. + # + ad_proc -public ad_parameter_cache { + -set + -delete:boolean + -global:boolean + key + parameter_name + } { + + Stub for a parameter cache, since "cachingmode" is "none". + + @param set Use this flag to indicate a value to set in the cache. + @param delete Delete the value from the cache + @param global If true, global param, false, instance param + @param key Specifies the key for the cache'd parameter, either the package instance + id (instance parameter) or package key (global parameter). + @param parameter_name Specifies the parameter name that is being cached. + @return The cached value. + + } { + if {$delete_p} { + return } - return + if {[info exists set]} { + return $set + } elseif { $global_p } { + set value [db_string select_global_parameter_value { + select apm_parameter_values.attr_value + from apm_parameters, apm_parameter_values + where apm_parameter_values.package_id is null + and apm_parameter_values.parameter_id = apm_parameters.parameter_id + and apm_parameters.parameter_name = :parameter_name + and apm_parameters.package_key = :key + } -default ""] + } else { + set value [db_string select_instance_parameter_value { + select apm_parameter_values.attr_value + from apm_parameters, apm_parameter_values + where apm_parameter_values.package_id = :key + and apm_parameter_values.parameter_id = apm_parameters.parameter_id + and apm_parameters.parameter_name = :parameter_name + } -default ""] + } + return $value } - if {[info exists set]} { - nsv_set "ad_param_${key}" $parameter_name $set - return $set - } elseif { [nsv_exists ad_param_$key $parameter_name] } { - return [nsv_get ad_param_$key $parameter_name] - } elseif { $global_p } { - set value [db_string select_global_parameter_value { - select apm_parameter_values.attr_value - from apm_parameters, apm_parameter_values - where apm_parameter_values.package_id is null - and apm_parameter_values.parameter_id = apm_parameters.parameter_id - and apm_parameters.parameter_name = :parameter_name - and apm_parameters.package_key = :key - } -default ""] - } else { - set value [db_string select_instance_parameter_value { - select apm_parameter_values.attr_value - from apm_parameters, apm_parameter_values - where apm_parameter_values.package_id = :key - and apm_parameter_values.parameter_id = apm_parameters.parameter_id - and apm_parameters.parameter_name = :parameter_name - } -default ""] + +} elseif {[::acs::icanuse "nsv_dict"]} { + + if {![nsv_array exists ad_param]} { + nsv_set ad_param . . } - nsv_set "ad_param_${key}" $parameter_name $value - return $value + + ad_proc -private ad_parameter_cache_flush_dict { + key + parameter_name + } { + Flush a single value from the nsv cache. + + This proc is necessary in cases, where a node writes a new + parameter value before it has read the old one. + + Since a plain "nsv_dict unset ad_param $key $parameter_name" + raises an exception, when the pair does not exist, and we do + not want to allow in cluster requests arbitrary "catch" + commands, we allow "ad_parameter_cache_flush_dict" instead. + Probably, the best solution is to add support for + + nsv_dict unset -nocomplain -- ad_param $key $parameter_nam + + The existing nsv_dict was built after Tcl's "dict unset", + which does not have the "-nocomplain" option either. However, + an atomic operation would certainly be preferable over an exists/unset + pair, which is no acceptable solution. + + } { + catch {nsv_dict unset ad_param $key $parameter_name} + } + + + ad_proc -public ad_parameter_cache { + -set + -delete:boolean + -global:boolean + key + parameter_name + } { + + Manages the cache for ad_parameter. + @param set Use this flag to indicate a value to set in the cache. + @param delete Delete the value from the cache + @param global If true, global param, false, instance param + @param key Specifies the key for the cache'd parameter, either the package instance + id (instance parameter) or package key (global parameter). + @param parameter_name Specifies the parameter name that is being cached. + @return The cached value. + + } { + if {$delete_p} { + acs::clusterwide ad_parameter_cache_flush_dict $key $parameter_name + acs::per_request_cache flush -pattern acs-tcl.ad_param-$key + return + } + if {[info exists set]} { + nsv_dict set ad_param $key $parameter_name $set + acs::per_request_cache flush -pattern acs-tcl.ad_param-$key + return $set + } + # + # Keep the parameter dict in a per-request cache to reduce + # potentially high number of nsv locks, when parameters of a + # package are queried a high number of times per request + # (without this we see on some sites > 100 locks on this nsv + # per request). + # + set dict [acs::per_request_cache eval -no_cache "" -key acs-tcl.ad_param-$key { + if {[nsv_get ad_param $key result]} { + #ns_log notice "ad_parameter_cache $key $parameter_name not cached" + set result + } else { + set result "" + } + }] + if {[dict exists $dict $parameter_name]} { + #ns_log notice "ad_parameter_cache $key $parameter_name get from dict" + return [dict get $dict $parameter_name] + } + if { $global_p } { + set value [db_string select_global_parameter_value { + select apm_parameter_values.attr_value + from apm_parameters, apm_parameter_values + where apm_parameter_values.package_id is null + and apm_parameter_values.parameter_id = apm_parameters.parameter_id + and apm_parameters.parameter_name = :parameter_name + and apm_parameters.package_key = :key + } -default ""] + } else { + set value [db_string select_instance_parameter_value { + select apm_parameter_values.attr_value + from apm_parameters, apm_parameter_values + where apm_parameter_values.package_id = :key + and apm_parameter_values.parameter_id = apm_parameters.parameter_id + and apm_parameters.parameter_name = :parameter_name + } -default ""] + } + nsv_dict set ad_param $key $parameter_name $value + return $value + } +} else { + ad_proc -public ad_parameter_cache { + -set + -delete:boolean + -global:boolean + key + parameter_name + } { + + Manages the cache for ad_parameter. + @param set Use this flag to indicate a value to set in the cache. + @param delete Delete the value from the cache + @param global If true, global param, false, instance param + @param key Specifies the key for the cache'd parameter, either the package instance + id (instance parameter) or package key (global parameter). + @param parameter_name Specifies the parameter name that is being cached. + @return The cached value. + + } { + if {$delete_p} { + if {[nsv_exists ad_param_$key $parameter_name]} { + nsv_unset ad_param_$key $parameter_name + } + return + } + if {[info exists set]} { + nsv_set "ad_param_${key}" $parameter_name $set + return $set + } elseif { [nsv_exists ad_param_$key $parameter_name] } { + return [nsv_get ad_param_$key $parameter_name] + } elseif { $global_p } { + set value [db_string select_global_parameter_value { + select apm_parameter_values.attr_value + from apm_parameters, apm_parameter_values + where apm_parameter_values.package_id is null + and apm_parameter_values.parameter_id = apm_parameters.parameter_id + and apm_parameters.parameter_name = :parameter_name + and apm_parameters.package_key = :key + } -default ""] + } else { + set value [db_string select_instance_parameter_value { + select apm_parameter_values.attr_value + from apm_parameters, apm_parameter_values + where apm_parameter_values.package_id = :key + and apm_parameter_values.parameter_id = apm_parameters.parameter_id + and apm_parameters.parameter_name = :parameter_name + } -default ""] + } + nsv_set "ad_param_${key}" $parameter_name $value + return $value + } } ad_proc -private ad_parameter_cache_all {} { @@ -505,16 +684,22 @@ } } -# returns particular parameter values as a Tcl list (i.e., it selects -# out those with a certain key) - -ad_proc -public ad_parameter_all_values_as_list { +ad_proc -deprecated ad_parameter_all_values_as_list { {-package_id ""} name {subsection ""} } { Returns multiple values for a parameter as a list. + DEPRECATED: this proc does not do much that joining a string + coming from a parameter, which does not make an invalid string + into a list. Best to take the value from the parameter directly + and rely on proper quoting by the user. Furthermore, the + 'subsection' argument is not used anywhere. + + @see parameter::get + @see join + } { return [join [parameter::get -package_id $package_id -parameter $name ] " "] } @@ -535,20 +720,22 @@ ad_proc -public ad_return_url { -urlencode:boolean + {-path_encode:boolean true} -qualified:boolean {-default_url .} {extra_args ""} } { Build a return url suitable for passing to a page you expect to return back - to the current page. + to the current page. Per default, the result is URL-encoded + (like the result of "export_vars" or ":pretty_link").

Example for direct inclusion in a link:

-    ad_returnredirect "foo?return_url=[ad_return_url -url_encode]"
+    ad_returnredirect "foo?return_url=[ad_return_url]"
     
Example setting a variable to be used by export_vars: @@ -561,40 +748,53 @@ Example setting a variable with extra_vars:
-    set return_url [ad_return_url [list some_id $some_id] [some_other_id $some_other_id]]
+    set return_url [ad_return_url [list [list some_id $some_id] [list some_other_id $some_other_id]]]
     
@author Don Baccus (dhogaza@pacifier.com) - @param urlencode If true URL-encode the result + @param path_encode If false do no URL-encode the result @param default_url When there is no connection, fall back to this URL @param qualified If provided the return URL will be fully qualified including http or https. @param extra_args A list of {name value} lists to append to the query string } { + if { $urlencode_p } { + ns_log warning "deprecated flag -urlencode; result is encoded per default" + } + if {[ns_conn isconnected]} { - set query_list [export_entire_form_as_url_vars] - if { [llength $query_list] == 0 } { - set url [ns_conn url] - } else { - set url "[ns_conn url]?[join $query_list &]" - } - if { $qualified_p } { - # Make the return_url fully qualified - set url [security::get_qualified_url $url] - } + set query_list [export_vars -entire_form] + set base_url [ns_conn url] } else { set query_list "" - set url $default_url + set base_url $default_url } - foreach {extra_arg} $extra_args { - lappend query_list [join $extra_arg "="] + + if { $path_encode_p } { + set base_url [ns_urlencode $base_url] } - if { $urlencode_p } { - set url [ns_urlencode $url] + if { [llength $query_list] == 0 } { + set url $base_url + } else { + set url "${base_url}?[join $query_list &]" } + + if {[llength $extra_args] > 0} { + # + # Deactivate base encode, since the input URL is already + # encoded as requested. + # + set url [export_vars -base $url -no_base_encode $extra_args] + } + + if { $qualified_p } { + # Make the return_url fully qualified + set url [security::get_qualified_url $url] + } + return $url } @@ -624,7 +824,7 @@ db_release_unused_handles ad_http_cache_control - ReturnHeaders + util_return_headers ns_write [ad_parse_template \ -params [list \ [list doc(title) $title] \ @@ -643,7 +843,7 @@ @see ad_progress_bar_begin } { util_user_message -message $message_after_redirect - ns_write "" + ns_write "" ns_conn close }