| 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__ |