line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# -*- perl -*- |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# $Id: Obj.pm,v 1.37 2005/01/28 08:44:07 eserte Exp $ |
5
|
|
|
|
|
|
|
# Author: Slaven Rezic |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
# Copyright (C) 2001 Online Office Berlin. All rights reserved. |
8
|
|
|
|
|
|
|
# Copyright (C) 2002 Slaven Rezic. |
9
|
|
|
|
|
|
|
# This is free software; you can redistribute it and/or modify it under the |
10
|
|
|
|
|
|
|
# terms of the GNU General Public License, see the file COPYING. |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# |
13
|
|
|
|
|
|
|
# Mail: slaven@rezic.de |
14
|
|
|
|
|
|
|
# WWW: http://we-framework.sourceforge.net |
15
|
|
|
|
|
|
|
# |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 NAME |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
WE::DB::Obj - object database for the WE_Framework |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 SYNOPSIS |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
$objdb = WE::DB::Obj->new($root, $db_file); |
24
|
|
|
|
|
|
|
$objdb = $root->ObjDB; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 DESCRIPTION |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=cut |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
package WE::DB::Obj; |
31
|
15
|
|
|
15
|
|
82
|
use base qw(WE::DB::ObjBase); |
|
15
|
|
|
|
|
30
|
|
|
15
|
|
|
|
|
11774
|
|
32
|
|
|
|
|
|
|
|
33
|
15
|
|
|
15
|
|
118
|
use strict; |
|
15
|
|
|
|
|
24
|
|
|
15
|
|
|
|
|
591
|
|
34
|
15
|
|
|
15
|
|
74
|
use vars qw($VERSION); |
|
15
|
|
|
|
|
33
|
|
|
15
|
|
|
|
|
1445
|
|
35
|
|
|
|
|
|
|
$VERSION = sprintf("%d.%02d", q$Revision: 1.37 $ =~ /(\d+)\.(\d+)/); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors(qw(DBFile DBTieArgs |
38
|
|
|
|
|
|
|
MLDBM_Serializer MLDBM_UseDB MLDBM_DumpMeth |
39
|
|
|
|
|
|
|
IsCachedDatabase)); |
40
|
|
|
|
|
|
|
|
41
|
15
|
|
|
15
|
|
14320
|
use MLDBM; |
|
15
|
|
|
|
|
90654
|
|
|
15
|
|
|
|
|
110
|
|
42
|
15
|
|
|
15
|
|
633
|
use Fcntl; |
|
15
|
|
|
|
|
31
|
|
|
15
|
|
|
|
|
5325
|
|
43
|
|
|
|
|
|
|
|
44
|
15
|
|
|
15
|
|
97
|
use WE::Util::Date; |
|
15
|
|
|
|
|
31
|
|
|
15
|
|
|
|
|
858
|
|
45
|
15
|
|
|
15
|
|
10164
|
use WE::Util::LangString qw(new_langstring langstring); |
|
15
|
|
|
|
|
45
|
|
|
15
|
|
|
|
|
1239
|
|
46
|
|
|
|
|
|
|
|
47
|
15
|
|
|
15
|
|
97
|
use constant OBJECT => 0; |
|
15
|
|
|
|
|
29
|
|
|
15
|
|
|
|
|
892
|
|
48
|
15
|
|
|
15
|
|
75
|
use constant CHILDREN => 1; |
|
15
|
|
|
|
|
27
|
|
|
15
|
|
|
|
|
730
|
|
49
|
15
|
|
|
15
|
|
76
|
use constant PARENTS => 2; |
|
15
|
|
|
|
|
52
|
|
|
15
|
|
|
|
|
560
|
|
50
|
15
|
|
|
15
|
|
77
|
use constant VERSIONS => 3; |
|
15
|
|
|
|
|
33
|
|
|
15
|
|
|
|
|
157216
|
|
51
|
|
|
|
|
|
|
|
52
|
0
|
|
|
0
|
0
|
|
sub DBClass { "DB_File" } |
53
|
0
|
|
|
0
|
0
|
|
sub SerializerClass { "Data::Dumper" } |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=head2 CONSTRUCTOR new($class, $root, $file [, %args]) |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
C creates a new database reference object (and, if the database |
58
|
|
|
|
|
|
|
does not exist, also the physical database). Usually called only from |
59
|
|
|
|
|
|
|
L. Parameters are: the C<$root> object (a |
60
|
|
|
|
|
|
|
C object) and the filename for the underlying database (here, |
61
|
|
|
|
|
|
|
it is C). |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
In the optional arguments, further options can be specified: |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=over 4 |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=item -serializer => $serializer |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
The type of the serializer, e.g. C (the default) or |
70
|
|
|
|
|
|
|
C. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=item -db => $db |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
The type of the database (dbm) implementation, e.g. C (the |
75
|
|
|
|
|
|
|
default) or C. Note that other databases than C or |
76
|
|
|
|
|
|
|
C have length restrictions, making them unsuitable for |
77
|
|
|
|
|
|
|
using with C. However, the CPAN module |
78
|
|
|
|
|
|
|
C workaround the deficiency of the 1K size |
79
|
|
|
|
|
|
|
limit in the standard C database. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=item -locking => $bool |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
True, if locking should be used. XXX For now, only 0 and 1 can be |
84
|
|
|
|
|
|
|
used, but this should probably be changed to use shared and exclusive |
85
|
|
|
|
|
|
|
locks. |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
By default, there is no locking. If locking is enabled and the |
88
|
|
|
|
|
|
|
database type is C, then C will be used. For |
89
|
|
|
|
|
|
|
other database types, no locking is implemented. |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=item -readonly => $bool |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
Open the database read-only. This is the same as specifying O_RDONLY. |
94
|
|
|
|
|
|
|
By default it is opened read-write and the database is created if |
95
|
|
|
|
|
|
|
necessary (O_RDWR|O_CREAT). |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=item -writeonly => $bool |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
If true, then a database will not be created if necessary. This is the |
100
|
|
|
|
|
|
|
same as specifying O_RDWR. |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=item -connect => $bool |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
If true, connects to the database while constructing the object. |
105
|
|
|
|
|
|
|
Otherwise the connection will be made automatically before each |
106
|
|
|
|
|
|
|
operation. Also, the methods B and B can be used |
107
|
|
|
|
|
|
|
for connecting and disconnecting from the database. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Normally, long running processes (servers or mod_perl processes) |
110
|
|
|
|
|
|
|
should specify -connect => 0 and use the auto-connection feature or |
111
|
|
|
|
|
|
|
manually connect()/disconnect(). So database changes are propagated |
112
|
|
|
|
|
|
|
immediately. |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
The default of the -connect option is true. |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=back |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=cut |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub new { |
121
|
0
|
|
|
0
|
1
|
|
my($proto, $root, $file, %args) = @_; |
122
|
0
|
|
0
|
|
|
|
my $class = ref $proto || $proto; |
123
|
0
|
|
|
|
|
|
my $self = {}; |
124
|
0
|
|
|
|
|
|
bless $self, $class; |
125
|
|
|
|
|
|
|
|
126
|
0
|
0
|
|
|
|
|
$args{-db} = $self->DBClass |
127
|
|
|
|
|
|
|
unless defined $args{-db}; |
128
|
0
|
0
|
|
|
|
|
$args{-serializer} = $self->SerializerClass |
129
|
|
|
|
|
|
|
unless defined $args{-serializer}; |
130
|
0
|
0
|
|
|
|
|
$args{-locking} = 0 unless defined $args{-locking}; |
131
|
0
|
0
|
|
|
|
|
$args{-readonly} = 0 unless defined $args{-readonly}; |
132
|
0
|
0
|
|
|
|
|
$args{-writeonly} = 0 unless defined $args{-writeonly}; |
133
|
0
|
0
|
|
|
|
|
$args{-connect} = 1 unless defined $args{-connect}; |
134
|
0
|
0
|
0
|
|
|
|
if (!$args{-readonly} && $args{-cache}) { |
135
|
0
|
|
|
|
|
|
die "-cache => 1 is only allowed with -readonly"; |
136
|
|
|
|
|
|
|
} |
137
|
0
|
0
|
|
|
|
|
$args{-cache} = 0 unless defined $args{-cache}; |
138
|
|
|
|
|
|
|
|
139
|
0
|
|
|
|
|
|
my @tie_args; |
140
|
0
|
0
|
|
|
|
|
if ($args{-readonly}) { |
|
|
0
|
|
|
|
|
|
141
|
0
|
|
|
|
|
|
push @tie_args, O_RDONLY; |
142
|
|
|
|
|
|
|
} elsif ($args{-writeonly}) { |
143
|
0
|
|
|
|
|
|
push @tie_args, O_RDWR; |
144
|
|
|
|
|
|
|
} else { |
145
|
0
|
|
|
|
|
|
push @tie_args, O_RDWR|O_CREAT; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
0
|
0
|
|
|
|
|
push @tie_args, $args{-db} eq 'Tie::TextDir' ? 0770 : 0660; |
149
|
|
|
|
|
|
|
|
150
|
0
|
0
|
|
|
|
|
if ($args{-db} eq 'DB_File') { |
151
|
0
|
|
|
|
|
|
require DB_File; |
152
|
0
|
|
|
|
|
|
push @tie_args, $DB_File::DB_HASH; |
153
|
0
|
0
|
|
|
|
|
if ($args{-locking}) { |
154
|
0
|
|
|
|
|
|
$self->MLDBM_UseDB('DB_File::Lock'); |
155
|
0
|
0
|
|
|
|
|
push @tie_args, $args{-readonly} ? "read" : "write"; |
156
|
|
|
|
|
|
|
} else { |
157
|
0
|
|
|
|
|
|
$self->MLDBM_UseDB('DB_File'); |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
} else { |
160
|
0
|
|
|
|
|
|
$self->MLDBM_UseDB($args{-db}); |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
0
|
|
|
|
|
|
$self->MLDBM_Serializer($args{-serializer}); |
164
|
0
|
0
|
|
|
|
|
if ($self->MLDBM_Serializer eq 'Storable') { |
165
|
0
|
|
|
|
|
|
$self->MLDBM_DumpMeth('portable'); |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
0
|
|
|
|
|
|
$self->DBFile($file); |
169
|
0
|
|
|
|
|
|
$self->DBTieArgs(\@tie_args); |
170
|
|
|
|
|
|
|
|
171
|
0
|
|
|
|
|
|
$self->Root($root); |
172
|
0
|
|
|
|
|
|
$self->Connected(0); |
173
|
|
|
|
|
|
|
|
174
|
0
|
0
|
|
|
|
|
if ($args{-cache}) { |
175
|
0
|
|
|
|
|
|
my $cached_db = {}; |
176
|
0
|
|
|
|
|
|
$self->connect; |
177
|
0
|
|
|
|
|
|
while(my($k,$v) = each %{ $self->{DB} }) { |
|
0
|
|
|
|
|
|
|
178
|
0
|
|
|
|
|
|
$cached_db->{$k} = $v; |
179
|
|
|
|
|
|
|
} |
180
|
0
|
|
|
|
|
|
$self->disconnect; |
181
|
0
|
|
|
|
|
|
$self->{DB} = $cached_db; |
182
|
0
|
|
|
|
|
|
$self->Connected(1); |
183
|
0
|
|
|
|
|
|
$self->IsCachedDatabase(1); |
184
|
0
|
|
|
|
|
|
return $self; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
0
|
0
|
0
|
|
|
|
if ($args{-connect} && $args{-connect} ne 'never') { |
188
|
0
|
|
|
|
|
|
$self->connect; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
0
|
|
|
|
|
|
$self; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
sub cached_db { |
195
|
0
|
|
|
0
|
0
|
|
my($self) = @_; |
196
|
0
|
0
|
|
|
|
|
my $db = ($self->MLDBM_UseDB eq 'DB_File::Lock' |
197
|
|
|
|
|
|
|
? 'DB_File' |
198
|
|
|
|
|
|
|
: $self->MLDBM_UseDB |
199
|
|
|
|
|
|
|
); |
200
|
0
|
|
|
|
|
|
$self->new($self->Root, |
201
|
|
|
|
|
|
|
$self->DBFile, |
202
|
|
|
|
|
|
|
-readonly => 1, |
203
|
|
|
|
|
|
|
-cache => 1, |
204
|
|
|
|
|
|
|
-db => $db, |
205
|
|
|
|
|
|
|
-serializer => $self->MLDBM_Serializer, |
206
|
|
|
|
|
|
|
); |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=head2 DESTRUCTOR DESTROY |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
Called automatically. Destroys the tied database handle. |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=cut |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
### XXX DESTROY seems to throw segfaults now (because of disconnect??? the |
216
|
|
|
|
|
|
|
### XXX eval in disconnect???) |
217
|
|
|
|
|
|
|
# sub DESTROY { |
218
|
|
|
|
|
|
|
# my $self = shift; |
219
|
|
|
|
|
|
|
# $self->Root(undef); |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# #XXXlocal $^W = undef; # XXX |
222
|
|
|
|
|
|
|
# $self->disconnect; |
223
|
|
|
|
|
|
|
# # if ($self->{DB} && ref $self->{DB} eq 'HASH' && tied %{$self->{DB}}) { |
224
|
|
|
|
|
|
|
# # untie %{ $self->{DB} }; |
225
|
|
|
|
|
|
|
# # } |
226
|
|
|
|
|
|
|
# } |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=head2 METHODS |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Please see also L for inherited methods. |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=over 4 |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=item connect |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=cut |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
sub connect { |
239
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
240
|
0
|
|
|
|
|
|
local $MLDBM::UseDB = $self->MLDBM_UseDB; |
241
|
0
|
|
|
|
|
|
local $MLDBM::Serializer = $self->MLDBM_Serializer; |
242
|
0
|
|
|
|
|
|
local $MLDBM::DumpMeth = $self->MLDBM_DumpMeth; |
243
|
|
|
|
|
|
|
|
244
|
0
|
|
|
|
|
|
my @args = @{$self->DBTieArgs}; |
|
0
|
|
|
|
|
|
|
245
|
0
|
0
|
|
|
|
|
tie %{ $self->{DB} }, 'MLDBM', $self->DBFile, @args |
|
0
|
|
|
|
|
|
|
246
|
0
|
|
|
|
|
|
or die "Can't tie MLDBM database @{[$self->DBFile]} with args <@args>, db <$MLDBM::UseDB> and serializer <$MLDBM::Serializer>: $!"; |
247
|
0
|
|
|
|
|
|
$self->Connected(1); |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=item disconnect |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=cut |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
sub disconnect { |
255
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
256
|
0
|
0
|
|
|
|
|
if ($self->Connected) { |
257
|
0
|
|
|
|
|
|
eval { |
258
|
0
|
|
|
|
|
|
untie %{ $self->{DB} }; |
|
0
|
|
|
|
|
|
|
259
|
0
|
0
|
|
|
|
|
};warn $@ if $@; |
260
|
0
|
|
|
|
|
|
$self->Connected(0); |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=item init |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
Initialize the database to hold meta data like _root_object or |
267
|
|
|
|
|
|
|
_next_id. Usually called only from C. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=cut |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# XXX hardcoded to create a site... |
272
|
|
|
|
|
|
|
sub init { |
273
|
0
|
|
|
0
|
1
|
|
my($self, %args) = @_; |
274
|
|
|
|
|
|
|
|
275
|
0
|
0
|
|
|
|
|
if (!$self->root_object) { |
276
|
|
|
|
|
|
|
$self->connect_if_necessary |
277
|
|
|
|
|
|
|
(sub { |
278
|
0
|
|
|
0
|
|
|
my $site = WE::Obj::Site->new(); |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# XXX hmmmm... should not be doubled... |
281
|
0
|
|
|
|
|
|
my $now = epoch2isodate(); |
282
|
0
|
|
|
|
|
|
$site->TimeCreated($now); |
283
|
0
|
|
|
|
|
|
$site->TimeModified($now); |
284
|
0
|
|
|
|
|
|
$site->Owner($self->Root->CurrentUser); |
285
|
0
|
|
0
|
|
|
|
my $title = $args{-title} || |
286
|
|
|
|
|
|
|
new_langstring(en => "Root of the site", |
287
|
|
|
|
|
|
|
de => "Wurzel der Website", |
288
|
|
|
|
|
|
|
); |
289
|
0
|
|
|
|
|
|
$site->Title($title); |
290
|
0
|
|
|
|
|
|
my $obj = $self->_store_obj($site); |
291
|
0
|
|
|
|
|
|
$self->{DB}{'_root_object'} = $obj->[OBJECT]->Id; |
292
|
0
|
|
|
|
|
|
}); |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
=item delete_db_contents |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
Delete all database contents |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=cut |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub delete_db_contents { |
303
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
304
|
|
|
|
|
|
|
$self->connect_if_necessary |
305
|
|
|
|
|
|
|
(sub { |
306
|
0
|
|
|
0
|
|
|
my @obj = keys %{ $self->{DB} }; |
|
0
|
|
|
|
|
|
|
307
|
0
|
|
|
|
|
|
foreach (@obj) { |
308
|
0
|
|
|
|
|
|
delete $self->{DB}{$_}; |
309
|
|
|
|
|
|
|
} |
310
|
0
|
|
|
|
|
|
$self->init; |
311
|
0
|
|
|
|
|
|
}); |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
# update names, links ... |
314
|
0
|
0
|
|
|
|
|
if ($self->Root->NameDB) { |
315
|
0
|
|
|
|
|
|
$self->Root->NameDB->delete_db_contents; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=item root_object |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
Return the root object. |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=cut |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
sub root_object { |
326
|
0
|
|
|
0
|
1
|
|
my($self) = @_; |
327
|
|
|
|
|
|
|
# XXX permission manager |
328
|
|
|
|
|
|
|
$self->connect_if_necessary |
329
|
|
|
|
|
|
|
(sub { |
330
|
0
|
0
|
|
0
|
|
|
if (exists $self->{DB}{'_root_object'}) { |
331
|
0
|
|
|
|
|
|
$self->get_object($self->{DB}{'_root_object'}); |
332
|
|
|
|
|
|
|
} else { |
333
|
0
|
|
|
|
|
|
undef; |
334
|
|
|
|
|
|
|
} |
335
|
0
|
|
|
|
|
|
}); |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=item is_root_object($objid) |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
Return true if the object with id C<$objid> is the root object. |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=cut |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
sub is_root_object { |
345
|
0
|
|
|
0
|
1
|
|
my($self, $objid) = @_; |
346
|
0
|
|
|
|
|
|
$self->idify_params($objid); |
347
|
|
|
|
|
|
|
$self->connect_if_necessary |
348
|
|
|
|
|
|
|
(sub { |
349
|
0
|
|
|
0
|
|
|
$self->{DB}{'_root_object'} eq $objid; |
350
|
0
|
|
|
|
|
|
}); |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=item _next_id |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
Increment and get the next free id. The internal id counter is always |
356
|
|
|
|
|
|
|
incremented, regardless whether the new id will be used or not. |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=cut |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub _next_id { |
361
|
0
|
|
|
0
|
|
|
my($self) = @_; |
362
|
|
|
|
|
|
|
$self->connect_if_necessary |
363
|
|
|
|
|
|
|
(sub { |
364
|
0
|
|
0
|
0
|
|
|
my $id = $self->{DB}->{'_next_id'} || 0; |
365
|
0
|
|
|
|
|
|
$self->{DB}->{'_next_id'}++; |
366
|
0
|
|
|
|
|
|
$id; |
367
|
0
|
|
|
|
|
|
}); |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=item _get_next_id |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
Only get the next free id, without incrementing it. |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
=cut |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
sub _get_next_id { |
377
|
0
|
|
|
0
|
|
|
my($self) = @_; |
378
|
|
|
|
|
|
|
$self->connect_if_necessary |
379
|
|
|
|
|
|
|
(sub { |
380
|
0
|
|
|
0
|
|
|
$self->{DB}->{'_next_id'}; |
381
|
0
|
|
|
|
|
|
}); |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=item _create_stored_obj |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
Create a new internal stored object. |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=cut |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
sub _create_stored_obj { |
391
|
0
|
|
|
0
|
|
|
my($self) = @_; |
392
|
0
|
|
|
|
|
|
[undef, [], [], []]; |
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=item _store_stored_obj($stored_object) |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
Store the internal stored object. |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
=cut |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
sub _store_stored_obj { |
402
|
0
|
|
|
0
|
|
|
my($self, $stored_obj) = @_; |
403
|
0
|
|
|
|
|
|
my $id = $stored_obj->[OBJECT]->Id; |
404
|
0
|
0
|
|
|
|
|
if (!defined $id) { |
405
|
0
|
|
|
|
|
|
die "Fatal error: there is no Id in the stored object"; |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
$self->connect_if_necessary |
408
|
|
|
|
|
|
|
(sub { |
409
|
0
|
|
|
0
|
|
|
$self->{DB}{$id} = $stored_obj; |
410
|
0
|
|
|
|
|
|
}); |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=item _store_obj($object) |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
Store the object. Please note that there is a difference between a |
416
|
|
|
|
|
|
|
stored object (holding additional data like children, parents etc.) |
417
|
|
|
|
|
|
|
and the mere object. |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
=cut |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
sub _store_obj { |
422
|
0
|
|
|
0
|
|
|
my($self, $obj) = @_; |
423
|
|
|
|
|
|
|
$self->connect_if_necessary |
424
|
|
|
|
|
|
|
(sub { |
425
|
0
|
|
|
0
|
|
|
my $id = $obj->Id; |
426
|
0
|
0
|
|
|
|
|
if (!defined $id) { |
427
|
0
|
|
|
|
|
|
$id = $self->_next_id; |
428
|
0
|
|
|
|
|
|
$obj->Id($id); |
429
|
|
|
|
|
|
|
} |
430
|
0
|
|
|
|
|
|
my $o = $self->{DB}{$id}; |
431
|
0
|
0
|
|
|
|
|
if (!$o) { |
432
|
0
|
|
|
|
|
|
$o = []; |
433
|
0
|
|
|
|
|
|
$o->[PARENTS] = []; |
434
|
0
|
|
|
|
|
|
$o->[CHILDREN] = []; |
435
|
0
|
|
|
|
|
|
$o->[VERSIONS] = []; |
436
|
|
|
|
|
|
|
} |
437
|
0
|
|
|
|
|
|
$o->[OBJECT] = $obj; |
438
|
|
|
|
|
|
|
|
439
|
0
|
|
|
|
|
|
$self->{DB}{$id} = $o; |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
# return stored object |
442
|
0
|
|
|
|
|
|
$o; |
443
|
0
|
|
|
|
|
|
}); |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=item _get_stored_obj($object_id) |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
Get a stored object. |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=cut |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
sub _get_stored_obj { |
453
|
0
|
|
|
0
|
|
|
my($self, $id) = @_; |
454
|
|
|
|
|
|
|
$self->connect_if_necessary |
455
|
|
|
|
|
|
|
(sub { |
456
|
0
|
|
|
0
|
|
|
$self->{DB}{$id}; |
457
|
0
|
|
|
|
|
|
}); |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=item get_object($object_id) |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
Get an object by id. |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=cut |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
sub get_object { |
467
|
0
|
|
|
0
|
1
|
|
my($self, $obj_id) = @_; |
468
|
0
|
|
|
|
|
|
my $o = $self->_get_stored_obj($obj_id); |
469
|
0
|
0
|
|
|
|
|
$o ? $o->[OBJECT] : undef; |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
=item exists($object_id) |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
Return true if the object exists. Parameter is the object id. |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
=cut |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
sub exists { |
479
|
0
|
|
|
0
|
1
|
|
my($self, $obj_id) = @_; |
480
|
0
|
|
|
|
|
|
defined $self->_get_stored_obj($obj_id); |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=item children_ids($object_id) |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
Return a list of the children ids of this object. If the object does |
486
|
|
|
|
|
|
|
not exist or the object has not children, return an empty list. |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
=cut |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
sub children_ids { |
491
|
0
|
|
|
0
|
1
|
|
my($self, $obj_id) = @_; |
492
|
0
|
|
|
|
|
|
$self->idify_params($obj_id); |
493
|
0
|
|
|
|
|
|
my $o = $self->_get_stored_obj($obj_id); |
494
|
0
|
0
|
|
|
|
|
$o ? @{ $o->[CHILDREN] } : (); |
|
0
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
=item parent_ids($object_id) |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
Like children_ids, but return parent ids instead. |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
=cut |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
sub parent_ids { |
504
|
0
|
|
|
0
|
1
|
|
my($self, $obj_id) = @_; |
505
|
0
|
|
|
|
|
|
$self->idify_params($obj_id); |
506
|
0
|
|
|
|
|
|
my $o = $self->_get_stored_obj($obj_id); |
507
|
0
|
0
|
|
|
|
|
$o ? @{ $o->[PARENTS] } : (); |
|
0
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
} |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
=item version_ids($object_id) |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
Like children_ids, but return version ids instead. |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
=cut |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
sub version_ids { |
517
|
0
|
|
|
0
|
1
|
|
my($self, $obj_id) = @_; |
518
|
0
|
|
|
|
|
|
$self->idify_params($obj_id); |
519
|
0
|
|
|
|
|
|
my $o = $self->_get_stored_obj($obj_id); |
520
|
0
|
0
|
|
|
|
|
$o ? @{ $o->[VERSIONS] } : (); |
|
0
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=item find_links($target_id) |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
Find links with the $target_id as target. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=cut |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
sub find_links { |
530
|
0
|
|
|
0
|
1
|
|
my($self, $target_id) = @_; |
531
|
0
|
|
|
|
|
|
$self->idify_params($target_id); |
532
|
0
|
|
|
|
|
|
my @obj_ids; |
533
|
0
|
0
|
|
|
|
|
if ($self->Root->LinkDB) { |
534
|
0
|
|
|
|
|
|
@obj_ids = $self->Root->LinkDB->find_links($target_id); |
535
|
|
|
|
|
|
|
} else { |
536
|
|
|
|
|
|
|
$self->connect_if_necessary |
537
|
|
|
|
|
|
|
(sub { |
538
|
0
|
|
|
0
|
|
|
while(my($id, $stored_obj) = each %{ $self->{DB} }) { |
|
0
|
|
|
|
|
|
|
539
|
0
|
0
|
|
|
|
|
next if $id =~ /^_/; |
540
|
0
|
|
|
|
|
|
foreach my $idx (PARENTS, CHILDREN, VERSIONS) { |
541
|
0
|
|
|
|
|
|
foreach (@{ $stored_obj->[$idx] }) { |
|
0
|
|
|
|
|
|
|
542
|
0
|
0
|
|
|
|
|
if ($_ eq $target_id) { |
543
|
0
|
|
|
|
|
|
push @obj_ids, $stored_obj->[OBJECT]->Id; |
544
|
0
|
|
|
|
|
|
next; |
545
|
|
|
|
|
|
|
} |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
} |
549
|
0
|
|
|
|
|
|
}); |
550
|
|
|
|
|
|
|
} |
551
|
0
|
|
|
|
|
|
@obj_ids; |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
sub _remove_from_link_array { |
555
|
0
|
|
|
0
|
|
|
my($self, $id, $stored_obj) = @_; |
556
|
0
|
|
|
|
|
|
foreach my $idx (PARENTS, CHILDREN, VERSIONS) { |
557
|
0
|
|
|
|
|
|
my $i = 0; |
558
|
0
|
|
|
|
|
|
foreach (@{ $stored_obj->[$idx] }) { |
|
0
|
|
|
|
|
|
|
559
|
0
|
0
|
|
|
|
|
if ($_ eq $id) { |
560
|
0
|
|
|
|
|
|
splice @{ $stored_obj->[$idx] }, $i, 1; |
|
0
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
} |
562
|
0
|
|
|
|
|
|
$i++; |
563
|
|
|
|
|
|
|
} |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
=item unlink($object_id, $parent_id, %args) |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
Remove the given parent link from the object. If there is no parent |
570
|
|
|
|
|
|
|
link anymore, remove the whole object. |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
Remaining arguments are passed to the B method (see there). |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
=cut |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
sub unlink { |
577
|
0
|
|
|
0
|
1
|
|
my($self, $obj_id, $parent_id, %args) = @_; |
578
|
0
|
|
|
|
|
|
$self->idify_params($obj_id, $parent_id); |
579
|
0
|
|
|
|
|
|
my $parent_stored_obj = $self->_get_stored_obj($parent_id); |
580
|
0
|
0
|
|
|
|
|
die "Can't get parent object with id $parent_id" unless $parent_stored_obj; |
581
|
0
|
|
|
|
|
|
my $stored_obj = $self->_get_stored_obj($obj_id); |
582
|
0
|
0
|
|
|
|
|
die "Can't get object with id $obj_id" unless $stored_obj; |
583
|
|
|
|
|
|
|
|
584
|
0
|
|
|
|
|
|
my $i = 0; |
585
|
0
|
|
|
|
|
|
foreach (@{ $parent_stored_obj->[CHILDREN] }) { |
|
0
|
|
|
|
|
|
|
586
|
0
|
0
|
|
|
|
|
if ($_ eq $obj_id) { |
587
|
0
|
|
|
|
|
|
splice @{ $parent_stored_obj->[CHILDREN] }, $i, 1; |
|
0
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
} |
589
|
0
|
|
|
|
|
|
$i++; |
590
|
|
|
|
|
|
|
} |
591
|
0
|
|
|
|
|
|
$self->_store_stored_obj($parent_stored_obj); |
592
|
|
|
|
|
|
|
|
593
|
0
|
|
|
|
|
|
$i = 0; |
594
|
0
|
|
|
|
|
|
foreach (@{ $stored_obj->[PARENTS] }) { |
|
0
|
|
|
|
|
|
|
595
|
0
|
0
|
|
|
|
|
if ($_ eq $parent_id) { |
596
|
0
|
|
|
|
|
|
splice @{ $stored_obj->[PARENTS] }, $i, 1; |
|
0
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
} |
598
|
0
|
|
|
|
|
|
$i++; |
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
|
601
|
0
|
0
|
|
|
|
|
if (!@{ $stored_obj->[PARENTS] }) { |
|
0
|
|
|
|
|
|
|
602
|
0
|
|
|
|
|
|
$self->remove($obj_id, %args); |
603
|
|
|
|
|
|
|
} else { |
604
|
0
|
|
|
|
|
|
$self->_store_stored_obj($stored_obj); |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
} |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
=item link($object_id, $folder_id) |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
Link an object to a folder. This can be used to create multiple links. |
611
|
|
|
|
|
|
|
It is possible to create multiple links from one object to another --- |
612
|
|
|
|
|
|
|
this behaviour may change XXX. See also L. |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=cut |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
# XXX cycle detection is missing |
617
|
|
|
|
|
|
|
sub link { |
618
|
0
|
|
|
0
|
1
|
|
my($self, $obj_id, $folder_id) = @_; |
619
|
0
|
|
|
|
|
|
$self->idify_params($obj_id, $folder_id); |
620
|
0
|
|
|
|
|
|
my $stored_obj = $self->_get_stored_obj($obj_id); |
621
|
0
|
0
|
|
|
|
|
die "Can't get object with id $obj_id" unless $stored_obj; |
622
|
|
|
|
|
|
|
# XXX use insertable types? |
623
|
|
|
|
|
|
|
# XXX permission manager |
624
|
0
|
|
|
|
|
|
my $folder_stored_obj = $self->_get_stored_obj($folder_id); |
625
|
0
|
0
|
|
|
|
|
die "Can't get folder object with id $folder_id" unless $folder_stored_obj; |
626
|
0
|
|
|
|
|
|
push @{ $stored_obj->[PARENTS] }, $folder_id; |
|
0
|
|
|
|
|
|
|
627
|
0
|
|
|
|
|
|
push @{ $folder_stored_obj->[CHILDREN] }, $obj_id; |
|
0
|
|
|
|
|
|
|
628
|
0
|
|
|
|
|
|
$self->_store_stored_obj($stored_obj); |
629
|
0
|
|
|
|
|
|
$self->_store_stored_obj($folder_stored_obj); |
630
|
|
|
|
|
|
|
} |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
=item remove($object_id, %args) |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
Remove the object $obj_id and all links to this object uncoditionally. |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
If -links => "unhandled" is specified, then links to this object won't |
637
|
|
|
|
|
|
|
get removed. This is dangerous, and needs an additional L run |
638
|
|
|
|
|
|
|
afterwards. This option is useful if a mass-delete should be done. |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=cut |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
sub remove { |
643
|
0
|
|
|
0
|
1
|
|
my($self, $obj_id, %args) = @_; |
644
|
0
|
|
|
|
|
|
$self->idify_params($obj_id); |
645
|
|
|
|
|
|
|
$self->connect_if_necessary |
646
|
|
|
|
|
|
|
(sub { |
647
|
0
|
|
|
0
|
|
|
my $stored_obj = $self->_get_stored_obj($obj_id); |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
# XXX Debugging! |
650
|
0
|
0
|
|
|
|
|
if (!$stored_obj->[OBJECT]) { |
651
|
0
|
|
|
|
|
|
require Data::Dumper; |
652
|
0
|
|
|
|
|
|
warn "SHOULD NOT HAPPEN: object $obj_id has no stored object"; |
653
|
0
|
|
|
|
|
|
warn Data::Dumper::Dumper($stored_obj); |
654
|
|
|
|
|
|
|
} |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
# remove content |
657
|
0
|
0
|
0
|
|
|
|
if (UNIVERSAL::isa($stored_obj->[OBJECT], ('WE::Obj::DocObj')) |
658
|
|
|
|
|
|
|
&& $self->Root->ContentDB) { |
659
|
0
|
|
|
|
|
|
$self->Root->ContentDB->remove($stored_obj->[OBJECT]); |
660
|
|
|
|
|
|
|
} |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
# unlink children |
663
|
0
|
|
|
|
|
|
foreach my $child_id (@{ $stored_obj->[CHILDREN] }) { |
|
0
|
|
|
|
|
|
|
664
|
0
|
|
|
|
|
|
$self->unlink($child_id, $obj_id); |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
# delete everything in name database |
668
|
0
|
0
|
|
|
|
|
if ($self->Root->NameDB) { |
669
|
0
|
|
|
|
|
|
my $o = $self->get_object($obj_id); |
670
|
0
|
|
|
|
|
|
$self->Root->NameDB->update([], [$o]); |
671
|
|
|
|
|
|
|
} |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
# delete physical object |
674
|
0
|
|
|
|
|
|
delete $self->{DB}{$obj_id}; |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
# delete remaining links |
677
|
0
|
0
|
0
|
|
|
|
if (!$args{'-links'} || $args{'-links'} ne "unhandled") { |
678
|
0
|
|
|
|
|
|
my @obj_ids = $self->find_links($obj_id); |
679
|
0
|
|
|
|
|
|
foreach my $id (@obj_ids) { |
680
|
0
|
|
|
|
|
|
my $stored_obj = $self->_get_stored_obj($id); |
681
|
0
|
|
|
|
|
|
$self->_remove_from_link_array($obj_id, $stored_obj); |
682
|
0
|
|
|
|
|
|
$self->_store_stored_obj($stored_obj); |
683
|
|
|
|
|
|
|
} |
684
|
|
|
|
|
|
|
} |
685
|
|
|
|
|
|
|
|
686
|
0
|
|
|
|
|
|
}); |
687
|
|
|
|
|
|
|
} |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
=item insert_doc(%args) |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
Insert a document. |
692
|
|
|
|
|
|
|
The following arguments should be given: |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
-content: a string to the content or |
695
|
|
|
|
|
|
|
-file: the filename for the content |
696
|
|
|
|
|
|
|
-parent: the id of the parent |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
Other arguments will be used as attributes for the object, e.g. |
699
|
|
|
|
|
|
|
-ContentType will be used as the ContentType attribute and -Title as |
700
|
|
|
|
|
|
|
the title attribute. Note that these attributes are typically starting |
701
|
|
|
|
|
|
|
with an uppercase letter. |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
Return the generated object. |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
=cut |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
sub insert_doc { |
708
|
0
|
|
|
0
|
1
|
|
my($self, %args) = @_; |
709
|
0
|
|
|
|
|
|
my $doc = WE::Obj::Doc->new; |
710
|
0
|
|
|
|
|
|
$self->insert_doc_obj($doc, %args); |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
sub insert_doc_obj { |
714
|
0
|
|
|
0
|
0
|
|
my($self, $doc, %args) = @_; |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
# XXX permission manager |
717
|
0
|
|
|
|
|
|
my $content = delete $args{-content}; |
718
|
0
|
|
|
|
|
|
my $file = delete $args{-file}; |
719
|
0
|
|
|
|
|
|
my $parent = delete $args{-parent}; |
720
|
0
|
|
|
|
|
|
while(my($k,$v) = each %args) { |
721
|
0
|
0
|
|
|
|
|
die "Option does not start with a dash: $k" if $k !~ /^-/; |
722
|
0
|
|
|
|
|
|
$doc->{ucfirst(substr($k,1))} = $v; |
723
|
|
|
|
|
|
|
} |
724
|
0
|
0
|
|
|
|
|
if (defined $file) { |
725
|
0
|
0
|
|
|
|
|
$doc->{ContentType} = $self->Root->ContentDB->get_mime_type_by_filename($file) if !$doc->{ContentType}; |
726
|
0
|
0
|
|
|
|
|
open(F, $file) or die "Can't open file $file: $!"; |
727
|
0
|
|
|
|
|
|
local $/ = undef; |
728
|
0
|
|
|
|
|
|
$content = ; |
729
|
0
|
|
|
|
|
|
close F; |
730
|
|
|
|
|
|
|
|
731
|
0
|
|
|
|
|
|
require File::Basename; |
732
|
0
|
|
|
|
|
|
my $base = File::Basename::basename($file); |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
# auto set title |
735
|
0
|
0
|
|
|
|
|
if (!defined $doc->{Title}) { |
736
|
0
|
0
|
|
|
|
|
if ($base =~ /^(.+)(\.[^.]+)$/) { |
737
|
0
|
|
|
|
|
|
$doc->{Title} = $1; # stripped extension |
738
|
|
|
|
|
|
|
} else { |
739
|
0
|
|
|
|
|
|
$doc->{Title} = $base; # there is no extension |
740
|
|
|
|
|
|
|
} |
741
|
|
|
|
|
|
|
} |
742
|
|
|
|
|
|
|
|
743
|
0
|
0
|
|
|
|
|
if (!defined $doc->{Basename}) { |
744
|
0
|
|
|
|
|
|
$doc->{Basename} = $base; |
745
|
|
|
|
|
|
|
} |
746
|
|
|
|
|
|
|
} |
747
|
|
|
|
|
|
|
|
748
|
0
|
0
|
|
|
|
|
$doc->ContentType("text/html") if !$doc->{ContentType}; # i.e. content given |
749
|
0
|
|
|
|
|
|
$self->insert($doc, -parent => $parent); |
750
|
0
|
|
|
|
|
|
$self->Root->ContentDB->store($doc, $content); |
751
|
0
|
|
|
|
|
|
$doc; |
752
|
|
|
|
|
|
|
} |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
=item insert_folder(%args) |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
Insert a folder. |
757
|
|
|
|
|
|
|
The following arguments should be given: |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
-parent: the id of the parent |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
Return the generated object. |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
=cut |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
sub insert_folder { |
766
|
0
|
|
|
0
|
1
|
|
my($self, %args) = @_; |
767
|
0
|
|
|
|
|
|
my $folder = WE::Obj::Folder->new; |
768
|
|
|
|
|
|
|
# XXX permission manager |
769
|
0
|
|
|
|
|
|
my $parent = delete $args{-parent}; |
770
|
0
|
|
|
|
|
|
while(my($k,$v) = each %args) { |
771
|
0
|
0
|
|
|
|
|
die "Option does not start with a dash: $k" if $k !~ /^-/; |
772
|
0
|
|
|
|
|
|
my $member = ucfirst(substr($k,1)); |
773
|
0
|
0
|
|
|
|
|
if ($folder->can($member)) { |
774
|
0
|
|
|
|
|
|
$folder->$member($v); |
775
|
|
|
|
|
|
|
} else { |
776
|
0
|
|
|
|
|
|
$folder->{$member} = $v; |
777
|
|
|
|
|
|
|
} |
778
|
|
|
|
|
|
|
} |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
### XXX autogenerate basename here? |
781
|
|
|
|
|
|
|
# if (!defined $folder->{Basename}) { |
782
|
|
|
|
|
|
|
# $folder->{Basename} = langstring($folder->{Title}, $self->Root->CurrentLang); |
783
|
|
|
|
|
|
|
# } |
784
|
|
|
|
|
|
|
|
785
|
0
|
|
|
|
|
|
$self->insert($folder, -parent => $parent); |
786
|
|
|
|
|
|
|
} |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
=item insert($object, %args) |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
General method for inserting objects. You will mostly use either |
791
|
|
|
|
|
|
|
insert_doc or insert_folder. |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
Arguments: C<-parent> for parent object. |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
Return the generated object. |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
=cut |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
sub insert { |
800
|
0
|
|
|
0
|
1
|
|
my($self, $obj, %args) = @_; |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
$self->connect_if_necessary(sub { |
803
|
0
|
|
|
0
|
|
|
my $parent = delete $args{-parent}; |
804
|
0
|
0
|
|
|
|
|
if (!defined $parent) { |
805
|
0
|
|
|
|
|
|
die "The -parent option is missing"; |
806
|
|
|
|
|
|
|
} |
807
|
0
|
|
|
|
|
|
$self->idify_params($parent); |
808
|
0
|
|
|
|
|
|
my $parent_stored_obj = $self->_get_stored_obj($parent); |
809
|
0
|
0
|
|
|
|
|
if (!$parent_stored_obj) { |
810
|
0
|
|
|
|
|
|
die "There is no parent with id $parent"; |
811
|
|
|
|
|
|
|
} |
812
|
0
|
|
|
|
|
|
my $parent_obj = $parent_stored_obj->[OBJECT]; |
813
|
0
|
0
|
|
|
|
|
if (!$parent_obj->isa("WE::Obj::FolderObj")) { |
814
|
0
|
|
|
|
|
|
die "The object with the id $parent is not a FolderObj, but a " . ref $parent_obj . ". Objects can only be inserted in folders."; |
815
|
|
|
|
|
|
|
} |
816
|
0
|
0
|
|
|
|
|
if (!$parent_obj->object_is_insertable($obj)) { |
817
|
0
|
|
|
|
|
|
die "The object type " . ref($obj) . " is not allowed in " . ref($parent_obj) . ". The only allowed object types are: " . join(", ", @{ $parent_obj->insertable_types }); |
|
0
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
} |
819
|
0
|
|
|
|
|
|
my $id = $self->_next_id; |
820
|
0
|
|
|
|
|
|
push @{$parent_stored_obj->[CHILDREN]}, $id; |
|
0
|
|
|
|
|
|
|
821
|
0
|
|
|
|
|
|
$self->_store_stored_obj($parent_stored_obj); |
822
|
|
|
|
|
|
|
|
823
|
0
|
|
|
|
|
|
$obj->Id($id); |
824
|
0
|
|
|
|
|
|
my $owner = $self->Root->CurrentUser; |
825
|
0
|
0
|
|
|
|
|
if (defined $owner) { |
826
|
0
|
|
|
|
|
|
$obj->Owner($owner); |
827
|
|
|
|
|
|
|
} else { |
828
|
0
|
|
|
|
|
|
$obj->Owner(undef); # no owner |
829
|
|
|
|
|
|
|
} |
830
|
0
|
|
|
|
|
|
my $now = epoch2isodate(); |
831
|
0
|
|
|
|
|
|
$obj->TimeCreated($now); |
832
|
0
|
|
|
|
|
|
$obj->TimeModified($now); |
833
|
0
|
|
|
|
|
|
my $obj_stored_obj = $self->_create_stored_obj; |
834
|
0
|
|
|
|
|
|
$obj_stored_obj->[OBJECT] = $obj; |
835
|
0
|
|
|
|
|
|
$obj_stored_obj->[PARENTS] = [$parent]; |
836
|
0
|
|
|
|
|
|
$self->_store_stored_obj($obj_stored_obj); |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
# update names, links ... |
839
|
0
|
0
|
|
|
|
|
if ($self->Root->NameDB) { |
840
|
0
|
|
|
|
|
|
$self->Root->NameDB->update([$obj],[]); |
841
|
|
|
|
|
|
|
} |
842
|
0
|
|
|
|
|
|
}); |
843
|
|
|
|
|
|
|
|
844
|
0
|
|
|
|
|
|
$obj; |
845
|
|
|
|
|
|
|
} |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
sub _insert_version { |
848
|
0
|
|
|
0
|
|
|
my($self, $obj, %args) = @_; |
849
|
|
|
|
|
|
|
|
850
|
0
|
|
|
|
|
|
my $version_parent = delete $args{-versionparent}; |
851
|
0
|
|
|
|
|
|
$self->idify_params($version_parent); |
852
|
0
|
|
|
|
|
|
my $parent_stored_obj = $self->_get_stored_obj($version_parent); |
853
|
0
|
|
|
|
|
|
my $id = $self->_next_id; |
854
|
0
|
|
|
|
|
|
push @{$parent_stored_obj->[VERSIONS]}, $id; |
|
0
|
|
|
|
|
|
|
855
|
0
|
|
|
|
|
|
$self->_store_stored_obj($parent_stored_obj); |
856
|
|
|
|
|
|
|
|
857
|
0
|
|
|
|
|
|
$obj->Id($id); |
858
|
0
|
|
|
|
|
|
$obj->Version_Parent($version_parent); |
859
|
0
|
|
|
|
|
|
my $owner = $self->Root->CurrentUser; |
860
|
0
|
0
|
|
|
|
|
if (defined $owner) { |
861
|
0
|
|
|
|
|
|
$obj->Version_Owner($owner); |
862
|
|
|
|
|
|
|
} else { |
863
|
0
|
|
|
|
|
|
$obj->Version_Owner(undef); # no owner |
864
|
|
|
|
|
|
|
} |
865
|
0
|
|
|
|
|
|
my $now = epoch2isodate(); |
866
|
0
|
|
|
|
|
|
$obj->Version_Time($now); |
867
|
0
|
0
|
|
|
|
|
if (defined $args{-log}) { |
868
|
0
|
|
|
|
|
|
$obj->Version_Comment($args{-log}); |
869
|
|
|
|
|
|
|
} |
870
|
0
|
0
|
|
|
|
|
if (defined $args{-number}) { |
871
|
0
|
|
|
|
|
|
$obj->Version_Number($args{-number}); |
872
|
|
|
|
|
|
|
} |
873
|
|
|
|
|
|
|
|
874
|
0
|
|
|
|
|
|
my $obj_stored_obj = $self->_create_stored_obj; |
875
|
0
|
|
|
|
|
|
$obj_stored_obj->[OBJECT] = $obj; |
876
|
0
|
|
|
|
|
|
$self->_store_stored_obj($obj_stored_obj); |
877
|
|
|
|
|
|
|
|
878
|
0
|
|
|
|
|
|
$obj; |
879
|
|
|
|
|
|
|
} |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
=item content($object_id) |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
Get the content for the given object. |
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
=cut |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
sub content { |
888
|
0
|
|
|
0
|
1
|
|
my($self, $objid) = @_; |
889
|
0
|
|
|
|
|
|
$self->idify_params($objid); |
890
|
|
|
|
|
|
|
# XXX permission manager |
891
|
0
|
|
|
|
|
|
my $obj = $self->get_object($objid); |
892
|
0
|
|
|
|
|
|
$self->Root->ContentDB->get_content($obj); |
893
|
|
|
|
|
|
|
} |
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
=item replace_content($object_id, $content) |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
Replace the content of an existing object. Return the object itself. |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
=cut |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
sub replace_content { |
902
|
0
|
|
|
0
|
1
|
|
my($self, $objid, $new_content) = @_; |
903
|
0
|
|
|
|
|
|
$self->idify_params($objid); |
904
|
0
|
|
0
|
|
|
|
my $obj = $self->get_object($objid) || die "Can't get object for id $objid"; |
905
|
0
|
|
|
|
|
|
$obj->TimeModified(epoch2isodate()); |
906
|
0
|
|
|
|
|
|
$obj->Dirty(1); |
907
|
0
|
|
|
|
|
|
$obj->DirtyContent(1); |
908
|
0
|
|
|
|
|
|
$self->_store_obj($obj); |
909
|
0
|
|
|
|
|
|
$self->Root->ContentDB->store($obj, $new_content); |
910
|
0
|
|
|
|
|
|
$obj; |
911
|
|
|
|
|
|
|
} |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
=item flush |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
Flushes all changes, so they are visible to other processes. This is |
916
|
|
|
|
|
|
|
done automatically on end of the program or if the object is |
917
|
|
|
|
|
|
|
destroyed. |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
=cut |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
sub flush { |
922
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
923
|
0
|
0
|
|
|
|
|
return if !$self->Connected; |
924
|
0
|
|
|
|
|
|
(tied %{$self->{DB}})->sync; |
|
0
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
} |
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
=item replace_object($object) |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
Replace the given object. Argument is an object. This object should |
930
|
|
|
|
|
|
|
contain the valid id. Return the object itself. |
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
=cut |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
sub replace_object { |
935
|
0
|
|
|
0
|
1
|
|
my($self, $obj) = @_; |
936
|
|
|
|
|
|
|
# XXX permission manager |
937
|
0
|
|
|
|
|
|
my $stored_obj = $self->_get_stored_obj($obj->Id); |
938
|
0
|
0
|
|
|
|
|
die "Can't get stored object from id " . $obj->Id if !$stored_obj; |
939
|
0
|
|
|
|
|
|
my $namedb = $self->Root->NameDB; |
940
|
0
|
|
|
|
|
|
my $clone; |
941
|
0
|
0
|
|
|
|
|
if ($namedb) { |
942
|
0
|
|
|
|
|
|
$clone = $stored_obj->[OBJECT]->clone; |
943
|
|
|
|
|
|
|
} |
944
|
0
|
|
|
|
|
|
$obj->TimeModified(epoch2isodate()); |
945
|
0
|
|
|
|
|
|
$obj->Dirty(1); |
946
|
0
|
|
|
|
|
|
$obj->DirtyAttributes(1); |
947
|
0
|
|
|
|
|
|
$stored_obj->[OBJECT] = $obj; |
948
|
0
|
|
|
|
|
|
$self->_store_stored_obj($stored_obj); |
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
# update names, links ... |
951
|
0
|
0
|
|
|
|
|
if ($namedb) { |
952
|
0
|
|
|
|
|
|
$namedb->update([$obj],[$clone]); |
953
|
|
|
|
|
|
|
} |
954
|
|
|
|
|
|
|
|
955
|
0
|
|
|
|
|
|
$obj; |
956
|
|
|
|
|
|
|
} |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
=item is_ancestor($object_id, $ancestor_id) |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
Return true if $ancestor_id is an ancestor of $object_id. |
961
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
=cut |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
sub is_ancestor { |
965
|
0
|
|
|
0
|
1
|
|
my($self, $object_id, $ancestor_id) = @_; |
966
|
0
|
|
|
|
|
|
$self->idify_params($object_id, $ancestor_id); |
967
|
0
|
|
|
|
|
|
my @pathobjects = $self->pathobjects($object_id); |
968
|
0
|
|
|
|
|
|
pop @pathobjects; # remove itself |
969
|
0
|
|
|
|
|
|
for my $o (@pathobjects) { |
970
|
0
|
0
|
|
|
|
|
return 1 if ($o->Id eq $ancestor_id); |
971
|
|
|
|
|
|
|
} |
972
|
0
|
|
|
|
|
|
0; |
973
|
|
|
|
|
|
|
} |
974
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
=item copy($object_id, $folder_id, %args) |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
Copies the object identified by $object_id to the folder identified by |
978
|
|
|
|
|
|
|
$folder_id. Both the object metadata and the content are copied. |
979
|
|
|
|
|
|
|
Folders are copied by default recursively. To only copy the folder |
980
|
|
|
|
|
|
|
object, use C<-recursive =E 0> in the %args parameter hash. |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
Return the copied object. If there is a recursive copy, then return a |
983
|
|
|
|
|
|
|
list of copied objects. In this list, the first object is the copied |
984
|
|
|
|
|
|
|
top folder. In scalar context, always return only the first (or only) |
985
|
|
|
|
|
|
|
copied object. |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
Version information is never copied (yet). |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
=cut |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
sub copy { |
992
|
0
|
|
|
0
|
1
|
|
my($self, $object_id, $target_id, %args) = @_; |
993
|
0
|
0
|
|
|
|
|
die "Cannot copy object $object_id into itself" |
994
|
|
|
|
|
|
|
if $target_id eq $object_id; |
995
|
0
|
0
|
|
|
|
|
die "Cannot copy $object_id into descendent object $target_id" |
996
|
|
|
|
|
|
|
if $self->is_ancestor($target_id, $object_id); |
997
|
0
|
0
|
|
|
|
|
$args{-mapping} = {} if !$args{-mapping}; |
998
|
0
|
|
|
|
|
|
my @copied = $self->_copy($object_id, -parent => $target_id, %args); |
999
|
0
|
|
|
|
|
|
$self->remap_attribute_links([ values %{ $args{-mapping} } ], |
|
0
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
$args{-mapping}); |
1001
|
|
|
|
|
|
|
# We have to remap the objects, because they might be changed |
1002
|
|
|
|
|
|
|
# in remap_attribute_links. |
1003
|
0
|
|
|
|
|
|
@copied = map { $self->get_object($_->Id) } @copied; |
|
0
|
|
|
|
|
|
|
1004
|
0
|
0
|
|
|
|
|
wantarray ? @copied : $copied[0]; |
1005
|
|
|
|
|
|
|
} |
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
sub remap_attribute_links { |
1008
|
0
|
|
|
0
|
0
|
|
my($self, $object_ids, $mapping) = @_; |
1009
|
|
|
|
|
|
|
$self->connect_if_necessary |
1010
|
|
|
|
|
|
|
(sub { |
1011
|
0
|
|
|
0
|
|
|
for my $objid (@$object_ids) { |
1012
|
0
|
|
|
|
|
|
my $o = $self->get_object($objid); |
1013
|
0
|
|
|
|
|
|
my $changed; |
1014
|
0
|
0
|
0
|
|
|
|
if ($o->can("IndexDoc") && |
|
|
|
0
|
|
|
|
|
1015
|
|
|
|
|
|
|
defined $o->IndexDoc && |
1016
|
|
|
|
|
|
|
exists $mapping->{$o->IndexDoc}) { |
1017
|
0
|
|
|
|
|
|
my $new = $mapping->{$o->IndexDoc}; |
1018
|
0
|
|
|
|
|
|
$o->IndexDoc($new); |
1019
|
0
|
|
|
|
|
|
$changed++; |
1020
|
|
|
|
|
|
|
} |
1021
|
0
|
0
|
|
|
|
|
if ($changed) { |
1022
|
0
|
|
|
|
|
|
$self->replace_object($o); |
1023
|
|
|
|
|
|
|
} |
1024
|
|
|
|
|
|
|
} |
1025
|
0
|
|
|
|
|
|
}); |
1026
|
|
|
|
|
|
|
} |
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
=item ci($object_id, %args) |
1029
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
Check in the current version of the object with id C<$object_id>. You |
1031
|
|
|
|
|
|
|
can use additional parameters: |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
=over |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
=item -log => $log_message |
1036
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
Specify a log message for this version (recommended). C<-comment> is |
1038
|
|
|
|
|
|
|
an alias for C<-log>. |
1039
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
=item -number => $version_number |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
Normally, the version number is just incremented (e.g. from 1.0 to |
1043
|
|
|
|
|
|
|
1.1). If you like, you can specify another version number. There are |
1044
|
|
|
|
|
|
|
no checks for valid version numbers (that is, you can specify more |
1045
|
|
|
|
|
|
|
than one number, invalid formatted version numbers etc). C<-version> is an alias for C<-number>. |
1046
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
=item -trimold => $number_of_versions |
1048
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
If set to a value greater 0, then delete old versions. Set |
1050
|
|
|
|
|
|
|
$number_of_versions specify the number of versions you want to keep. |
1051
|
|
|
|
|
|
|
With -trimold => 1, all but the newest version will be wiped out. |
1052
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
=back |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
Return the checked-in objects. The original object is set to not dirty. |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
=cut |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
sub ci { |
1060
|
0
|
|
|
0
|
1
|
|
my($self, $object_id, %args) = @_; |
1061
|
0
|
0
|
|
|
|
|
if (defined $args{-version}) { |
1062
|
0
|
|
|
|
|
|
$args{-number} = delete $args{-version}; |
1063
|
|
|
|
|
|
|
} |
1064
|
0
|
0
|
|
|
|
|
if (!defined $args{-number}) { |
1065
|
0
|
|
|
|
|
|
$args{-number} = $self->_get_next_version($object_id); |
1066
|
|
|
|
|
|
|
} |
1067
|
0
|
0
|
|
|
|
|
if (defined $args{-comment}) { |
1068
|
0
|
|
|
|
|
|
$args{-log} = delete $args{-comment}; |
1069
|
|
|
|
|
|
|
} |
1070
|
0
|
|
|
|
|
|
my $trimold = delete $args{-trimold}; |
1071
|
0
|
|
|
|
|
|
my(@ret) = $self->_copy($object_id, |
1072
|
|
|
|
|
|
|
-versionparent => $object_id, %args); |
1073
|
|
|
|
|
|
|
|
1074
|
0
|
0
|
|
|
|
|
if ($trimold) { |
1075
|
0
|
|
|
|
|
|
$self->trim_old_versions($object_id, -trimold => $trimold); |
1076
|
|
|
|
|
|
|
} |
1077
|
|
|
|
|
|
|
|
1078
|
0
|
|
|
|
|
|
$self->_undirty($object_id); |
1079
|
|
|
|
|
|
|
|
1080
|
0
|
0
|
|
|
|
|
wantarray ? @ret : $ret[0]; |
1081
|
|
|
|
|
|
|
} |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
=item trim_old_versions($object_id, [ -trimold => $number | -all => 1 ]) |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
Trim the last C<$number> versions of object C<$object_id>. If C<-all> |
1086
|
|
|
|
|
|
|
is used instead, then trim all old versions. C<-all> and C<-trimold> |
1087
|
|
|
|
|
|
|
are mutually exclusive. |
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
=cut |
1090
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
# XXX -all is not tested yet! |
1092
|
|
|
|
|
|
|
sub trim_old_versions { |
1093
|
0
|
|
|
0
|
1
|
|
my($self, $object, %args) = @_; |
1094
|
0
|
|
|
|
|
|
$self->objectify_params($object); |
1095
|
0
|
|
|
|
|
|
my $object_id = $object->Id; |
1096
|
0
|
|
|
|
|
|
my $trimold = delete $args{-trimold}; |
1097
|
0
|
|
|
|
|
|
my $all = delete $args{-all}; |
1098
|
0
|
0
|
|
|
|
|
if (keys %args) { die "Unknown argument: " . join ", ", keys %args } |
|
0
|
|
|
|
|
|
|
1099
|
0
|
0
|
0
|
|
|
|
return if !$trimold && !$all; |
1100
|
0
|
|
|
|
|
|
my(@versions) = $self->version_ids($object_id); |
1101
|
0
|
0
|
|
|
|
|
if (@versions > 0) { # XXX this used to be @versions>1, but that was probably wrong |
1102
|
0
|
|
|
|
|
|
my @newest_ids; |
1103
|
0
|
0
|
|
|
|
|
if ($all) { |
1104
|
0
|
|
|
|
|
|
@newest_ids = (); |
1105
|
|
|
|
|
|
|
} else { |
1106
|
0
|
|
|
|
|
|
@newest_ids = splice @versions, -$trimold; # don't trim the $trimold newest versions |
1107
|
|
|
|
|
|
|
} |
1108
|
0
|
|
|
|
|
|
foreach my $id (@versions) { |
1109
|
0
|
|
|
|
|
|
$self->remove($id); |
1110
|
|
|
|
|
|
|
} |
1111
|
0
|
|
|
|
|
|
eval{ |
1112
|
0
|
|
|
|
|
|
my $stored_obj = $self->_store_obj($object); |
1113
|
0
|
|
|
|
|
|
$stored_obj->[VERSIONS] = [@newest_ids]; |
1114
|
0
|
|
|
|
|
|
$self->_store_stored_obj($stored_obj); |
1115
|
0
|
0
|
|
|
|
|
};die "$@ $object $object_id @versions" if $@; |
1116
|
|
|
|
|
|
|
} |
1117
|
|
|
|
|
|
|
} |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
=item co($object_id [, -version => $version_number]) |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
NYI. |
1122
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
Check out the object with the version number C<$version_number>. If |
1124
|
|
|
|
|
|
|
version number is not given, then check out the latest version. If the |
1125
|
|
|
|
|
|
|
version number is not given and there are no versions at all, then an |
1126
|
|
|
|
|
|
|
exception will be thrown. Please note that a check out will override |
1127
|
|
|
|
|
|
|
the current object, so you probably should do a C first. No |
1128
|
|
|
|
|
|
|
locking is done (yet). |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
=cut |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
sub co { |
1133
|
0
|
|
|
0
|
1
|
|
my($self, $object_id, %args) = @_; |
1134
|
0
|
|
|
|
|
|
$self->idify_params($object_id); |
1135
|
0
|
0
|
|
|
|
|
if (defined $args{-version}) { |
1136
|
0
|
|
|
|
|
|
$args{-number} = delete $args{-version}; |
1137
|
|
|
|
|
|
|
} |
1138
|
0
|
|
|
|
|
|
my $v_obj; |
1139
|
0
|
0
|
|
|
|
|
if (!defined $args{-number}) { |
1140
|
0
|
|
|
|
|
|
my @v_id = $self->version_ids($object_id); |
1141
|
0
|
0
|
|
|
|
|
if (!@v_id) { |
1142
|
0
|
|
|
|
|
|
die "There are no versions available for object $object_id"; |
1143
|
|
|
|
|
|
|
} |
1144
|
0
|
|
|
|
|
|
$v_obj = $self->get_object($v_id[-1]); |
1145
|
|
|
|
|
|
|
} |
1146
|
0
|
0
|
|
|
|
|
if (!$v_obj) { |
1147
|
0
|
|
|
|
|
|
foreach my $v ($self->versions($object_id)) { |
1148
|
0
|
0
|
|
|
|
|
if ($v->Version_Number eq $args{-number}) { |
1149
|
0
|
|
|
|
|
|
$v_obj = $v; |
1150
|
0
|
|
|
|
|
|
last; |
1151
|
|
|
|
|
|
|
} |
1152
|
|
|
|
|
|
|
} |
1153
|
|
|
|
|
|
|
} |
1154
|
0
|
0
|
|
|
|
|
if (!$v_obj) { |
1155
|
0
|
|
|
|
|
|
die "Can't find version $args{-number} for object $object_id"; |
1156
|
|
|
|
|
|
|
} |
1157
|
|
|
|
|
|
|
|
1158
|
0
|
|
|
|
|
|
my $stored_obj = $self->_get_stored_obj($object_id); |
1159
|
0
|
|
|
|
|
|
my $old_o = $stored_obj->[OBJECT]; |
1160
|
0
|
|
|
|
|
|
$stored_obj->[OBJECT] = $v_obj; |
1161
|
0
|
|
|
|
|
|
$self->Root->ContentDB->copy($v_obj, $old_o); |
1162
|
0
|
|
|
|
|
|
$v_obj->Id($old_o->Id); |
1163
|
0
|
|
|
|
|
|
$self->_store_stored_obj($stored_obj); |
1164
|
0
|
|
|
|
|
|
$stored_obj->[OBJECT]; |
1165
|
|
|
|
|
|
|
} |
1166
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
sub _copy { |
1168
|
0
|
|
|
0
|
|
|
my($self, $object_id, %args) = @_; |
1169
|
0
|
|
|
|
|
|
$self->idify_params($object_id); |
1170
|
0
|
|
|
|
|
|
my $obj = $self->get_object($object_id); |
1171
|
0
|
0
|
|
|
|
|
die "Can't find object with id $object_id" if !$obj; |
1172
|
|
|
|
|
|
|
|
1173
|
0
|
|
|
|
|
|
my $mapping = delete $args{-mapping}; |
1174
|
|
|
|
|
|
|
|
1175
|
0
|
|
|
|
|
|
my %insert_args; |
1176
|
|
|
|
|
|
|
my $insert_meth; |
1177
|
0
|
0
|
|
|
|
|
if (defined $args{-parent}) { |
1178
|
0
|
|
|
|
|
|
my $target_id = delete $args{-parent}; |
1179
|
0
|
|
|
|
|
|
$self->idify_params($target_id); |
1180
|
0
|
|
|
|
|
|
my $target_obj = $self->get_object($target_id); |
1181
|
0
|
0
|
|
|
|
|
die "Target must be a folder" if !$target_obj->is_folder; |
1182
|
0
|
|
|
|
|
|
%insert_args = (-parent => $target_id, %args); |
1183
|
0
|
|
|
|
|
|
$insert_meth = "insert"; |
1184
|
|
|
|
|
|
|
} else { # new version |
1185
|
0
|
|
|
|
|
|
my $version_parent_id = delete $args{-versionparent}; |
1186
|
0
|
|
|
|
|
|
$self->idify_params($version_parent_id); |
1187
|
0
|
|
|
|
|
|
my $target_obj = $self->get_object($version_parent_id); |
1188
|
0
|
0
|
|
|
|
|
die "Target $version_parent_id does not exist" if !$target_obj; |
1189
|
0
|
|
|
|
|
|
%insert_args = (-versionparent => $version_parent_id, %args); |
1190
|
0
|
|
|
|
|
|
$insert_meth = "_insert_version"; |
1191
|
|
|
|
|
|
|
} |
1192
|
|
|
|
|
|
|
|
1193
|
0
|
0
|
|
|
|
|
if ($obj->is_doc) { |
1194
|
0
|
|
|
|
|
|
my $content = $self->content($object_id); |
1195
|
0
|
|
|
|
|
|
my $clone_obj = $obj->clone; |
1196
|
|
|
|
|
|
|
#XXX if (grep($_ eq $target_id, $self->parent_ids($object_id))) { |
1197
|
|
|
|
|
|
|
# # XXX NYI: change title to "Copy of ..." (lang-dependent) |
1198
|
|
|
|
|
|
|
# # XXX no: this is also called from ci()! |
1199
|
|
|
|
|
|
|
# } |
1200
|
0
|
|
|
|
|
|
$self->$insert_meth($clone_obj, %insert_args); |
1201
|
0
|
0
|
|
|
|
|
if ($mapping) { |
1202
|
0
|
|
|
|
|
|
$mapping->{$obj->Id} = $clone_obj->Id; |
1203
|
|
|
|
|
|
|
} |
1204
|
0
|
|
|
|
|
|
$self->replace_content($clone_obj, $content); |
1205
|
0
|
|
|
|
|
|
$clone_obj; |
1206
|
|
|
|
|
|
|
} else { # copy folder |
1207
|
0
|
|
|
|
|
|
my $clone_obj = $obj->clone; |
1208
|
|
|
|
|
|
|
#XXX if (grep($_ eq $target_id, $self->parent_ids($object_id))) { |
1209
|
|
|
|
|
|
|
# # XXX NYI: change title to "Copy of ..." (lang-dependent) |
1210
|
|
|
|
|
|
|
# # XXX no: this is also called from ci()! |
1211
|
|
|
|
|
|
|
# } |
1212
|
0
|
|
|
|
|
|
my @ret; |
1213
|
0
|
|
|
|
|
|
$self->$insert_meth($clone_obj, %insert_args); |
1214
|
0
|
0
|
|
|
|
|
if ($mapping) { |
1215
|
0
|
|
|
|
|
|
$mapping->{$obj->Id} = $clone_obj->Id; |
1216
|
|
|
|
|
|
|
} |
1217
|
0
|
|
|
|
|
|
push @ret, $clone_obj; |
1218
|
0
|
0
|
0
|
|
|
|
if (!exists $args{-recursive} || $args{-recursive}) { |
1219
|
0
|
|
|
|
|
|
foreach my $child_id ($self->children_ids($object_id)) { |
1220
|
0
|
0
|
|
|
|
|
if (exists $insert_args{-parent}) { |
1221
|
0
|
|
|
|
|
|
$insert_args{-parent} = $clone_obj->Id; |
1222
|
|
|
|
|
|
|
} else { |
1223
|
0
|
|
|
|
|
|
$insert_args{-versionparent} = $clone_obj->Id; |
1224
|
|
|
|
|
|
|
} |
1225
|
0
|
|
|
|
|
|
push @ret, $self->_copy($child_id, %insert_args, -mapping => $mapping); |
1226
|
|
|
|
|
|
|
} |
1227
|
|
|
|
|
|
|
} |
1228
|
0
|
|
|
|
|
|
@ret; |
1229
|
|
|
|
|
|
|
} |
1230
|
|
|
|
|
|
|
} |
1231
|
|
|
|
|
|
|
|
1232
|
|
|
|
|
|
|
=item move($object_id, $parent_id, %args) |
1233
|
|
|
|
|
|
|
|
1234
|
|
|
|
|
|
|
Move the object with C<$object_id> and linked to the parent |
1235
|
|
|
|
|
|
|
C<$parent_id> to another position or destination. If C<$parent_id> is |
1236
|
|
|
|
|
|
|
C, then the first found parent is used. If there are multiple |
1237
|
|
|
|
|
|
|
parents, then it is better to specify the right one. The C<%args> |
1238
|
|
|
|
|
|
|
portion may look like this: |
1239
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
=over 4 |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
=item -destination => $folder_id |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
Move the object to another folder. You can also use C<-target> as an |
1245
|
|
|
|
|
|
|
alias for C<-destination>. |
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
=item -after => $after_object_id |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
Leave the object in the same folder, but move it after the object with |
1250
|
|
|
|
|
|
|
the id C<$after_object_id>. If there is no such object in the folder, |
1251
|
|
|
|
|
|
|
then an exception is raised. |
1252
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
=item -before => $before_object_id |
1254
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
Same as C<-after>, but move the object before the specified object. |
1256
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
=item -to => "begin" | "end" |
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
Move the object to the beginning or end of the folder. For "begin", |
1260
|
|
|
|
|
|
|
you can also use "first" and for "end", you can use "last". |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
=back |
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
Return nothing. On error an exception will be raised. |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
=cut |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
sub move { |
1269
|
0
|
|
|
0
|
1
|
|
my($self, $objid, $parentid, %args) = @_; |
1270
|
0
|
|
|
|
|
|
$self->idify_params($objid); |
1271
|
0
|
0
|
|
|
|
|
if (!defined $parentid) { |
1272
|
0
|
|
|
|
|
|
$parentid = ($self->parent_ids($objid))[0]; |
1273
|
|
|
|
|
|
|
} |
1274
|
0
|
|
|
|
|
|
$self->idify_params($parentid); |
1275
|
|
|
|
|
|
|
|
1276
|
0
|
|
|
|
|
|
my $destination = delete $args{-destination}; |
1277
|
0
|
0
|
|
|
|
|
if (!defined $destination) { |
1278
|
0
|
|
|
|
|
|
$destination = delete $args{-target}; # Alias for -destination |
1279
|
|
|
|
|
|
|
} |
1280
|
0
|
|
|
|
|
|
my $after = delete $args{-after}; |
1281
|
0
|
|
|
|
|
|
my $before = delete $args{-before}; |
1282
|
0
|
|
|
|
|
|
my $to = delete $args{-to}; |
1283
|
|
|
|
|
|
|
|
1284
|
|
|
|
|
|
|
my $check_move = sub { |
1285
|
0
|
|
|
0
|
|
|
my($target_id) = @_; |
1286
|
0
|
0
|
|
|
|
|
die "Cannot move object $objid into itself" |
1287
|
|
|
|
|
|
|
if $target_id eq $objid; |
1288
|
0
|
0
|
|
|
|
|
die "Cannot move $objid into descendent object $target_id" |
1289
|
|
|
|
|
|
|
if $self->is_ancestor($target_id, $objid); |
1290
|
0
|
|
|
|
|
|
}; |
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
# XXX permission manager |
1293
|
0
|
0
|
0
|
|
|
|
if (defined $destination) { |
|
|
0
|
0
|
|
|
|
|
1294
|
0
|
|
|
|
|
|
$self->idify_params($destination); |
1295
|
0
|
|
|
|
|
|
$check_move->($destination); |
1296
|
|
|
|
|
|
|
# first link, then unlink (in this order!) |
1297
|
0
|
|
|
|
|
|
$self->link($objid, $destination); |
1298
|
0
|
|
|
|
|
|
$self->unlink($objid, $parentid); |
1299
|
|
|
|
|
|
|
} elsif (defined $before || defined $after || defined $to) { |
1300
|
0
|
|
|
|
|
|
my $parent_stored_obj = $self->_get_stored_obj($parentid); |
1301
|
0
|
|
|
|
|
|
my $moved; |
1302
|
0
|
0
|
|
|
|
|
if (defined $after) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1303
|
0
|
|
|
|
|
|
$self->idify_params($after); |
1304
|
0
|
0
|
|
|
|
|
return if $after eq $objid; |
1305
|
0
|
|
|
|
|
|
for(my $i=0; $i<=$#{ $parent_stored_obj->[CHILDREN] }; $i++) { |
|
0
|
|
|
|
|
|
|
1306
|
0
|
|
|
|
|
|
my $id = $parent_stored_obj->[CHILDREN][$i]; |
1307
|
0
|
0
|
|
|
|
|
if ($id eq $after) { |
|
|
0
|
|
|
|
|
|
1308
|
0
|
|
|
|
|
|
splice @{ $parent_stored_obj->[CHILDREN] }, $i+1, 0, $objid; |
|
0
|
|
|
|
|
|
|
1309
|
0
|
|
|
|
|
|
$moved = 1; |
1310
|
0
|
|
|
|
|
|
$i++; |
1311
|
|
|
|
|
|
|
} elsif ($id eq $objid) { |
1312
|
0
|
|
|
|
|
|
splice @{ $parent_stored_obj->[CHILDREN] }, $i, 1; |
|
0
|
|
|
|
|
|
|
1313
|
0
|
|
|
|
|
|
$i--; |
1314
|
|
|
|
|
|
|
} |
1315
|
|
|
|
|
|
|
} |
1316
|
|
|
|
|
|
|
} elsif (defined $before) { |
1317
|
0
|
|
|
|
|
|
$self->idify_params($before); |
1318
|
0
|
0
|
|
|
|
|
return if $before eq $objid; |
1319
|
0
|
|
|
|
|
|
for(my $i=0; $i<=$#{ $parent_stored_obj->[CHILDREN] }; $i++) { |
|
0
|
|
|
|
|
|
|
1320
|
0
|
|
|
|
|
|
my $id = $parent_stored_obj->[CHILDREN][$i]; |
1321
|
0
|
0
|
|
|
|
|
if ($id eq $before) { |
|
|
0
|
|
|
|
|
|
1322
|
0
|
|
|
|
|
|
splice @{ $parent_stored_obj->[CHILDREN] }, $i, 0, $objid; |
|
0
|
|
|
|
|
|
|
1323
|
0
|
|
|
|
|
|
$moved = 1; |
1324
|
0
|
|
|
|
|
|
$i++; |
1325
|
|
|
|
|
|
|
} elsif ($id eq $objid) { |
1326
|
0
|
|
|
|
|
|
splice @{ $parent_stored_obj->[CHILDREN] }, $i, 1; |
|
0
|
|
|
|
|
|
|
1327
|
0
|
|
|
|
|
|
$i--; |
1328
|
|
|
|
|
|
|
} |
1329
|
|
|
|
|
|
|
} |
1330
|
|
|
|
|
|
|
} elsif (defined $to) { |
1331
|
0
|
|
|
|
|
|
for(my $i=0; $i<=$#{ $parent_stored_obj->[CHILDREN] }; $i++) { |
|
0
|
|
|
|
|
|
|
1332
|
0
|
|
|
|
|
|
my $id = $parent_stored_obj->[CHILDREN][$i]; |
1333
|
0
|
0
|
|
|
|
|
if ($id eq $objid) { |
1334
|
0
|
|
|
|
|
|
splice @{ $parent_stored_obj->[CHILDREN] }, $i, 1; |
|
0
|
|
|
|
|
|
|
1335
|
0
|
0
|
|
|
|
|
if ($to =~ /^(begin|first)$/) { |
|
|
0
|
|
|
|
|
|
1336
|
0
|
|
|
|
|
|
unshift @{ $parent_stored_obj->[CHILDREN] }, $objid; |
|
0
|
|
|
|
|
|
|
1337
|
0
|
|
|
|
|
|
$moved = 1; |
1338
|
0
|
|
|
|
|
|
last; |
1339
|
|
|
|
|
|
|
} elsif ($to =~ /^(end|last)$/) { |
1340
|
0
|
|
|
|
|
|
push @{ $parent_stored_obj->[CHILDREN] }, $objid; |
|
0
|
|
|
|
|
|
|
1341
|
0
|
|
|
|
|
|
$moved = 1; |
1342
|
0
|
|
|
|
|
|
last; |
1343
|
|
|
|
|
|
|
} else { |
1344
|
0
|
|
|
|
|
|
die "Invalid -to specification. Must be -first, -last, -begin or -end"; |
1345
|
|
|
|
|
|
|
} |
1346
|
|
|
|
|
|
|
} |
1347
|
|
|
|
|
|
|
} |
1348
|
|
|
|
|
|
|
} |
1349
|
0
|
0
|
|
|
|
|
if (!$moved) { |
1350
|
0
|
|
|
|
|
|
die "The object $objid could not be moved in parent $parentid"; |
1351
|
|
|
|
|
|
|
} |
1352
|
0
|
|
|
|
|
|
$self->_store_stored_obj($parent_stored_obj); |
1353
|
|
|
|
|
|
|
} else { |
1354
|
0
|
|
|
|
|
|
die "Nowhere to move. Please specify either -destination, -before or -after"; |
1355
|
|
|
|
|
|
|
} |
1356
|
|
|
|
|
|
|
} |
1357
|
|
|
|
|
|
|
|
1358
|
|
|
|
|
|
|
=item dump(%args) |
1359
|
|
|
|
|
|
|
|
1360
|
|
|
|
|
|
|
Dump object structure as a string. Possible options: |
1361
|
|
|
|
|
|
|
|
1362
|
|
|
|
|
|
|
=over 4 |
1363
|
|
|
|
|
|
|
|
1364
|
|
|
|
|
|
|
=item -root => $object_id |
1365
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
Specify another object to start dumping from. If not specified, start |
1367
|
|
|
|
|
|
|
dumping from root object. |
1368
|
|
|
|
|
|
|
|
1369
|
|
|
|
|
|
|
=item -versions => $bool |
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
If true, then version information is also dumped. |
1372
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
=item -attributes => $bool |
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
If true, then attribute information is also dumped. |
1376
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
=item -children => $bool |
1378
|
|
|
|
|
|
|
|
1379
|
|
|
|
|
|
|
Recurse into children. This is by default true. |
1380
|
|
|
|
|
|
|
|
1381
|
|
|
|
|
|
|
=item -callback => $sub |
1382
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
A reference to a callback which can dump additional code. The |
1384
|
|
|
|
|
|
|
subroutine will get the following key-value pairs as arguments: |
1385
|
|
|
|
|
|
|
|
1386
|
|
|
|
|
|
|
=over 4 |
1387
|
|
|
|
|
|
|
|
1388
|
|
|
|
|
|
|
=item -obj |
1389
|
|
|
|
|
|
|
|
1390
|
|
|
|
|
|
|
The current object |
1391
|
|
|
|
|
|
|
|
1392
|
|
|
|
|
|
|
=item -level |
1393
|
|
|
|
|
|
|
|
1394
|
|
|
|
|
|
|
The current level |
1395
|
|
|
|
|
|
|
|
1396
|
|
|
|
|
|
|
=item -indentstring |
1397
|
|
|
|
|
|
|
|
1398
|
|
|
|
|
|
|
An indentation string |
1399
|
|
|
|
|
|
|
|
1400
|
|
|
|
|
|
|
=back |
1401
|
|
|
|
|
|
|
|
1402
|
|
|
|
|
|
|
The subroutine should return a string. See C in the |
1403
|
|
|
|
|
|
|
C script for an example. |
1404
|
|
|
|
|
|
|
|
1405
|
|
|
|
|
|
|
=back |
1406
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
=cut |
1408
|
|
|
|
|
|
|
|
1409
|
|
|
|
|
|
|
sub dump { |
1410
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1411
|
0
|
|
|
|
|
|
my %args = @_; |
1412
|
0
|
0
|
|
|
|
|
my $root_object = (defined $args{-root} |
1413
|
|
|
|
|
|
|
? $self->get_object(delete $args{-root}) |
1414
|
|
|
|
|
|
|
: $self->root_object |
1415
|
|
|
|
|
|
|
); |
1416
|
0
|
|
|
|
|
|
$self->_dump($root_object, 0, {}, %args); |
1417
|
|
|
|
|
|
|
} |
1418
|
|
|
|
|
|
|
|
1419
|
|
|
|
|
|
|
sub _dump { |
1420
|
0
|
|
|
0
|
|
|
my($self, $obj, $level, $seen, %args) = @_; |
1421
|
|
|
|
|
|
|
|
1422
|
0
|
|
|
|
|
|
my $s = " " x $level; |
1423
|
|
|
|
|
|
|
|
1424
|
0
|
0
|
|
|
|
|
if (!defined $obj) { |
1425
|
0
|
|
|
|
|
|
warn "Undefined object detected in level=$level. Probably children/parent structure or the database is damaged.\n"; |
1426
|
0
|
|
|
|
|
|
return $s . "\n"; |
1427
|
|
|
|
|
|
|
} |
1428
|
|
|
|
|
|
|
|
1429
|
0
|
0
|
|
|
|
|
if ($seen->{$obj->Id}) { |
1430
|
0
|
|
|
|
|
|
warn "Object with id already seen, no dumping from this point on...\n"; |
1431
|
0
|
|
|
|
|
|
return $s . "Id . ">\n"; |
1432
|
|
|
|
|
|
|
} |
1433
|
0
|
|
|
|
|
|
$seen->{$obj->Id}++; |
1434
|
|
|
|
|
|
|
|
1435
|
|
|
|
|
|
|
my $shorten = sub { |
1436
|
0
|
0
|
|
0
|
|
|
if (length $_[0] > $_[1]) { |
1437
|
0
|
|
|
|
|
|
substr($_[0], 0, $_[1]) |
1438
|
|
|
|
|
|
|
} else { |
1439
|
0
|
|
|
|
|
|
$_[0]; |
1440
|
|
|
|
|
|
|
} |
1441
|
0
|
|
|
|
|
|
}; |
1442
|
|
|
|
|
|
|
my $langstr = sub { |
1443
|
0
|
|
|
0
|
|
|
langstring($_[0], $self->Root->CurrentLang); |
1444
|
0
|
|
|
|
|
|
}; |
1445
|
|
|
|
|
|
|
|
1446
|
0
|
0
|
|
|
|
|
my $title = (defined $obj->Title |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1447
|
|
|
|
|
|
|
? $shorten->($langstr->($obj->Title), defined $obj->Version_Number ? 35-length($obj->Version_Number)-3 : 35) |
1448
|
|
|
|
|
|
|
: "(no title)" |
1449
|
|
|
|
|
|
|
) . (defined $obj->Version_Number ? " (".$obj->Version_Number.")" : ""); |
1450
|
|
|
|
|
|
|
|
1451
|
0
|
0
|
0
|
|
|
|
$s .= sprintf "%s %-35s " . (" "x(13-$level)) . "%-8s %-8s %4d\n", |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
($obj->is_sequence |
1453
|
|
|
|
|
|
|
? "s" |
1454
|
|
|
|
|
|
|
: $obj->is_folder |
1455
|
|
|
|
|
|
|
? "d" |
1456
|
|
|
|
|
|
|
: defined $obj->Version_Number |
1457
|
|
|
|
|
|
|
? "v" |
1458
|
|
|
|
|
|
|
: "-"), |
1459
|
|
|
|
|
|
|
$title, |
1460
|
|
|
|
|
|
|
$shorten->($obj->Owner || "(none)", 8), |
1461
|
|
|
|
|
|
|
defined $obj->TimeModified ? WE::Util::Date::short_readable_time(isodate2epoch($obj->TimeModified)) : "(none)", |
1462
|
|
|
|
|
|
|
$obj->Id; |
1463
|
0
|
0
|
|
|
|
|
if ($args{-versions}) { |
1464
|
0
|
|
|
|
|
|
foreach my $sub_obj ($self->versions($obj)) { |
1465
|
0
|
|
|
|
|
|
$s .= $self->_dump($sub_obj, $level+1, $seen, %args); |
1466
|
|
|
|
|
|
|
} |
1467
|
|
|
|
|
|
|
} |
1468
|
0
|
0
|
|
|
|
|
if ($args{-attributes}) { |
1469
|
0
|
|
|
|
|
|
foreach my $key (sort keys %$obj) { |
1470
|
0
|
|
|
|
|
|
my $val = $obj->{$key}; |
1471
|
0
|
0
|
|
|
|
|
if (UNIVERSAL::can($val, "dump")) { |
1472
|
0
|
|
|
|
|
|
$val = $val->dump; |
1473
|
|
|
|
|
|
|
} |
1474
|
0
|
0
|
|
|
|
|
if (!defined $val) { $val = "(undef)" } |
|
0
|
|
|
|
|
|
|
1475
|
0
|
|
|
|
|
|
$s .= " "x($level+1) . "|$key => $val" . "\n"; |
1476
|
|
|
|
|
|
|
} |
1477
|
0
|
|
|
|
|
|
my @parent_ids = $self->parent_ids($obj); |
1478
|
0
|
0
|
|
|
|
|
if (@parent_ids > 1) { |
1479
|
0
|
|
|
|
|
|
$s .= " "x($level+1) . "|Multiple parents => @parent_ids\n"; |
1480
|
|
|
|
|
|
|
} |
1481
|
|
|
|
|
|
|
} |
1482
|
0
|
0
|
|
|
|
|
if ($args{-callback}) { |
1483
|
0
|
|
|
|
|
|
my $callback_s = $args{-callback}->(-obj => $obj, -level => $level, |
1484
|
|
|
|
|
|
|
-indentstring => " "x($level+1), |
1485
|
|
|
|
|
|
|
); |
1486
|
0
|
0
|
|
|
|
|
$s .= $callback_s if defined $callback_s; |
1487
|
|
|
|
|
|
|
} |
1488
|
0
|
0
|
0
|
|
|
|
if ($obj->is_folder && (!exists $args{-children} || $args{-children})) { |
|
|
|
0
|
|
|
|
|
1489
|
0
|
|
|
|
|
|
foreach my $sub_obj ($self->children($obj)) { |
1490
|
0
|
|
|
|
|
|
$s .= $self->_dump($sub_obj, $level+1, $seen, %args); |
1491
|
|
|
|
|
|
|
} |
1492
|
|
|
|
|
|
|
} |
1493
|
0
|
|
|
|
|
|
$s; |
1494
|
|
|
|
|
|
|
} |
1495
|
|
|
|
|
|
|
|
1496
|
|
|
|
|
|
|
=item depth($obj_id) |
1497
|
|
|
|
|
|
|
|
1498
|
|
|
|
|
|
|
Get the minimum and maximum depth of the object. There are multiple |
1499
|
|
|
|
|
|
|
depths, because the object can be in multiple parents with different |
1500
|
|
|
|
|
|
|
depths. |
1501
|
|
|
|
|
|
|
|
1502
|
|
|
|
|
|
|
($min_depth, $max_depth) = $objdb->depth($obj_id); |
1503
|
|
|
|
|
|
|
|
1504
|
|
|
|
|
|
|
=cut |
1505
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
sub depth { |
1507
|
0
|
|
|
0
|
1
|
|
my($self, $objid) = @_; |
1508
|
0
|
|
|
|
|
|
$self->idify_params($objid); |
1509
|
0
|
|
|
|
|
|
$self->_depth($objid, 0, 0); |
1510
|
|
|
|
|
|
|
} |
1511
|
|
|
|
|
|
|
|
1512
|
|
|
|
|
|
|
# XXX cycle detection? (see link) |
1513
|
|
|
|
|
|
|
sub _depth { |
1514
|
0
|
|
|
0
|
|
|
my($self, $objid, $min_depth, $max_depth) = @_; |
1515
|
0
|
|
|
|
|
|
my($add_min_depth, $add_max_depth); |
1516
|
0
|
|
|
|
|
|
foreach my $p_id ($self->parent_ids($objid)) { |
1517
|
0
|
|
|
|
|
|
my($p_min, $p_max) = $self->depth($p_id); |
1518
|
0
|
0
|
0
|
|
|
|
if (!defined $add_min_depth || $p_min < $add_min_depth) { |
1519
|
0
|
|
|
|
|
|
$add_min_depth = $p_min; |
1520
|
|
|
|
|
|
|
} |
1521
|
0
|
0
|
0
|
|
|
|
if (!defined $add_max_depth || $p_max > $add_max_depth) { |
1522
|
0
|
|
|
|
|
|
$add_max_depth = $p_max; |
1523
|
|
|
|
|
|
|
} |
1524
|
|
|
|
|
|
|
} |
1525
|
0
|
0
|
|
|
|
|
$add_min_depth = 0 if !defined $add_min_depth; |
1526
|
0
|
0
|
|
|
|
|
$add_max_depth = 0 if !defined $add_max_depth; |
1527
|
0
|
|
|
|
|
|
($min_depth + $add_min_depth + 1, $max_depth + $add_max_depth + 1); |
1528
|
|
|
|
|
|
|
} |
1529
|
|
|
|
|
|
|
|
1530
|
|
|
|
|
|
|
sub _get_next_version { |
1531
|
0
|
|
|
0
|
|
|
my($self, $objid) = @_; |
1532
|
0
|
|
|
|
|
|
$self->idify_params($objid); |
1533
|
0
|
|
|
|
|
|
my @versions = $self->versions($objid); |
1534
|
0
|
|
|
|
|
|
my $max_major; |
1535
|
|
|
|
|
|
|
my $max_minor; |
1536
|
0
|
|
|
|
|
|
foreach my $v (@versions) { |
1537
|
0
|
|
|
|
|
|
my($major, $minor); |
1538
|
0
|
0
|
|
|
|
|
if (defined $v->Version_Number) { |
1539
|
0
|
|
|
|
|
|
($major, $minor) = split /\./, $v->Version_Number; |
1540
|
|
|
|
|
|
|
} |
1541
|
0
|
0
|
0
|
|
|
|
if (!defined $max_major || |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
1542
|
|
|
|
|
|
|
(defined $major && ($major > $max_major || |
1543
|
|
|
|
|
|
|
($major == $max_major && $minor > $max_minor)) |
1544
|
|
|
|
|
|
|
) |
1545
|
|
|
|
|
|
|
) { |
1546
|
0
|
|
|
|
|
|
$max_major = $major; |
1547
|
0
|
|
|
|
|
|
$max_minor = $minor; |
1548
|
|
|
|
|
|
|
} |
1549
|
|
|
|
|
|
|
} |
1550
|
0
|
0
|
|
|
|
|
if (!defined $max_major) { |
1551
|
0
|
|
|
|
|
|
"1.0"; |
1552
|
|
|
|
|
|
|
} else { |
1553
|
0
|
|
|
|
|
|
$max_minor++; |
1554
|
0
|
|
|
|
|
|
$max_major . "." . $max_minor; |
1555
|
|
|
|
|
|
|
} |
1556
|
|
|
|
|
|
|
} |
1557
|
|
|
|
|
|
|
|
1558
|
|
|
|
|
|
|
=item PATH_SEP |
1559
|
|
|
|
|
|
|
|
1560
|
|
|
|
|
|
|
The default path separator is "/". |
1561
|
|
|
|
|
|
|
|
1562
|
|
|
|
|
|
|
=cut |
1563
|
|
|
|
|
|
|
|
1564
|
15
|
|
|
15
|
|
205
|
use constant PATH_SEP => "/"; |
|
15
|
|
|
|
|
48
|
|
|
15
|
|
|
|
|
23700
|
|
1565
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
=item pathname2id($pathname [, $parent_obj]) |
1567
|
|
|
|
|
|
|
|
1568
|
|
|
|
|
|
|
Return the object id for the matching "pathname". There are no real |
1569
|
|
|
|
|
|
|
pathnames in the WE_Framework, so a dummy pathname is constructed by |
1570
|
|
|
|
|
|
|
the titles (english, if there are multiple). C is used |
1571
|
|
|
|
|
|
|
as the path separator. |
1572
|
|
|
|
|
|
|
|
1573
|
|
|
|
|
|
|
If C<$parent_obj> is given as a object, then the given pathname should |
1574
|
|
|
|
|
|
|
be only a partial path starting from this parent object. |
1575
|
|
|
|
|
|
|
|
1576
|
|
|
|
|
|
|
Return C if no object could be found. |
1577
|
|
|
|
|
|
|
|
1578
|
|
|
|
|
|
|
=cut |
1579
|
|
|
|
|
|
|
|
1580
|
|
|
|
|
|
|
# XXX cycle test? |
1581
|
|
|
|
|
|
|
sub pathname2id { |
1582
|
0
|
|
|
0
|
1
|
|
my($self, $name, $obj) = @_; |
1583
|
0
|
|
0
|
|
|
|
$obj ||= $self->root_object; |
1584
|
0
|
|
|
|
|
|
my(@c) = split PATH_SEP, $name; |
1585
|
0
|
0
|
0
|
|
|
|
shift @c if (!defined $c[0] || $c[0] eq ''); # for "/" |
1586
|
|
|
|
|
|
|
COMP_LOOP: |
1587
|
0
|
|
|
|
|
|
while (my $component = shift @c) { |
1588
|
|
|
|
|
|
|
# my $component_stripped = $component; |
1589
|
|
|
|
|
|
|
# XXX is this ok? should I check whether the last component is a folder or not? |
1590
|
|
|
|
|
|
|
# if (@c == 0) { # last component |
1591
|
0
|
|
|
|
|
|
(my $component_stripped = $component) =~ s/\.[^.]+$//; # strip extension # XXX for last component (files) ? |
1592
|
|
|
|
|
|
|
# } |
1593
|
0
|
|
|
|
|
|
foreach my $c ($self->children($obj)) { |
1594
|
0
|
|
|
|
|
|
my $base = $c->Basename; |
1595
|
0
|
0
|
|
|
|
|
if (defined $base) { |
1596
|
0
|
|
|
|
|
|
$base = _make_path_component($base); |
1597
|
0
|
0
|
|
|
|
|
if ($component eq $base) { |
1598
|
0
|
|
|
|
|
|
$obj = $c; |
1599
|
0
|
|
|
|
|
|
next COMP_LOOP; |
1600
|
|
|
|
|
|
|
} |
1601
|
|
|
|
|
|
|
} else { |
1602
|
0
|
|
|
|
|
|
$base = langstring($c->Title); |
1603
|
0
|
|
|
|
|
|
$base = _make_path_component($base); |
1604
|
0
|
0
|
|
|
|
|
if ($component_stripped eq $base) { |
1605
|
0
|
|
|
|
|
|
$obj = $c; |
1606
|
0
|
|
|
|
|
|
next COMP_LOOP; |
1607
|
|
|
|
|
|
|
} |
1608
|
|
|
|
|
|
|
} |
1609
|
|
|
|
|
|
|
} |
1610
|
0
|
|
|
|
|
|
return undef; |
1611
|
|
|
|
|
|
|
} |
1612
|
0
|
|
|
|
|
|
$obj->Id; |
1613
|
|
|
|
|
|
|
} |
1614
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
=item pathname($object_id [, $parent_obj, %args]) |
1616
|
|
|
|
|
|
|
|
1617
|
|
|
|
|
|
|
For the object C<$object_id>, the virtual pathname (as described in |
1618
|
|
|
|
|
|
|
pathname2id) is returned. |
1619
|
|
|
|
|
|
|
|
1620
|
|
|
|
|
|
|
If C<$parent_obj> is given as a object, then the returned pathname is |
1621
|
|
|
|
|
|
|
only a partial path starting from this parent object. |
1622
|
|
|
|
|
|
|
|
1623
|
|
|
|
|
|
|
Possible key-values for %args: |
1624
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
=over |
1626
|
|
|
|
|
|
|
|
1627
|
|
|
|
|
|
|
=item -lang => $lang |
1628
|
|
|
|
|
|
|
|
1629
|
|
|
|
|
|
|
Use the specified language C<$lang> rather than the default language |
1630
|
|
|
|
|
|
|
(en) for title composition. |
1631
|
|
|
|
|
|
|
|
1632
|
|
|
|
|
|
|
=back |
1633
|
|
|
|
|
|
|
|
1634
|
|
|
|
|
|
|
=cut |
1635
|
|
|
|
|
|
|
|
1636
|
|
|
|
|
|
|
# XXX cycle test |
1637
|
|
|
|
|
|
|
# XXX should be more thought on (what about WE::Obj::Sites etc.) |
1638
|
|
|
|
|
|
|
sub pathname { |
1639
|
0
|
|
|
0
|
1
|
|
my($self, $obj, $parent_obj, %args) = @_; |
1640
|
0
|
|
|
|
|
|
$self->objectify_params($obj); |
1641
|
0
|
|
|
|
|
|
my @parents = $self->parent_ids($obj->Id); |
1642
|
0
|
|
|
|
|
|
my $ext = ""; |
1643
|
0
|
0
|
|
|
|
|
if ($obj->is_doc) { |
1644
|
0
|
|
|
|
|
|
$ext = "." . $self->Root->ContentDB->extension($obj); |
1645
|
|
|
|
|
|
|
} |
1646
|
0
|
|
|
|
|
|
my $base = $obj->Basename; |
1647
|
0
|
0
|
|
|
|
|
if (!defined $base) { |
1648
|
0
|
0
|
|
|
|
|
my $langstring = (exists $args{-lang} |
1649
|
|
|
|
|
|
|
? langstring($obj->Title, $args{-lang}) |
1650
|
|
|
|
|
|
|
: langstring($obj->Title) |
1651
|
|
|
|
|
|
|
); |
1652
|
0
|
|
|
|
|
|
$base = _make_path_component($langstring) . $ext; |
1653
|
|
|
|
|
|
|
} |
1654
|
0
|
0
|
0
|
|
|
|
if (defined $parent_obj && $obj->Id eq $parent_obj->Id) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1655
|
0
|
|
|
|
|
|
""; |
1656
|
|
|
|
|
|
|
} elsif ($obj->isa("WE::Obj::Site")) { |
1657
|
0
|
|
|
|
|
|
"/" |
1658
|
|
|
|
|
|
|
} elsif (@parents) { |
1659
|
0
|
|
|
|
|
|
my $parent_path = $self->pathname($parents[0], $parent_obj, %args); |
1660
|
0
|
0
|
|
|
|
|
$parent_path .= PATH_SEP if $parent_path !~ m|^/?$|; |
1661
|
0
|
|
|
|
|
|
$parent_path . $base; |
1662
|
|
|
|
|
|
|
} else { |
1663
|
0
|
|
|
|
|
|
"/$base"; |
1664
|
|
|
|
|
|
|
} |
1665
|
|
|
|
|
|
|
} |
1666
|
|
|
|
|
|
|
|
1667
|
|
|
|
|
|
|
sub _make_path_component { |
1668
|
0
|
|
|
0
|
|
|
my $name = shift; |
1669
|
0
|
|
|
|
|
|
$name =~ s/@{[PATH_SEP]}/_/g; |
|
0
|
|
|
|
|
|
|
1670
|
0
|
|
|
|
|
|
$name; |
1671
|
|
|
|
|
|
|
} |
1672
|
|
|
|
|
|
|
|
1673
|
|
|
|
|
|
|
=item get_released_children($folder_id) |
1674
|
|
|
|
|
|
|
|
1675
|
|
|
|
|
|
|
Return recursive all folders and released children of the given folder |
1676
|
|
|
|
|
|
|
C<$folder_id> as an array of objects. |
1677
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
=cut |
1679
|
|
|
|
|
|
|
|
1680
|
|
|
|
|
|
|
sub get_released_children { |
1681
|
0
|
|
|
0
|
1
|
|
my($objdb, $folder_id, %args) = @_; |
1682
|
0
|
|
|
|
|
|
my @children = $objdb->children($folder_id); |
1683
|
0
|
|
|
|
|
|
my @res; |
1684
|
0
|
|
|
|
|
|
for my $o (@children) { |
1685
|
0
|
0
|
|
|
|
|
if ($o->is_folder) { |
1686
|
0
|
|
|
|
|
|
push @res, $o; |
1687
|
|
|
|
|
|
|
} else { |
1688
|
0
|
|
|
|
|
|
my $r = $objdb->get_released_object($o->Id, %args); |
1689
|
0
|
0
|
|
|
|
|
push @res, $r if defined $r; |
1690
|
|
|
|
|
|
|
} |
1691
|
|
|
|
|
|
|
} |
1692
|
0
|
|
|
|
|
|
@res; |
1693
|
|
|
|
|
|
|
} |
1694
|
|
|
|
|
|
|
|
1695
|
|
|
|
|
|
|
=item get_released_object($object_id) |
1696
|
|
|
|
|
|
|
|
1697
|
|
|
|
|
|
|
Return the last released version for C<$object_id>. If there is no |
1698
|
|
|
|
|
|
|
released version yet, return C. |
1699
|
|
|
|
|
|
|
|
1700
|
|
|
|
|
|
|
=cut |
1701
|
|
|
|
|
|
|
|
1702
|
|
|
|
|
|
|
sub get_released_object { |
1703
|
0
|
|
|
0
|
1
|
|
my($objdb, $obj_id, %args) = @_; |
1704
|
0
|
|
|
|
|
|
my $obj = $objdb->get_object($obj_id); |
1705
|
0
|
0
|
|
|
|
|
die "Can't get object with id $obj_id" if !$obj; |
1706
|
0
|
|
|
|
|
|
my $releasable = $objdb->is_active_page($obj, %args); |
1707
|
0
|
0
|
|
|
|
|
return undef if (!$releasable); |
1708
|
0
|
0
|
0
|
|
|
|
if (defined $obj->Release_State && $obj->Release_State eq 'released') { |
1709
|
0
|
|
|
|
|
|
return $obj; |
1710
|
|
|
|
|
|
|
} |
1711
|
0
|
|
|
|
|
|
foreach my $v_id (reverse $objdb->version_ids($obj_id)) { |
1712
|
0
|
|
|
|
|
|
my $v = $objdb->get_object($v_id); |
1713
|
0
|
0
|
0
|
|
|
|
if (defined $v->Release_State && $v->Release_State eq 'released') { |
1714
|
0
|
|
|
|
|
|
return $v; |
1715
|
|
|
|
|
|
|
} |
1716
|
|
|
|
|
|
|
} |
1717
|
0
|
|
|
|
|
|
undef; |
1718
|
|
|
|
|
|
|
} |
1719
|
|
|
|
|
|
|
|
1720
|
|
|
|
|
|
|
=item is_active_page($obj) |
1721
|
|
|
|
|
|
|
|
1722
|
|
|
|
|
|
|
Return true if the object $obj is active, that is, the release state |
1723
|
|
|
|
|
|
|
is not I and I/I does not apply. |
1724
|
|
|
|
|
|
|
|
1725
|
|
|
|
|
|
|
=cut |
1726
|
|
|
|
|
|
|
|
1727
|
|
|
|
|
|
|
sub is_active_page { |
1728
|
0
|
|
|
0
|
1
|
|
my($objdb, $o, %args) = @_; |
1729
|
0
|
|
|
|
|
|
$objdb->objectify_params($o); |
1730
|
0
|
|
|
|
|
|
my $now = $args{-now}; |
1731
|
0
|
0
|
|
|
|
|
$now = epoch2isodate if !defined $now; |
1732
|
|
|
|
|
|
|
my $active = $objdb->walk_up_preorder |
1733
|
|
|
|
|
|
|
($o, sub { |
1734
|
0
|
|
|
0
|
|
|
my($obj_id) = @_; |
1735
|
0
|
|
|
|
|
|
my $o = $objdb->get_object($obj_id); |
1736
|
0
|
0
|
|
|
|
|
if (!$o) { |
1737
|
0
|
|
|
|
|
|
warn "Should never happen --- No object for id $obj_id found..."; |
1738
|
0
|
|
|
|
|
|
return 1; |
1739
|
|
|
|
|
|
|
} |
1740
|
0
|
0
|
0
|
|
|
|
if (defined $o->Release_State && $o->Release_State eq 'inactive') { |
1741
|
0
|
|
|
|
|
|
$WE::DB::Obj::prune = 1; # cut off subtree |
1742
|
|
|
|
|
|
|
#warn "Inactive object found ($obj_id)\n"; |
1743
|
0
|
|
|
|
|
|
return 0; |
1744
|
|
|
|
|
|
|
} |
1745
|
|
|
|
|
|
|
|
1746
|
0
|
0
|
|
|
|
|
if ($o->is_time_restricted) { |
1747
|
0
|
|
|
|
|
|
$WE::DB::Obj::prune = 1; # cut off subtree |
1748
|
|
|
|
|
|
|
#warn "Time restricted object found ($obj_id)\n"; |
1749
|
0
|
|
|
|
|
|
return 0; |
1750
|
|
|
|
|
|
|
} |
1751
|
0
|
|
|
|
|
|
1; |
1752
|
0
|
|
|
|
|
|
}); |
1753
|
0
|
|
|
|
|
|
$active; |
1754
|
|
|
|
|
|
|
} |
1755
|
|
|
|
|
|
|
|
1756
|
|
|
|
|
|
|
sub count { |
1757
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1758
|
|
|
|
|
|
|
$self->connect_if_necessary |
1759
|
|
|
|
|
|
|
(sub { |
1760
|
0
|
|
|
0
|
|
|
scalar keys(%{$self->{DB}}) - 2; |
|
0
|
|
|
|
|
|
|
1761
|
0
|
|
|
|
|
|
}); |
1762
|
|
|
|
|
|
|
} |
1763
|
|
|
|
|
|
|
|
1764
|
|
|
|
|
|
|
1; |
1765
|
|
|
|
|
|
|
|
1766
|
|
|
|
|
|
|
__END__ |