Index: openacs-4/packages/xotcl-core/tcl/cr-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xotcl-core/tcl/cr-procs.tcl,v diff -u -r1.54 -r1.55 --- openacs-4/packages/xotcl-core/tcl/cr-procs.tcl 22 Jul 2015 19:13:39 -0000 1.54 +++ openacs-4/packages/xotcl-core/tcl/cr-procs.tcl 7 Aug 2017 23:48:30 -0000 1.55 @@ -17,6 +17,7 @@ {mime_type text/plain} {storage_type "text"} {folder_id -100} + {non_cached_instance_var_patterns {__*}} } -ad_doc {

The meta class CrClass serves for a class of applications that mostly store information in the content repository and that use a few @@ -35,7 +36,7 @@

Each content item can be retrieved either through the general method - + CrClass get_instance_from_db or through the "get_instance_from_db" method of every subclass of CrItem. @@ -48,29 +49,38 @@ # CrClass ad_proc get_object_type { - -item_id - {-revision_id 0} + -item_id:integer,required + {-revision_id:integer 0} } { Return the object type for an item_id or revision_id. @return object_type typically an XOTcl class } { - set object_type [ns_cache eval xotcl_object_type_cache \ - [expr {$item_id ? $item_id : $revision_id}] { - if {$item_id} { - ::xo::dc 1row get_class_from_item_id \ - "select content_type as object_type from cr_items where item_id=:item_id" - } else { - ::xo::dc 1row get_class_from_revision_id \ - "select object_type from acs_objects where object_id=:revision_id" - } - return $object_type - }] + # + # Use a request-spanning cache. When the type whould changes, we + # would require xo::broadcast or server restart. + # + set key ::xo::object_type($item_id,$revision_id) + if {[info exists $key]} { + return [set $key] + } + set $key [ns_cache eval xotcl_object_type_cache \ + [expr {$item_id ? $item_id : $revision_id}] { + if {$item_id} { + ::xo::dc 1row get_class_from_item_id \ + "select content_type as object_type from cr_items where item_id=:item_id" + } else { + ::xo::dc 1row get_class_from_revision_id \ + "select object_type from acs_objects where object_id=:revision_id" + } + return $object_type + }] } CrClass ad_proc get_instance_from_db { - {-item_id 0} - {-revision_id 0} + {-item_id:integer 0} + {-revision_id:integer 0} + {-initialize:boolean true} } { Instantiate the live revision or the specified revision of an CrItem. The XOTcl object is destroyed automatically on cleanup @@ -80,7 +90,7 @@ } { set object_type [my get_object_type -item_id $item_id -revision_id $revision_id] set class [::xo::db::Class object_type_to_class $object_type] - return [$class get_instance_from_db -item_id $item_id -revision_id $revision_id] + return [$class get_instance_from_db -item_id $item_id -revision_id $revision_id -initialize $initialize] } CrClass ad_proc get_parent_id { @@ -409,7 +419,7 @@ -item_id:required {-revision_id 0} -object:required - {-initialize true} + {-initialize:boolean true} } { Load a content item into the specified object. If revision_id is provided, the specified revision is returned, otherwise the live @@ -464,19 +474,27 @@ } if {$revision_id} { $object set revision_id $revision_id - $object db_1row [my qn fetch_from_view_revision_id] "\ - select [join $atts ,], i.parent_id \ - from [my set table_name]i n, cr_items i,acs_objects o \ - where n.revision_id = :revision_id \ - and i.item_id = n.item_id \ - and o.object_id = n.revision_id" + + db_with_handle db { + set sql [::xo::dc prepare -handle $db -argtypes integer "\ + select [join $atts ,], i.parent_id \ + from [my set table_name]i n, cr_items i,acs_objects o \ + where n.revision_id = :revision_id \ + and i.item_id = n.item_id \ + and o.object_id = n.revision_id"] + + set selection [db_exec 1row $db dbqd..cr-procs-fetch_object-from-revision_id $sql] + } + $object mset [ns_set array $selection] + } else { # We fetch the creation_user and the modifying_user by returning the # creation_user of the automatic view as modifying_user. In case of # troubles, comment next line out. lappend atts "n.creation_user as modifying_user" $object set item_id $item_id + $object db_1row [my qn fetch_from_view_item_id] "\ select [join $atts ,], i.parent_id \ from [my set table_name]i n, cr_items i, acs_objects o \ @@ -502,6 +520,7 @@ CrClass ad_instproc get_instance_from_db { {-item_id 0} {-revision_id 0} + {-initialize:boolean true} } { Retrieve either the live revision or a specified revision of a content item with all attributes into a newly created object. @@ -516,7 +535,8 @@ set object ::[expr {$revision_id ? $revision_id : $item_id}] if {![my isobject $object]} { my fetch_object -object $object \ - -item_id $item_id -revision_id $revision_id + -item_id $item_id -revision_id $revision_id \ + -initialize $initialize $object destroy_on_cleanup } return $object @@ -533,7 +553,7 @@ my get_context package_id creation_user creation_ip #my log "ID [self] create $args" if {[catch {set p [my create ::0 {*}$args]} errorMsg]} { - my log "Error: $errorMsg, $::errorInfo" + ad_log error $errorMsg } #my log "ID [::0 serialize]" set item_id [::0 save_new \ @@ -620,7 +640,9 @@ lappend cond "coalesce(ci.live_revision,ci.latest_revision) = bt.revision_id" if {$parent_id ne ""} { if {$with_children} { - lappend cond "ci.parent_id in (select $parent_id from dual union select item_id from cr_items where parent_id = $parent_id)" + append from_clause ", (select $parent_id as item_id from dual union \ + select item_id from cr_items where parent_id = $parent_id) children" + lappend cond "ci.parent_id = children.item_id" } else { lappend cond "ci.parent_id = $parent_id" } @@ -654,6 +676,7 @@ {-page_size 20} {-page_number ""} {-base_table "cr_revisions"} + {-initialize true} } { Returns a set (ordered composite) of the answer tuples of an 'instance_select_query' with the same attributes. @@ -671,7 +694,8 @@ -page_size $page_size \ -page_number $page_number \ -base_table $base_table \ - ]] + ] \ + -initialize $initialize] return $s } @@ -892,7 +916,7 @@ #my instvar creation_user set __atts [list creation_user] set __vars $__atts - + # The modifying_user is not maintained by the CR (bug?) # xotcl-core handles this by having the modifying user as # creation_user of the revision. @@ -918,11 +942,28 @@ lappend __vars $__slot_name } + if {$use_given_publish_date} { + if {"publish_date" ni $__atts} { + my instvar publish_date + lappend __atts publish_date + lappend __vars publish_date + } + set publish_date_flag [list -publish_date $publish_date] + } else { + set publish_date_flag "" + } + ::xo::dc transaction { [my info class] instvar storage_type set revision_id [xo::dc nextval acs_object_id_seq] if {$storage_type eq "file"} { - my instvar import_file + my instvar import_file mime_type name + # Get the mime_type from the file, eventually creating a new + # one if it's unrecognized. + set mime_type [cr_check_mime_type \ + -mime_type $mime_type \ + -filename $name \ + -file $import_file] set text [cr_create_content_file $item_id $revision_id $import_file] } ::xo::dc [::xo::dc insert-view-operation] revision_add \ @@ -931,17 +972,15 @@ my fix_content $revision_id $text if {$live_p} { - ::xo::db::sql::content_item set_live_revision \ - -revision_id $revision_id \ - -publish_status [my set publish_status] # - # set_live revision updates publish_date to the current date. - # In order to keep a given publish date, we have to update the - # field manually. + # Update the life revision with the publish status and + # optionally the publish_date # - if {$use_given_publish_date} { - my update_revision $revision_id publish_date [my publish_date] - } + ::xo::db::sql::content_item set_live_revision \ + -revision_id $revision_id \ + -publish_status [my set publish_status] \ + -is_latest true \ + {*}$publish_date_flag my set revision_id $revision_id my update_item_index } else { @@ -956,34 +995,20 @@ return $item_id } - if {[apm_version_names_compare [ad_acs_version] 5.2] > -1} { - ns_log notice "--OpenACS Version 5.2 or newer [ad_acs_version]" - CrItem set content_item__new_args { - -name $name -parent_id $parent_id -creation_user $creation_user \ - -creation_ip $creation_ip \ - -item_subtype "content_item" -content_type $object_type \ - -description $description -mime_type $mime_type -nls_language $nls_language \ - -is_live f -storage_type $storage_type -package_id $package_id - } - } else { - ns_log notice "--OpenACS Version 5.1 or older [ad_acs_version]" - CrItem set content_item__new_args { - -name $name -parent_id $parent_id -creation_user $creation_user \ - -creation_ip $creation_ip \ - -item_subtype "content_item" -content_type $object_type \ - -description $description -mime_type $mime_type -nls_language $nls_language \ - -is_live f -storage_type $storage_type - } - } - - CrItem ad_instproc set_live_revision {-revision_id:required {-publish_status "ready"}} { + CrItem ad_instproc set_live_revision { + -revision_id:required + {-publish_status "ready"} + {-is_latest:boolean false} + } { @param revision_id @param publish_status one of 'live', 'ready' or 'production' } { ::xo::db::sql::content_item set_live_revision \ -revision_id $revision_id \ - -publish_status $publish_status + -publish_status $publish_status \ + -is_latest $is_latest ::xo::clusterwide ns_cache flush xotcl_object_cache ::[my item_id] + ::xo::clusterwide ns_cache flush xotcl_object_cache ::$revision_id } CrItem ad_instproc update_item_index {} { @@ -1005,8 +1030,10 @@ @param creation_user user_id if the creating user @param live_p make this revision the live revision } { + set __class [my info class] my instvar parent_id item_id import_file name + if {![info exists package_id] && [my exists package_id]} { set package_id [my package_id] } @@ -1028,6 +1055,17 @@ lappend __atts [$__slot column_name] lappend __vars $__slot_name } + + if {$use_given_publish_date} { + if {"publish_date" ni $__atts} { + my instvar publish_date + lappend __atts publish_date + lappend __vars publish_date + } + set publish_date_flag [list -publish_date $publish_date] + } else { + set publish_date_flag "" + } ::xo::dc transaction { $__class instvar storage_type object_type @@ -1044,10 +1082,32 @@ set title [expr {[my exists __title_prefix] ? "[my set __title_prefix] ($name)" : $name}] } - #my msg --[subst [[self class] set content_item__new_args]] - set item_id [eval ::xo::db::sql::content_item new \ - [[self class] set content_item__new_args]] + if {$storage_type eq "file"} { + # Get the mime_type from the file, eventually creating a new + # one if it's unrecognized. + set mime_type [cr_check_mime_type \ + -mime_type $mime_type \ + -filename $name \ + -file $import_file] + } + + set item_id [::xo::db::sql::content_item new \ + -name $name \ + -parent_id $parent_id \ + -creation_user $creation_user \ + -creation_ip $creation_ip \ + -item_subtype "content_item" \ + -content_type $object_type \ + -description $description \ + -mime_type $mime_type \ + -nls_language $nls_language \ + -is_live f \ + -storage_type $storage_type \ + -package_id $package_id \ + -with_child_rels f] + + if {$storage_type eq "file"} { set text [cr_create_content_file $item_id $revision_id $import_file] } @@ -1056,12 +1116,15 @@ my fix_content $revision_id $text if {$live_p} { + # + # Update the life revision with the publish status and + # optionally the publish_date + # ::xo::db::sql::content_item set_live_revision \ -revision_id $revision_id \ - -publish_status [my set publish_status] - if {$use_given_publish_date} { - my update_revision $revision_id publish_date [my publish_date] - } + -publish_status [my set publish_status] \ + -is_latest true \ + {*}$publish_date_flag my update_item_index } } @@ -1088,14 +1151,26 @@ my instvar item_id ::xo::dc dml update_rename \ "update cr_items set name = :new_name where item_id = :item_id" + my set name $new_name my update_item_index } - CrItem instproc revisions {} { + # + # The method "changed_redirect_url" is a helper method for old-style + # wiki pages, still using ad_form. Form.edit_data calls this method + # after a rename operation to optionally redirect the browser after + # the edit operation to the new url, unless an explicit return_url + # was specified. + # + CrItem instproc changed_redirect_url {} { + return "" + } + CrItem instproc www-revisions {} { + set isAdmin [acs_user::site_wide_admin_p] - ::TableWidget t1 -volatile \ + ::TableWidget create t1 -volatile \ -columns { Field version_number -label "" -html {align right} ImageAnchorField edit -label "" -src /resources/acs-subsite/Zoom16.gif \ @@ -1131,10 +1206,7 @@ content_revision__get_number(r.revision_id) as version_number " \ -from "cr_items ci, cr_revisions r, acs_objects o" \ -where "ci.item_id = :page_id and r.item_id = ci.item_id and o.object_id = r.revision_id - and exists (select 1 from acs_object_party_privilege_map m - where m.object_id = r.revision_id - and m.party_id = :user_id - and m.privilege = 'read')" \ + and acs_permission__permission_p(r.revision_id, :user_id, 'read')" \ -orderby "r.revision_id desc"] ::xo::dc foreach revisions_select $sql { @@ -1278,7 +1350,7 @@ # # We should provide either a minimal parameter page for this # purposes, or - more conservative - provide simply package - # parameters for this. The only thing we are loosing are "computed + # parameters for this. The only thing we are losing are "computed # parameters", what most probably no-one uses. The delegation based # parameters are most probably good replacement to manage such # parameters site-wide. @@ -1377,6 +1449,7 @@ ::xo::db::CrFolder ad_proc get_instance_from_db { {-item_id 0} {-revision_id 0} + {-initialize:boolean true} } { The "standard" get_instance_from_db methods return objects following the naming convention "::", e.g. ::1234 @@ -1391,7 +1464,7 @@ } { set object ::cr_folder$item_id if {![my isobject $object]} { - my fetch_object -object $object -item_id $item_id + my fetch_object -object $object -item_id $item_id -initialize $initialize $object destroy_on_cleanup } return $object @@ -1417,7 +1490,7 @@ -item_id:required {-revision_id 0} -object:required - {-initialize true} + {-initialize:boolean true} } { We overwrite the default fetch_object method here. We join acs_objects, cr_items and cr_folders and fetch @@ -1507,7 +1580,7 @@ -item_id:required {-revision_id 0} -object:required - {-initialize true} + {-initialize:boolean true} } { set serialized_object [ns_cache eval xotcl_object_cache $object { #my log "--CACHE true fetch [self args], call shadowed method [self next]" @@ -1569,29 +1642,45 @@ ::xotcl::Class create CrCache::Item CrCache::Item set name_pattern {^::[0-9]+$} + CrCache::Item instproc remove_non_persistent_vars {} { - # we do not want to save __db__artefacts in the cache - foreach x [my info vars __db_*] {my unset $x} - # remove as well vars and array starting with "__", assuming these - # are volatile variables created by initialize_loaded_object or - # similar mechanisms + # + # Do not save __db__artefacts in the cache. + # + foreach x [info vars :__db_*] { + unset :$x + } + # + # Remove vars and arrays matching the class-specific specified + # non_cached_instance_var_patterns and treat these as variables, + # which are not stored in the cache, but which are kept in the + # instance variables. These variables are removed before caching + # and restored afterwards. + # set arrays {} set scalars {} - foreach x [my info vars __*] { - if {[my array exists $x]} { - lappend arrays $x [my array get $x] - my array unset $x + set non_cached_vars {} + foreach pattern [[my info class] non_cached_instance_var_patterns] { + lappend non_cached_vars {*}[info vars :$pattern] + } + + #puts stderr "pattern [[my info class] non_cached_instance_var_patterns], non_cached_vars=$non_cached_vars" + foreach x $non_cached_vars { + if {[array exists :$x]} { + lappend arrays $x [array get :$x] + array unset :$x } { - lappend scalars $x [my set $x] - my unset $x + lappend scalars $x [set :$x] + unset :$x } } return [list $arrays $scalars] } + CrCache::Item instproc set_non_persistent_vars {vars} { lassign $vars arrays scalars foreach {var value} $arrays {my array set $var $value} - foreach {var value} $scalars {my set $var $value} + :mset $scalars } CrCache::Item instproc flush_from_cache_and_refresh {} { # cache only names with IDs