Index: openacs-4/packages/acs-tcl/tcl/site-nodes-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/site-nodes-procs.tcl,v diff -u -r1.93.2.13 -r1.93.2.14 --- openacs-4/packages/acs-tcl/tcl/site-nodes-procs.tcl 10 Nov 2016 15:23:10 -0000 1.93.2.13 +++ openacs-4/packages/acs-tcl/tcl/site-nodes-procs.tcl 9 Jan 2017 16:49:27 -0000 1.93.2.14 @@ -22,7 +22,7 @@ # acs-core procs. So, the implementation checks, if the installation # fullfills the requirements of the new code, if not, it falls back to # the classical implementation. -# +# # Some timings: # simple installation: # nsv-based get_children: 291 microseconds @@ -133,11 +133,46 @@ ad_proc -public site_node::delete { {-node_id:required} + -delete_subnodes:boolean } { delete the site node -} { - db_exec_plsql delete_site_node {} - update_cache -node_id $node_id +} { + if {!$delete_subnodes_p} { + set n_subnodes [llength [site_node::get_children \ + -node_id $node_id]] + if {$n_subnodes != 0} { + error "Site node has subnodes. To force use -delete_subnodes option" + } + } + + set nodes_to_delete {} + + # breadth-first visit of the node tree, so we can delete children + # starting from leaves, then their parents and so on to the top + # (and thus not triggering reference constraint errors) + set queue [list $node_id] + while {$queue ne ""} { + set parent_id [lindex $queue 0] + lappend nodes_to_delete $parent_id + set queue [lrange $queue 1 end] + set queue [concat $queue [site_node::get_children \ + -element "node_id" \ + -node_id $parent_id]] + } + + # delete nodes in reverse order, starting from leaves + set i [expr {[llength $nodes_to_delete] - 1}] + while {$i >= 0} { + set node_id [lindex $nodes_to_delete $i] + # first delete package_id under this node... + set package_id [site_node::get_object_id \ + -node_id $node_id] + apm_package_instance_delete $package_id + # ...then the node itself + db_exec_plsql delete_site_node {} + update_cache -node_id $node_id + incr i -1 + } } ad_proc -public site_node::mount { @@ -155,11 +190,11 @@ with_finally -code { #Now update the nsv caches. array set node [site_node::get_from_node_id -node_id $node_id] - + foreach var [list object_type package_key package_id instance_name package_type] { set $var "" } - + db_0or1row get_package_info { select 'apm_package' as object_type, p.package_key, @@ -170,14 +205,14 @@ where p.package_id = :object_id and t.package_key = p.package_key } - + nsv_set site_nodes $node(url) \ [list url $node(url) node_id $node(node_id) parent_id $node(parent_id) name $node(name) \ directory_p $node(directory_p) pattern_p $node(pattern_p) \ object_id $object_id object_type $object_type \ package_key $package_key package_id $package_id \ instance_name $instance_name package_type $package_type] - + set url_by_object_id [list $node(url)] if { [nsv_exists site_node_url_by_object_id $object_id] } { set url_by_object_id [concat [nsv_get site_node_url_by_object_id $object_id] $url_by_object_id] @@ -187,7 +222,7 @@ $url_by_object_id] } nsv_set site_node_url_by_object_id $object_id $url_by_object_id - + if { $package_key ne "" } { set url_by_package_key [list $node(url)] if { [nsv_exists site_node_url_by_package_key $package_key] } { @@ -258,7 +293,7 @@ @param package_id The id of the new package. Optional. @return The id of the instantiated package - + @author Peter Marklund } { # Create a new node if none was provided and none exists @@ -275,7 +310,7 @@ # Create the node if it doesn't exists set parent_url [get_url -notrailing -node_id $parent_node_id] - set url "${parent_url}/${node_name}" + set url "${parent_url}/${node_name}" if { ![exists_p -url $url] } { set node_id [site_node::new -name $node_name -parent_id $parent_node_id] @@ -357,19 +392,19 @@ ns_mutex lock [nsv_get site_nodes_mutex mutex] with_finally -code { - - # Lars: We need to record the object_id's touched, so we can sort the - # object_id->url mappings again. We store them sorted by length of the URL + + # Lars: We need to record the object_id's touched, so we can sort the + # object_id->url mappings again. We store them sorted by length of the URL if { [nsv_exists site_node_url_by_node_id $node_id] } { set old_url [nsv_get site_node_url_by_node_id $node_id] if { $sync_children_p } { append old_url * } - + # This is a little cumbersome, but we have to remove the entry for - # the object_id->url mapping, for each object_id that used to be + # the object_id->url mapping, for each object_id that used to be # mounted here - + # Loop over all the URLs under the node we're updating set cur_nodes [nsv_array get site_nodes $old_url] foreach {cur_node_url curr_node_values} $cur_nodes { @@ -386,7 +421,7 @@ nsv_set site_node_url_by_object_id $cur_object_id $cur_url_by_object_id } } - + # Find the package_key previously mounted there set cur_package_key $cur_node(package_key) if { $cur_package_key ne "" } { @@ -412,7 +447,7 @@ } else { set query_name select_site_node } - + set cur_obj_ids [list] db_foreach $query_name {} { if {$parent_id eq ""} { @@ -472,7 +507,7 @@ either url or node_id is required, if both are passed url is ignored - The array elements are: package_id, package_key, object_type, directory_p, + The array elements are: package_id, package_key, object_type, directory_p, instance_name, pattern_p, parent_id, node_id, object_id, url. } { if {$url eq "" && $node_id eq ""} { @@ -498,7 +533,7 @@ either url or node_id is required, if both are passed url is ignored - The array elements are: package_id, package_key, object_type, directory_p, + The array elements are: package_id, package_key, object_type, directory_p, instance_name, pattern_p, parent_id, node_id, object_id, url. @see site_node::get @@ -511,7 +546,7 @@ {-node_id:required} } { returns an array representing the site node for the given node_id - + @see site_node::get } { return [get_from_url -url [get_url -node_id $node_id]] @@ -572,7 +607,7 @@ } { set url_no_trailing [string trimright $url "/"] return [nsv_exists site_nodes "$url_no_trailing/"] -} +} ad_proc -public site_node::get_from_object_id { {-object_id:required} @@ -587,7 +622,7 @@ ad_proc -public site_node::get_all_from_object_id { {-object_id:required} } { - Return a list of site node info associated with the given object_id. + Return a list of site node info associated with the given object_id. The nodes will be ordered descendingly by url (children before their parents). } { set node_id_list [list] @@ -613,7 +648,7 @@ if {[nsv_exists site_node_url_by_node_id $node_id]} { set url [nsv_get site_node_url_by_node_id $node_id] } - + if { $notrailing_p } { set url [string trimright $url "/"] } @@ -696,7 +731,7 @@ while {$node_id ne "" } { array set node [get -node_id $node_id] - + if {$array_result_p} { lappend result [array get node] } else { @@ -705,7 +740,7 @@ set node_id $node(parent_id) } - + return $result } @@ -726,34 +761,34 @@ {-element {}} {-node_id:required} } { - This proc gives answers to questions such as: What are all the package_id's + This proc gives answers to questions such as: What are all the package_id's (or any of the other available elements) for all the instances of package_key or package_type mounted under node_id xxx? @param node_id The node for which you want to find the children. @option all Set this if you want all children, not just direct children - + @option package_type If specified, this will limit the returned nodes to those with an - package of the specified package type (normally apm_service or + package of the specified package type (normally apm_service or apm_application) mounted. Conflicts with the -package_key option. - + @param package_key If specified, this will limit the returned nodes to those with a package of the specified package key mounted. Conflicts with the -package_type option. Can take one or more packges keys as a Tcl list. - @param filters Takes a list of { element value element value ... } for filtering - the result list. Only nodes where element is value for each of the - filters in the list will get included. For example: + @param filters Takes a list of { element value element value ... } for filtering + the result list. Only nodes where element is value for each of the + filters in the list will get included. For example: -filters { package_key "acs-subsite" }. - - @param element The element of the site node you wish returned. Defaults to url, but + + @param element The element of the site node you wish returned. Defaults to url, but the following elements are available: object_type, url, object_id, instance_name, package_type, package_id, name, node_id, directory_p. - - @return A list of URLs of the site_nodes immediately under this site node, or all children, + + @return A list of URLs of the site_nodes immediately under this site node, or all children, if the -all switch is specified. - + @author Lars Pind (lars@collaboraid.biz) } { if { $package_type ne "" && $package_key ne "" } { @@ -768,11 +803,11 @@ set node_url [site_node::get_url -node_id $node_id] - if { !$all_p } { + if { !$all_p } { set child_urls [list] set s [string length "$node_url"] # find all child_urls who have only one path element below node_id - # by clipping the node url and last character and seeing if there + # by clipping the node url and last character and seeing if there # is a / in the string. about 2x faster than the RE version. foreach child_url [nsv_array names site_nodes "${node_url}?*"] { if { [string first / [string range $child_url $s end-1]] < 0 } { @@ -822,7 +857,7 @@ } } - # if we had filters or were getting a particular element then we + # if we had filters or were getting a particular element then we # have our results in return_val otherwise it's just urls if { $element ne "" || [llength $filters] > 0} { @@ -841,21 +876,21 @@ } { Starting with the node at with given id, or at given url, climb up the site map and return the id of the first not-null - mounted object. If no ancestor object is found the empty string is + mounted object. If no ancestor object is found the empty string is returned. - Will ignore itself and only return true ancestors unless + Will ignore itself and only return true ancestors unless include_self is set. - @param url The url of the node to start from. You must provide - either url or node_id. An empty url is taken to mean + @param url The url of the node to start from. You must provide + either url or node_id. An empty url is taken to mean the main site. - @param node_id The id of the node to start from. Takes precedence + @param node_id The id of the node to start from. Takes precedence over any provided url. - @param package_key Restrict search to objects of this package type. You + @param package_key Restrict search to objects of this package type. You may supply a list of package_keys. - @param include_self Return the package_id at the passed-in node if it is - of the desired package_key. Ignored if package_key is + @param include_self Return the package_id at the passed-in node if it is + of the desired package_key. Ignored if package_key is empty. @return The id of the first object found and an empty string if no object @@ -886,7 +921,7 @@ # move up a level set url [string trimright $url /] set url [string range $url 0 [string last / $url]] - + array set node_array [site_node::get -url $url] # are we looking for a specific package_key? @@ -899,15 +934,15 @@ return $elm_value -} +} ad_proc -public site_node::get_package_url { {-package_key:required} } { Get the URL of any mounted instance of a package with the given package_key. If there is more than one mounted instance of a package, returns - the first URL. To see all of the mounted URLs, use the + the first URL. To see all of the mounted URLs, use the site_node::get_children proc. @return a URL, or empty string if no instance of the package is mounted. @@ -933,7 +968,7 @@ Returns folder name to use, or empty string if the supplied folder name wasn't acceptable. } { set existing_urls [site_node::get_children -node_id $parent_node_id -element name] - + array set parent_node [site_node::get -node_id $parent_node_id] if { $parent_node(package_key) ne "" } { # Find all the page or directory names under this package @@ -949,7 +984,7 @@ lappend existing_urls $name } } - } + } if { $folder ne "" } { if { $folder in $existing_urls } { @@ -1052,10 +1087,10 @@ package_keys } {

