| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # -*- perl -*- | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | # $Id: ObjBase.pm,v 1.16 2004/02/19 22:26:53 eserte Exp $ | 
| 5 |  |  |  |  |  |  | # Author: Slaven Rezic | 
| 6 |  |  |  |  |  |  | # | 
| 7 |  |  |  |  |  |  | # Copyright (C) 2002 Slaven Rezic. All rights reserved. | 
| 8 |  |  |  |  |  |  | # This package is free software; you can redistribute it and/or | 
| 9 |  |  |  |  |  |  | # modify it under the same terms as Perl itself. | 
| 10 |  |  |  |  |  |  | # | 
| 11 |  |  |  |  |  |  | # Mail: slaven@rezic.de | 
| 12 |  |  |  |  |  |  | # WWW:  http://www.rezic.de/eserte/ | 
| 13 |  |  |  |  |  |  | # | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | =head1 NAME | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | WE::DB::ObjBase - base class for WE_Framework object databases | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | use base qw(WE::DB::ObjBase); | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | =cut | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | package WE::DB::ObjBase; | 
| 28 | 15 |  |  | 15 |  | 92 | use base qw(WE::DB::Base); | 
|  | 15 |  |  |  |  | 27 |  | 
|  | 15 |  |  |  |  | 8807 |  | 
| 29 |  |  |  |  |  |  |  | 
| 30 | 15 |  |  | 15 |  | 86 | use strict; | 
|  | 15 |  |  |  |  | 29 |  | 
|  | 15 |  |  |  |  | 448 |  | 
| 31 | 15 |  |  | 15 |  | 101 | use vars qw($VERSION); | 
|  | 15 |  |  |  |  | 183 |  | 
|  | 15 |  |  |  |  | 1139 |  | 
| 32 |  |  |  |  |  |  | $VERSION = sprintf("%d.%02d", q$Revision: 1.16 $ =~ /(\d+)\.(\d+)/); | 
| 33 |  |  |  |  |  |  |  | 
| 34 | 15 |  |  | 15 |  | 9001 | use WE::Util::Date; | 
|  | 15 |  |  |  |  | 37 |  | 
|  | 15 |  |  |  |  | 37692 |  | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | =head2 METHODS | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | Please see also L for inherited methods. | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | =over | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | =cut | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | =item children($object_id) | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | Like children_ids, but return objects. | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | =cut | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | sub children { | 
| 51 | 0 |  |  | 0 | 1 |  | my($self, $obj_id) = @_; | 
| 52 | 0 |  |  |  |  |  | map { | 
| 53 | 0 |  |  |  |  |  | my $o = $self->get_object($_); | 
| 54 | 0 | 0 |  |  |  |  | if (!$o) { | 
| 55 | 0 |  |  |  |  |  | my $obj_id = $obj_id; | 
| 56 | 0 |  |  |  |  |  | my $child_id = $_; | 
| 57 | 0 |  |  |  |  |  | $self->idify_params($obj_id, $child_id); | 
| 58 | 0 |  |  |  |  |  | warn "Inconsistency in children method call for objid=$obj_id detected: child with objid=$child_id non-existent. Consider to run we_fsck. Error"; | 
| 59 | 0 |  |  |  |  |  | (); | 
| 60 |  |  |  |  |  |  | } else { | 
| 61 | 0 |  |  |  |  |  | $o; | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  | } $self->children_ids($obj_id); | 
| 64 |  |  |  |  |  |  | } | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | =item parents($object_id) | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | Like parent_ids, but return parent objects instead. | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | =cut | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | sub parents { | 
| 73 | 0 |  |  | 0 | 1 |  | my($self, $obj_id) = @_; | 
| 74 | 0 |  |  |  |  |  | map { | 
| 75 | 0 |  |  |  |  |  | my $o = $self->get_object($_); | 
| 76 | 0 | 0 |  |  |  |  | if (!$o) { | 
| 77 | 0 |  |  |  |  |  | warn "Inconsistency in parents($obj_id) detected"; | 
| 78 | 0 |  |  |  |  |  | (); | 
| 79 |  |  |  |  |  |  | } else { | 
| 80 | 0 |  |  |  |  |  | $o; | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  | } $self->parent_ids($obj_id); | 
| 83 |  |  |  |  |  |  | } | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | =item versions($object_id) | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | Like version_ids, but return version objects instead. | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | =cut | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | sub versions { | 
| 92 | 0 |  |  | 0 | 1 |  | my($self, $obj_id) = @_; | 
| 93 | 0 |  |  |  |  |  | map { | 
| 94 | 0 |  |  |  |  |  | my $o = $self->get_object($_); | 
| 95 | 0 | 0 |  |  |  |  | if (!$o) { | 
| 96 | 0 |  |  |  |  |  | warn "Inconsistency in versions($obj_id) detected"; | 
| 97 | 0 |  |  |  |  |  | (); | 
| 98 |  |  |  |  |  |  | } else { | 
| 99 | 0 |  |  |  |  |  | $o; | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  | } $self->version_ids($obj_id); | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | =item objectify_params($id_or_obj, ...) | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | For each parameter in the list, change the argument to be an object of | 
| 107 |  |  |  |  |  |  | the database. | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | =cut | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | sub objectify_params { | 
| 112 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 113 | 0 |  |  |  |  |  | foreach (@_) { | 
| 114 | 0 | 0 |  |  |  |  | if (!UNIVERSAL::isa($_, "WE::Obj")) { | 
| 115 | 0 |  |  |  |  |  | $_ = $self->get_object($_); | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  | } | 
| 118 |  |  |  |  |  |  | } | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | =item idify_params($id_or_obj, ...) | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | For each parameter in the list, change the argument to be an object | 
| 123 |  |  |  |  |  |  | identifier if it was an object, or leave it as it was. | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | =cut | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | sub idify_params { | 
| 128 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 129 | 0 |  |  |  |  |  | foreach (@_) { | 
| 130 | 0 | 0 |  |  |  |  | if (UNIVERSAL::isa($_,"WE::Obj")) { | 
| 131 | 0 |  |  |  |  |  | $_ = $_->Id; | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  | } | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | =item replace_content_from_file($object_id, $filename) | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | Like replace_content, but get contents from file. | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | =cut | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | sub replace_content_from_file { | 
| 143 | 0 |  |  | 0 | 1 |  | my($self, $objid, $filename) = @_; | 
| 144 | 0 |  |  |  |  |  | $self->idify_params($objid); | 
| 145 | 0 | 0 |  |  |  |  | open(F, $filename) or die "Can't open file $filename: $!"; | 
| 146 | 0 |  |  |  |  |  | local $/ = undef; | 
| 147 | 0 |  |  |  |  |  | my $new_content = ; | 
| 148 | 0 |  |  |  |  |  | close F; | 
| 149 | 0 |  |  |  |  |  | $self->replace_content($objid, $new_content); | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | =item walk($object_id, $sub_routine, @args) | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | Traverse the object hierarchie, beginning at the object with id | 
| 155 |  |  |  |  |  |  | C<$object_id>. For each object, C<$sub_routine> is called with the | 
| 156 |  |  |  |  |  |  | object id and optional C<@args>. Note that the subroutine is B | 
| 157 |  |  |  |  |  |  | called for the start object itself. | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | If there's no persistent connection to the database (i.e. the database | 
| 160 |  |  |  |  |  |  | was not accessed with -connect => 1), then using | 
| 161 |  |  |  |  |  |  | B is advisable for better performance. | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | Here are some examples for using walk. | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | Get the number of descendent objects from the folder with Id | 
| 166 |  |  |  |  |  |  | C<$folder_id>. The result is in the C<$obj_count> variable: | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | my $obj_count = 0; | 
| 169 |  |  |  |  |  |  | $objdb->walk($folder_id, sub { | 
| 170 |  |  |  |  |  |  | my($id, $ref) = @_; | 
| 171 |  |  |  |  |  |  | $$ref++; | 
| 172 |  |  |  |  |  |  | }, \$obj_count); | 
| 173 |  |  |  |  |  |  | warn "There are $obj_count objects in $folder_id\n"; | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | Get all released descendant objects. The released state should be | 
| 176 |  |  |  |  |  |  | recorded in the Release_State member. The resulting list is a flat | 
| 177 |  |  |  |  |  |  | array. | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | my @results; | 
| 180 |  |  |  |  |  |  | $objdb->walk($folder_id, sub { | 
| 181 |  |  |  |  |  |  | my($id) = @_; | 
| 182 |  |  |  |  |  |  | my $obj = $objdb->get_object($id); | 
| 183 |  |  |  |  |  |  | if ($obj->Release_State eq 'released') { | 
| 184 |  |  |  |  |  |  | push @results, $obj; | 
| 185 |  |  |  |  |  |  | } | 
| 186 |  |  |  |  |  |  | }); | 
| 187 |  |  |  |  |  |  | # The released objects are in @results. | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | If you want to break the recursion on a condition, simply use an | 
| 190 |  |  |  |  |  |  | C-block and C on the condition. See the source code of | 
| 191 |  |  |  |  |  |  | C method for an example. | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | C uses postorder traversal, that is, subtrees first, node later. | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | Note that the start object itself is not included in the traversal and | 
| 196 |  |  |  |  |  |  | the subroutine will not be called for it. | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | The returned value of the last callback called with be returned. | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | =item walk_preorder($object_id, $sub_routine, @args) | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | This is like C, but uses preorder instead of postorder, that is, | 
| 203 |  |  |  |  |  |  | node first, children later. | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | Note that the start object itself will be included in the traversal. | 
| 206 |  |  |  |  |  |  | This is different from the C method. | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | In preorder walk, the traversal of subtrees can be avoided by setting | 
| 209 |  |  |  |  |  |  | the global variable C<$WE::DB::Obj::prune> to a true value. | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | =cut | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | sub walk { | 
| 214 | 0 |  |  | 0 | 1 |  | my($self, $objid, $sub_routine, @args) = @_; | 
| 215 | 0 |  |  |  |  |  | my $ret; | 
| 216 | 0 |  |  |  |  |  | $self->idify_params($objid); | 
| 217 | 0 | 0 |  |  |  |  | if (!UNIVERSAL::isa($sub_routine, 'CODE')) { | 
| 218 | 0 |  |  |  |  |  | die "Second parameter of walk should be code reference"; | 
| 219 |  |  |  |  |  |  | } | 
| 220 | 0 |  |  |  |  |  | for my $sub_obj_id ($self->children_ids($objid)) { | 
| 221 | 0 |  |  |  |  |  | $self->walk($sub_obj_id, $sub_routine, @args); | 
| 222 | 0 |  |  |  |  |  | $ret = $sub_routine->($sub_obj_id, @args); | 
| 223 |  |  |  |  |  |  | } | 
| 224 | 0 |  |  |  |  |  | $ret; | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | sub walk_preorder { | 
| 228 | 0 |  |  | 0 | 1 |  | my($self, $objid, $sub_routine, @args) = @_; | 
| 229 | 0 |  |  |  |  |  | my $ret; | 
| 230 | 0 |  |  |  |  |  | $self->idify_params($objid); | 
| 231 | 0 | 0 |  |  |  |  | if (!UNIVERSAL::isa($sub_routine, 'CODE')) { | 
| 232 | 0 |  |  |  |  |  | die "Second parameter of walk_preorder should be code reference"; | 
| 233 |  |  |  |  |  |  | } | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | { | 
| 236 | 0 |  |  |  |  |  | local $WE::DB::Obj::prune; | 
|  | 0 |  |  |  |  |  |  | 
| 237 | 0 |  |  |  |  |  | $ret = $sub_routine->($objid, @args); | 
| 238 | 0 | 0 |  |  |  |  | return $ret if $WE::DB::Obj::prune; | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  |  | 
| 241 | 0 |  |  |  |  |  | for my $sub_obj_id ($self->children_ids($objid)) { | 
| 242 | 0 |  |  |  |  |  | $ret = $self->walk_preorder($sub_obj_id, $sub_routine, @args); | 
| 243 |  |  |  |  |  |  | } | 
| 244 | 0 |  |  |  |  |  | $ret; | 
| 245 |  |  |  |  |  |  | } | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | # XXX Document, and implement walk_up_prepostorder when needed! | 
| 248 |  |  |  |  |  |  | sub walk_prepostorder { | 
| 249 | 0 |  |  | 0 | 0 |  | my($self, $objid, $pre_sub_routine, $post_sub_routine, @args) = @_; | 
| 250 | 0 |  |  |  |  |  | my $ret; | 
| 251 | 0 |  |  |  |  |  | $self->idify_params($objid); | 
| 252 | 0 | 0 | 0 |  |  |  | if (!UNIVERSAL::isa($pre_sub_routine, 'CODE') || | 
| 253 |  |  |  |  |  |  | !UNIVERSAL::isa($post_sub_routine, 'CODE')) { | 
| 254 | 0 |  |  |  |  |  | die "Second and third parameters of walk_prepostorder should be code references"; | 
| 255 |  |  |  |  |  |  | } | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | { | 
| 258 | 0 |  |  |  |  |  | local $WE::DB::Obj::prune; | 
|  | 0 |  |  |  |  |  |  | 
| 259 | 0 |  |  |  |  |  | $ret = $pre_sub_routine->($objid, @args); | 
| 260 | 0 | 0 |  |  |  |  | return $ret if $WE::DB::Obj::prune; | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 | 0 |  |  |  |  |  | for my $sub_obj_id ($self->children_ids($objid)) { | 
| 264 | 0 |  |  |  |  |  | $ret = $self->walk_prepostorder($sub_obj_id, $pre_sub_routine, $post_sub_routine, @args); | 
| 265 |  |  |  |  |  |  | } | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | { | 
| 268 | 0 |  |  |  |  |  | local $WE::DB::Obj::prune; | 
|  | 0 |  |  |  |  |  |  | 
| 269 | 0 |  |  |  |  |  | $ret = $post_sub_routine->($objid, @args); | 
| 270 | 0 | 0 |  |  |  |  | return $ret if $WE::DB::Obj::prune; | 
| 271 |  |  |  |  |  |  | } | 
| 272 |  |  |  |  |  |  |  | 
| 273 | 0 |  |  |  |  |  | $ret; | 
| 274 |  |  |  |  |  |  | } | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | =item walk_up($object_id, $sub_routine, @args) | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | Same as C, but walk the tree up, that is, traverse all parents | 
| 279 |  |  |  |  |  |  | from the object to the root. | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | =item walk_up_preorder($object_id, $sub_routine, @args) | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | Same as C, but traverse in pre-order, that is, from the | 
| 285 |  |  |  |  |  |  | object to the root. Note that the object itself is also included in | 
| 286 |  |  |  |  |  |  | the traversal. | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | In preorder walk, the further traversal of parents can be avoided by | 
| 289 |  |  |  |  |  |  | setting the global variable C<$WE::DB::Obj::prune> to a true value. | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | =cut | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | sub walk_up { | 
| 294 | 0 |  |  | 0 | 1 |  | my($self, $objid, $sub_routine, @args) = @_; | 
| 295 | 0 |  |  |  |  |  | my $ret; | 
| 296 | 0 |  |  |  |  |  | $self->idify_params($objid); | 
| 297 | 0 | 0 |  |  |  |  | if (!UNIVERSAL::isa($sub_routine, 'CODE')) { | 
| 298 | 0 |  |  |  |  |  | die "Second parameter of walk_up should be code reference"; | 
| 299 |  |  |  |  |  |  | } | 
| 300 | 0 |  |  |  |  |  | for my $p_obj_id ($self->parent_ids($objid)) { | 
| 301 | 0 |  |  |  |  |  | $self->walk_up($p_obj_id, $sub_routine, @args); | 
| 302 | 0 |  |  |  |  |  | $ret = $sub_routine->($p_obj_id, @args); | 
| 303 |  |  |  |  |  |  | } | 
| 304 | 0 |  |  |  |  |  | $ret; | 
| 305 |  |  |  |  |  |  | } | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | sub walk_up_preorder { | 
| 308 | 0 |  |  | 0 | 1 |  | my($self, $objid, $sub_routine, @args) = @_; | 
| 309 | 0 |  |  |  |  |  | my $ret; | 
| 310 | 0 |  |  |  |  |  | $self->idify_params($objid); | 
| 311 | 0 | 0 |  |  |  |  | if (!UNIVERSAL::isa($sub_routine, 'CODE')) { | 
| 312 | 0 |  |  |  |  |  | die "Second parameter of walk_up_preorder should be code reference"; | 
| 313 |  |  |  |  |  |  | } | 
| 314 |  |  |  |  |  |  |  | 
| 315 | 0 |  |  |  |  |  | local $WE::DB::Obj::prune; | 
| 316 | 0 |  |  |  |  |  | $ret = $sub_routine->($objid, @args); | 
| 317 | 0 | 0 |  |  |  |  | return $ret if $WE::DB::Obj::prune; | 
| 318 |  |  |  |  |  |  |  | 
| 319 | 0 |  |  |  |  |  | for my $p_obj_id ($self->parent_ids($objid)) { | 
| 320 | 0 | 0 |  |  |  |  | if (defined $p_obj_id) { | 
| 321 | 0 |  |  |  |  |  | $ret = $self->walk_up_preorder($p_obj_id, $sub_routine, @args); | 
| 322 |  |  |  |  |  |  | } | 
| 323 |  |  |  |  |  |  | } | 
| 324 | 0 |  |  |  |  |  | $ret; | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | =item whole_tree([$objid]) | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | Return the whole (sub)tree of C<$objid>. If C<$objid> is not given, | 
| 330 |  |  |  |  |  |  | then return the whole tree. The elements of the tree are structured in | 
| 331 |  |  |  |  |  |  | a nested array. Each element is a hash of the following elements: Id, | 
| 332 |  |  |  |  |  |  | Title and isFolder. | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | =cut | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | sub whole_tree { | 
| 337 | 0 |  |  | 0 | 1 |  | my($self, $objid, $tree) = @_; | 
| 338 | 0 | 0 |  |  |  |  | $objid = $self->root_object->id if !defined $objid; | 
| 339 | 0 | 0 |  |  |  |  | $tree  = [] if !$tree; | 
| 340 | 0 |  |  |  |  |  | my $obj = $self->get_object($objid); | 
| 341 | 0 | 0 |  |  |  |  | if (!$obj) { | 
| 342 | 0 |  |  |  |  |  | warn "Can't get object $objid!"; | 
| 343 | 0 |  |  |  |  |  | return; | 
| 344 |  |  |  |  |  |  | } | 
| 345 | 0 |  |  |  |  |  | push @$tree, {Id=>$obj->Id, Title=>$obj->Title, isFolder=>$obj->is_folder}; | 
| 346 | 0 |  |  |  |  |  | my @children_ids = $self->children_ids($objid); | 
| 347 | 0 | 0 |  |  |  |  | if (@children_ids) { | 
| 348 | 0 |  |  |  |  |  | my $child_tree = []; | 
| 349 | 0 |  |  |  |  |  | foreach my $cid (@children_ids) { | 
| 350 | 0 |  |  |  |  |  | $self->whole_tree($cid, $child_tree); | 
| 351 |  |  |  |  |  |  | } | 
| 352 | 0 |  |  |  |  |  | push @$tree, $child_tree; | 
| 353 |  |  |  |  |  |  | } | 
| 354 | 0 |  |  |  |  |  | $tree; | 
| 355 |  |  |  |  |  |  | } | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | =item _undirty($object) | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | Return the object with all Dirty flags set to 0. | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | =cut | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | sub _undirty { | 
| 364 | 0 |  |  | 0 |  |  | my($self, $obj) = @_; | 
| 365 | 0 |  |  |  |  |  | $self->objectify_params($obj); | 
| 366 | 0 |  |  |  |  |  | $obj->Dirty(0); | 
| 367 | 0 |  |  |  |  |  | $obj->DirtyAttributes(0); | 
| 368 | 0 |  |  |  |  |  | $obj->DirtyContent(0); | 
| 369 | 0 |  |  |  |  |  | $self->replace_object($obj); | 
| 370 |  |  |  |  |  |  | } | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | =item is_locked($object_id) | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | Return true if the object is locked by someone else. | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | =cut | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | sub is_locked { | 
| 379 | 0 |  |  | 0 | 1 |  | my($self, $obj) = @_; | 
| 380 | 0 |  |  |  |  |  | $self->objectify_params($obj); | 
| 381 | 0 | 0 | 0 |  |  |  | return 0 if !defined $obj->LockedBy || $obj->LockedBy eq ''; | 
| 382 | 0 | 0 |  |  |  |  | return 0 if $obj->LockedBy eq $self->Root->CurrentUser; | 
| 383 | 0 | 0 |  |  |  |  | if ($obj->LockType eq 'SessionLock') { | 
| 384 | 0 | 0 |  |  |  |  | if ($self->Root->OnlineUserDB) { | 
| 385 | 0 |  |  |  |  |  | my $r = $self->Root->OnlineUserDB->check_logged($obj->LockedBy); | 
| 386 | 0 | 0 |  |  |  |  | if (!$r) { | 
| 387 | 0 |  |  |  |  |  | $self->unlock($obj); # XXX -force => 1 ??? | 
| 388 |  |  |  |  |  |  | } | 
| 389 | 0 |  |  |  |  |  | return $r; | 
| 390 |  |  |  |  |  |  | } else { | 
| 391 | 0 |  |  |  |  |  | return 0; | 
| 392 |  |  |  |  |  |  | } | 
| 393 |  |  |  |  |  |  | } | 
| 394 | 0 | 0 |  |  |  |  | return 1 if ($obj->LockType eq 'PermanentLock'); # XXX probably check for existing user? | 
| 395 | 0 |  |  |  |  |  | warn "Unknown lock type @{[ $obj->LockType ]}, assumed locked"; | 
|  | 0 |  |  |  |  |  |  | 
| 396 | 0 |  |  |  |  |  | 1; | 
| 397 |  |  |  |  |  |  | } | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | =item lock($object_id, -type => $lock_type) | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | Lock the object C<$object_id>. Only single objects can be locked (no | 
| 402 |  |  |  |  |  |  | folder hierarchies). Locking must be handled in the client by using | 
| 403 |  |  |  |  |  |  | C. The C<$lock_type> may have the following values: | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | =over 4 | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | =item SessionLock | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | This lock should only be valid for this session. If the user closes | 
| 410 |  |  |  |  |  |  | the session (either by a logout or by closing the browser window), | 
| 411 |  |  |  |  |  |  | then the lock will be invalidated. | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | =item PermanentLock | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | This lock lasts over session ends. | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | =back | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  | Return the object itself. | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | Now, it should be checked programmatically whether the lock can be set | 
| 422 |  |  |  |  |  |  | or not (by looking at the value is_locked). It is not clear what is | 
| 423 |  |  |  |  |  |  | the right solution, because there are version control systems where | 
| 424 |  |  |  |  |  |  | breaking locks is possible (RCS). | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | =cut | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | sub lock { | 
| 429 | 0 |  |  | 0 | 1 |  | my($self, $obj_id, %args) = @_; | 
| 430 | 0 | 0 |  |  |  |  | die "Lock -type is missing" if !$args{-type}; | 
| 431 | 0 | 0 |  |  |  |  | die "Valid Lock types are SessionLock and PermanentLock" | 
| 432 |  |  |  |  |  |  | unless $args{-type} =~ /^(Session|Permanent)Lock$/; | 
| 433 | 0 |  |  |  |  |  | $self->idify_params($obj_id); | 
| 434 | 0 |  |  |  |  |  | my $obj = $self->get_object($obj_id); | 
| 435 | 0 |  |  |  |  |  | $obj->LockedBy($self->Root->CurrentUser); | 
| 436 | 0 |  |  |  |  |  | $obj->LockType($args{-type}); | 
| 437 | 0 |  |  |  |  |  | $obj->LockTime(epoch2isodate()); | 
| 438 | 0 |  |  |  |  |  | $self->replace_object($obj); | 
| 439 |  |  |  |  |  |  | } | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | =item unlock($object_id) | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | Unlock the object with id C<$object_id>. | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | Return the object itself. | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | Now, it should be checked programmatically whether the lock can be | 
| 448 |  |  |  |  |  |  | unset or not (by looking at the value is_locked). It is not clear what | 
| 449 |  |  |  |  |  |  | is the right solution, because there are version control systems where | 
| 450 |  |  |  |  |  |  | breaking locks is possible (RCS). | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | =cut | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | sub unlock { | 
| 455 | 0 |  |  | 0 | 1 |  | my($self, $obj_id) = @_; | 
| 456 | 0 |  |  |  |  |  | $self->idify_params($obj_id); | 
| 457 | 0 |  |  |  |  |  | my $obj = $self->get_object($obj_id); | 
| 458 | 0 |  |  |  |  |  | $obj->LockedBy(undef); | 
| 459 | 0 |  |  |  |  |  | $obj->LockType(undef); | 
| 460 | 0 |  |  |  |  |  | $obj->LockTime(undef); | 
| 461 | 0 |  |  |  |  |  | $self->replace_object($obj); | 
| 462 |  |  |  |  |  |  | } | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | =item pathobjects($object_or_id [, $parent_obj]) | 
| 465 |  |  |  |  |  |  |  | 
| 466 |  |  |  |  |  |  | For the object or id C<$object_or_id>, the object path is returned. | 
| 467 |  |  |  |  |  |  | This is similar to the C method, but returns a list of | 
| 468 |  |  |  |  |  |  | objects instead of a pathname. | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | If C<$parent_obj> is given as a object, then the returned pathname is | 
| 471 |  |  |  |  |  |  | only a partial path starting from this parent object. | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  | =cut | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | sub pathobjects { | 
| 476 | 0 |  |  | 0 | 1 |  | my($self, $obj, $parent_obj) = @_; | 
| 477 | 0 |  |  |  |  |  | $self->objectify_params($obj); | 
| 478 | 0 | 0 | 0 |  |  |  | if (defined $parent_obj && $obj->Id eq $parent_obj->Id) { | 
| 479 | 0 |  |  |  |  |  | return (); | 
| 480 |  |  |  |  |  |  | } | 
| 481 | 0 |  |  |  |  |  | my @parents = $self->parent_ids($obj->Id); | 
| 482 | 0 | 0 |  |  |  |  | if (@parents) { | 
| 483 | 0 |  |  |  |  |  | ($self->pathobjects($parents[0], $parent_obj), $obj); | 
| 484 |  |  |  |  |  |  | } else { | 
| 485 | 0 |  |  |  |  |  | ($obj); | 
| 486 |  |  |  |  |  |  | } | 
| 487 |  |  |  |  |  |  | } | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | =item pathobjects_with_cache($object_or_id [, $parent_obj], $cache_hash_ref) | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | As C, but also use a cache for a faster access. | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | =cut | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | sub pathobjects_with_cache { | 
| 496 | 0 |  |  | 0 | 1 |  | my($self, $obj, $parent_obj, $cache) = @_; | 
| 497 | 0 | 0 | 0 |  |  |  | if (!ref $obj && exists $cache->{$obj}) { # get by id | 
| 498 | 0 |  |  |  |  |  | return @{ $cache->{$obj} }; | 
|  | 0 |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | } | 
| 500 | 0 |  |  |  |  |  | $self->objectify_params($obj); | 
| 501 | 0 | 0 |  |  |  |  | return () if !$obj; | 
| 502 | 0 |  |  |  |  |  | my $objid = $obj->Id; | 
| 503 | 0 | 0 |  |  |  |  | if (exists $cache->{$objid}) { | 
| 504 | 0 |  |  |  |  |  | return @{ $cache->{$objid} }; | 
|  | 0 |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | } | 
| 506 | 0 | 0 | 0 |  |  |  | if (defined $parent_obj && $objid eq $parent_obj->Id) { | 
| 507 | 0 |  |  |  |  |  | $cache->{$obj->Id} = []; | 
| 508 | 0 |  |  |  |  |  | return (); | 
| 509 |  |  |  |  |  |  | } | 
| 510 | 0 |  |  |  |  |  | my @parents = $self->parent_ids($objid); | 
| 511 | 0 | 0 |  |  |  |  | if (@parents) { | 
| 512 | 0 | 0 |  |  |  |  | if (exists $cache->{$parents[0]}) { | 
| 513 | 0 |  |  |  |  |  | (@{ $cache->{$parents[0]} }, $obj); | 
|  | 0 |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  | } else { | 
| 515 | 0 |  |  |  |  |  | my @parent_parents = $self->pathobjects_with_cache($parents[0], $parent_obj); | 
| 516 | 0 |  |  |  |  |  | $cache->{$parents[0]} = [@parent_parents]; | 
| 517 | 0 |  |  |  |  |  | (@parent_parents, $obj); | 
| 518 |  |  |  |  |  |  | } | 
| 519 |  |  |  |  |  |  | } else { | 
| 520 | 0 |  |  |  |  |  | ($obj); | 
| 521 |  |  |  |  |  |  | } | 
| 522 |  |  |  |  |  |  | } | 
| 523 |  |  |  |  |  |  |  | 
| 524 |  |  |  |  |  |  | =item name_to_objid($name) | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | Return the object id for the object containing the Attribute | 
| 527 |  |  |  |  |  |  | C. If there is no such object, undef is returned. Note: This | 
| 528 |  |  |  |  |  |  | method may or may not be efficient, depending whether there is an | 
| 529 |  |  |  |  |  |  | index database (C) or not. | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | =cut | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | sub name_to_objid { | 
| 534 | 0 |  |  | 0 | 1 |  | my($self, $name) = @_; | 
| 535 | 0 |  |  |  |  |  | my $objid; | 
| 536 | 0 | 0 |  |  |  |  | if ($self->Root->NameDB) { | 
| 537 | 0 |  |  |  |  |  | $objid = $self->Root->NameDB->get_id($name); | 
| 538 | 0 | 0 |  |  |  |  | return $objid if defined $objid; | 
| 539 |  |  |  |  |  |  | } | 
| 540 |  |  |  |  |  |  | # for backward compatibility (database without name.db) | 
| 541 | 0 |  |  |  |  |  | eval { | 
| 542 | 0 |  |  |  |  |  | local $SIG{__DIE__}; | 
| 543 |  |  |  |  |  |  | $self->walk($self->root_object->Id, sub { | 
| 544 | 0 |  |  | 0 |  |  | my($id) = @_; | 
| 545 | 0 |  |  |  |  |  | my $obj = $self->get_object($id); | 
| 546 | 0 | 0 | 0 |  |  |  | if (defined $obj->Name && $obj->Name eq $name) { | 
| 547 | 0 |  |  |  |  |  | $objid = $obj->Id; | 
| 548 | 0 |  |  |  |  |  | die "Found"; | 
| 549 |  |  |  |  |  |  | } | 
| 550 | 0 |  |  |  |  |  | }); | 
| 551 |  |  |  |  |  |  | }; | 
| 552 | 0 |  |  |  |  |  | $objid; | 
| 553 |  |  |  |  |  |  | } | 
| 554 |  |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | 1; | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | __END__ |