- Use site_node::closest_ancestor_package. Note that - site_node_closest_ancestor_package will include the passed-in node in the - search, whereas the new proc doesn't by default. If you want to include - the passed-in node, call site_node::closest_ancestor_package with the + Use site_node::closest_ancestor_package. Note that + site_node_closest_ancestor_package will include the passed-in node in the + search, whereas the new proc doesn't by default. If you want to include + the passed-in node, call site_node::closest_ancestor_package with the -include_self flag

@@ -1098,7 +1133,7 @@ return $node(package_id) } } - + # Add a trailing slash and try again. if {[string index $url end] ne "/"} { append url "/" @@ -1109,23 +1144,23 @@ } } } - + # Try successively shorter prefixes. while {$url ne ""} { # Chop off last component and try again. set url [string trimright $url /] set url [string range $url 0 [string last / $url]] - + if {[catch {nsv_get site_nodes $url} result] == 0} { array set node $result - if {$node(pattern_p) == "t" - && $node(object_id) ne "" + if {$node(pattern_p) == "t" + && $node(object_id) ne "" && $node(package_key) in $package_keys} { return $node(package_id) } } } - + return $default } @@ -1210,7 +1245,7 @@ ::nx::Class create SiteNode { # - # @method get + # @method get # returns a site node from url or site-node with all its context info # @@ -1228,65 +1263,65 @@ if {$node_id eq ""} { set node_id [:get_node_id -url $url] } - + return [:properties -node_id $node_id] } # - # @method properties + # @method properties # returns a site node from node_id with all its context info # :protected method properties { -node_id:integer,required } { # - # Get url, since it is not returned by the later query. - + # Get url, since it is not returned by the later query. + # TODO: I did not want to modifiy the query for the time # being. When doing the Oracle support, the retrieval of the URL # should be moved into the query below.... # set url [:get_url -node_id $node_id] - + # # get site-node with context from the database # ::db_1row dbqd.acs-tcl.tcl.site-nodes-procs.site_node::update_cache.select_site_node {} - - set object_type [expr {$package_id eq "" ? "" : "apm_package"}] + + set object_type [expr {$package_id eq "" ? "" : "apm_package"}] set list [list url $url node_id $node_id parent_id $parent_id name $name \ directory_p $directory_p pattern_p $pattern_p object_id $object_id \ object_type $object_type package_key $package_key package_id $package_id \ instance_name $instance_name package_type $package_type] return $list } - + # - # @method get_children + # @method get_children # get children of a site node # :public method get_children { - -node_id:required + -node_id:required -all:switch - {-package_type ""} + {-package_type ""} {-package_key ""} - {-filters ""} + {-filters ""} {-element ""} } { # - # Fitering happens here exactly like in the nsv-based version. If should be possible to + # Fitering happens here exactly like in the nsv-based version. If should be possible to # realize (at least some of the) filtering via the SQL query # if {$all} { # # the following query is just for PG, TODO: Oracle is missing # set child_urls [::xo::dc list [current method]-all { - select site_node__url(children.node_id) + select site_node__url(children.node_id) from site_nodes as parent, site_nodes as children - where parent.node_id = :node_id + where parent.node_id = :node_id and children.tree_sortkey between parent.tree_sortkey and tree_right(parent.tree_sortkey) and children.tree_sortkey <> parent.tree_sortkey }] @@ -1309,19 +1344,19 @@ -map_function_names true] set child_urls [::xo::dc list [current method] $sql] } - + if { $package_type ne "" } { lappend filters package_type $package_type } elseif { $package_key ne "" } { lappend filters package_key $package_key } - + if { [llength $filters] > 0 } { set return_val [list] foreach child_url $child_urls { array unset site_node if {![catch {array set site_node [:get -url $child_url]}]} { - + set passed_p 1 foreach { elm val } $filters { if { $site_node($elm) ne $val } { @@ -1349,13 +1384,13 @@ } else { set return_val $child_urls } - + return $return_val } - - + + # - # @method get_urls_from_object_id + # @method get_urls_from_object_id # # returns a list of urls for site_nodes that have the given # object mounted or the empty list if there are none. The urls @@ -1364,26 +1399,26 @@ # deleting site nodes as we must delete child site nodes before # their parents. # - + :public method get_urls_from_object_id { - -object_id:required + -object_id:required } { # # the following query is just for PG, TODO: Oracle is missing # set child_urls [::xo::dc list [current method]-all { - select site_node__url(node_id) - from site_nodes + select site_node__url(node_id) + from site_nodes where object_id = :object_id order by tree_sortkey desc }] } - + :public method get_urls_from_package_key { - -package_key:required + -package_key:required } { return [::xo::dc list [current method]-urls-from-package-key { - select site_node__url(node_id) + select site_node__url(node_id) from site_nodes n, apm_packages p where p.package_key = :package_key and n.object_id = p.package_id @@ -1392,7 +1427,7 @@ # # @method get_node_id get_package_url - # just a small stub for a special case for method + # just a small stub for a special case for method # get_urls_from_package_key # :public method get_package_url { @@ -1402,7 +1437,7 @@ } # - # @method get_node_id + # @method get_node_id # obtain node id from url, using directly the stored procedure # site_node.node_id # @@ -1424,7 +1459,7 @@ # a stub to be overloaded by the cache manager # :public method flush_cache {{-node_id ""} {-with_subtree:boolean}} {;} - + # Create an object "site_node" to provide a user-interface close # to the classical one. :create site_node @@ -1454,7 +1489,7 @@ ::nx::Class create SiteNodeCache { :public method get_children { - -node_id:required + -node_id:required {-all:switch} {-package_type ""} {-package_key ""} {-filters ""} {-element ""} } { ns_cache_eval xo_site_nodes get_children-$node_id-$all-$package_type-$package_key-$filters-$element { next } @@ -1498,7 +1533,7 @@ } :public method flush_cache {{-node_id ""} {-with_subtree:boolean true}} { - # + # # Flush entries from site-node tree, including the current node, # the root of flushed (sub)tree. If the node_id is not provided, # or it is the node_id of root of the full site-node tree, flush @@ -1519,7 +1554,7 @@ set tree [::xo::dc list_of_lists [current method]-flush-tree " select site_node__url(children.node_id), children.node_id, children.object_id from site_nodes as parent, site_nodes as children - where parent.node_id = :node_id + where parent.node_id = :node_id and children.tree_sortkey between parent.tree_sortkey and tree_right(parent.tree_sortkey) $limit_clause "] @@ -1567,11 +1602,11 @@ [list parent_id $parent_id] \ [list directory_p $directory_p] \ [list pattern_p $pattern_p]] - + set node_id [package_instantiate_object -var_list $var_list site_node] return $node_id } - + ad_proc -public site_node::mount { {-node_id:required} {-object_id:required} @@ -1585,7 +1620,7 @@ # We might have for this node_id (or under it) some entries in the # cache, so flush these first. site_node::update_cache -sync_children -node_id $node_id - + # DAVEB update context_id if it is passed in # some code relies on context_id to be set by # instantiate_and_mount so we can't assume @@ -1606,7 +1641,7 @@ } ad_proc -private site_node::init_cache {} { - Initialize the site node cache; actually, this means flushing the + Initialize the site node cache; actually, this means flushing the cache in case we have root site node. } { ns_log notice "site_node::init_cache" @@ -1638,7 +1673,7 @@ } { Returns an array representing the site node that matches the given url. Either url or node_id is required, if both are passed url is ignored. - The array elements are: package_id, package_key, object_type, directory_p, + The array elements are: package_id, package_key, object_type, directory_p, instance_name, pattern_p, parent_id, node_id, object_id, url. } { return [::xo::site_node get -url $url -node_id $node_id] @@ -1714,32 +1749,32 @@ {-element {}} {-node_id:required} } { - This proc gives answers to questions such as: What are all the package_id's + This proc gives answers to questions such as: What are all the package_id's (or any of the other available elements) for all the instances of package_key or package_type mounted under node_id xxx? @param node_id The node for which you want to find the children. @option all Set this if you want all children, not just direct children - + @option package_type If specified, this will limit the returned nodes to those with an - package of the specified package type (normally apm_service or + package of the specified package type (normally apm_service or apm_application) mounted. Conflicts with the -package_key option. - + @param package_key If specified, this will limit the returned nodes to those with a package of the specified package key mounted. Conflicts with the -package_type option. Can take one or more packges keys as a Tcl list. - @param filters Takes a list of { element value element value ... } for filtering - the result list. Only nodes where element is value for each of the - filters in the list will get included. For example: + @param filters Takes a list of { element value element value ... } for filtering + the result list. Only nodes where element is value for each of the + filters in the list will get included. For example: -filters { package_key "acs-subsite" }. - - @param element The element of the site node you wish returned. Defaults to url, but + + @param element The element of the site node you wish returned. Defaults to url, but the following elements are available: object_type, url, object_id, instance_name, package_type, package_id, name, node_id, directory_p. - - @return A list of URLs of the site_nodes immediately under this site node, or all children, + + @return A list of URLs of the site_nodes immediately under this site node, or all children, if the -all switch is specified. } { ::xo::site_node get_children -all=$all_p -package_type $package_type -package_key $package_key \ @@ -1752,7 +1787,7 @@ Get the URL of any mounted instance of a package with the given package_key. If there is more than one mounted instance of a package, returns - the first URL. To see all of the mounted URLs, use the + the first URL. To see all of the mounted URLs, use the site_node::get_children proc. @return a URL, or empty string if no instance of the package is mounted. @@ -1767,10 +1802,10 @@ package_keys } {

- Use site_node::closest_ancestor_package. Note that - site_node_closest_ancestor_package will include the passed-in node in the - search, whereas the new proc doesn't by default. If you want to include - the passed-in node, call site_node::closest_ancestor_package with the + Use site_node::closest_ancestor_package. Note that + site_node_closest_ancestor_package will include the passed-in node in the + search, whereas the new proc doesn't by default. If you want to include + the passed-in node, call site_node::closest_ancestor_package with the -include_self flag

@@ -1821,4 +1856,3 @@ # tcl-indent-level: 4 # indent-tabs-mode: nil # End: -