| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Wiki::Toolkit::Store::Database; |
|
2
|
|
|
|
|
|
|
|
|
3
|
8
|
|
|
8
|
|
3478
|
use strict; |
|
|
8
|
|
|
|
|
43
|
|
|
|
8
|
|
|
|
|
299
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
8
|
|
|
8
|
|
46
|
use vars qw( $VERSION $timestamp_fmt ); |
|
|
8
|
|
|
|
|
16
|
|
|
|
8
|
|
|
|
|
613
|
|
|
6
|
|
|
|
|
|
|
$timestamp_fmt = "%Y-%m-%d %H:%M:%S"; |
|
7
|
|
|
|
|
|
|
|
|
8
|
8
|
|
|
8
|
|
7977
|
use DBI; |
|
|
8
|
|
|
|
|
102225
|
|
|
|
8
|
|
|
|
|
541
|
|
|
9
|
8
|
|
|
8
|
|
5800
|
use Time::Piece; |
|
|
8
|
|
|
|
|
101440
|
|
|
|
8
|
|
|
|
|
35
|
|
|
10
|
8
|
|
|
8
|
|
627
|
use Time::Seconds; |
|
|
8
|
|
|
|
|
20
|
|
|
|
8
|
|
|
|
|
590
|
|
|
11
|
8
|
|
|
8
|
|
49
|
use Carp qw( carp croak ); |
|
|
8
|
|
|
|
|
17
|
|
|
|
8
|
|
|
|
|
535
|
|
|
12
|
8
|
|
|
8
|
|
49
|
use Digest::MD5 qw( md5_hex ); |
|
|
8
|
|
|
|
|
17
|
|
|
|
8
|
|
|
|
|
686
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
$VERSION = '0.32'; |
|
15
|
|
|
|
|
|
|
my $SCHEMA_VER = 11; |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# first, detect if Encode is available - it's not under 5.6. If we _are_ |
|
18
|
|
|
|
|
|
|
# under 5.6, give up - we'll just have to hope that nothing explodes. This |
|
19
|
|
|
|
|
|
|
# is the current 0.54 behaviour, so that's ok. |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my $CAN_USE_ENCODE; |
|
22
|
|
|
|
|
|
|
BEGIN { |
|
23
|
8
|
|
|
8
|
|
683
|
eval " use Encode "; |
|
|
8
|
|
|
8
|
|
2519
|
|
|
|
8
|
|
|
|
|
42800
|
|
|
|
8
|
|
|
|
|
568
|
|
|
24
|
8
|
50
|
|
|
|
72305
|
$CAN_USE_ENCODE = $@ ? 0 : 1; |
|
25
|
|
|
|
|
|
|
} |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 NAME |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Wiki::Toolkit::Store::Database - parent class for database storage backends |
|
30
|
|
|
|
|
|
|
for Wiki::Toolkit |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
This is probably only useful for Wiki::Toolkit developers. |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# See below for parameter details. |
|
37
|
|
|
|
|
|
|
my $store = Wiki::Toolkit::Store::MySQL->new( %config ); |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head1 METHODS |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=over 4 |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=item B |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
my $store = Wiki::Toolkit::Store::MySQL->new( dbname => "wiki", |
|
46
|
|
|
|
|
|
|
dbuser => "wiki", |
|
47
|
|
|
|
|
|
|
dbpass => "wiki", |
|
48
|
|
|
|
|
|
|
dbhost => "db.example.com", |
|
49
|
|
|
|
|
|
|
dbport => 1234, |
|
50
|
|
|
|
|
|
|
charset => "iso-8859-1" ); |
|
51
|
|
|
|
|
|
|
or |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
my $store = Wiki::Toolkit::Store::MySQL->new( dbh => $dbh ); |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
C is optional, defaults to C, and does nothing |
|
56
|
|
|
|
|
|
|
unless you're using perl 5.8 or newer. |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
If you do not provide an active database handle in C, then |
|
59
|
|
|
|
|
|
|
C is mandatory. C, C, C and C |
|
60
|
|
|
|
|
|
|
are optional, but you'll want to supply them unless your database's |
|
61
|
|
|
|
|
|
|
connection method doesn't require them. |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
If you do provide C then it must have the following |
|
64
|
|
|
|
|
|
|
parameters set; otherwise you should just provide the connection |
|
65
|
|
|
|
|
|
|
information and let us create our own handle: |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=over 4 |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=item * |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
C = 1 |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=item * |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
C = 0 |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=item * |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
C = 1 |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=back |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=cut |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub new { |
|
86
|
2
|
|
|
2
|
1
|
192
|
my ($class, @args) = @_; |
|
87
|
2
|
|
|
|
|
6
|
my $self = {}; |
|
88
|
2
|
|
|
|
|
6
|
bless $self, $class; |
|
89
|
2
|
|
|
|
|
11
|
return $self->_init(@args); |
|
90
|
|
|
|
|
|
|
} |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub _init { |
|
93
|
3
|
|
|
3
|
|
14
|
my ($self, %args) = @_; |
|
94
|
|
|
|
|
|
|
|
|
95
|
3
|
50
|
|
|
|
16
|
if ( $args{dbh} ) { |
|
96
|
0
|
|
|
|
|
0
|
$self->{_dbh} = $args{dbh}; |
|
97
|
0
|
|
|
|
|
0
|
$self->{_external_dbh} = 1; # don't disconnect at DESTROY time |
|
98
|
0
|
|
0
|
|
|
0
|
$self->{_charset} = $args{charset} || "iso-8859-1"; |
|
99
|
|
|
|
|
|
|
} else { |
|
100
|
3
|
50
|
|
|
|
48
|
die "Must supply a dbname" unless defined $args{dbname}; |
|
101
|
0
|
|
|
|
|
0
|
$self->{_dbname} = $args{dbname}; |
|
102
|
0
|
|
0
|
|
|
0
|
$self->{_dbuser} = $args{dbuser} || ""; |
|
103
|
0
|
|
0
|
|
|
0
|
$self->{_dbpass} = $args{dbpass} || ""; |
|
104
|
0
|
|
0
|
|
|
0
|
$self->{_dbhost} = $args{dbhost} || ""; |
|
105
|
0
|
|
0
|
|
|
0
|
$self->{_dbport} = $args{dbport} || ""; |
|
106
|
0
|
|
0
|
|
|
0
|
$self->{_charset} = $args{charset} || "iso-8859-1"; |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# Connect to database and store the database handle. |
|
109
|
|
|
|
|
|
|
my ($dbname, $dbuser, $dbpass, $dbhost, $dbport) = |
|
110
|
0
|
|
|
|
|
0
|
@$self{qw(_dbname _dbuser _dbpass _dbhost _dbport)}; |
|
111
|
0
|
0
|
|
|
|
0
|
my $dsn = $self->_dsn($dbname, $dbhost, $dbport) |
|
112
|
|
|
|
|
|
|
or croak "No data source string provided by class"; |
|
113
|
0
|
0
|
|
|
|
0
|
$self->{_dbh} = DBI->connect( $dsn, $dbuser, $dbpass, |
|
114
|
|
|
|
|
|
|
$self->_get_dbh_connect_attr ) |
|
115
|
|
|
|
|
|
|
or croak "Can't connect to database $dbname using $dsn: " |
|
116
|
|
|
|
|
|
|
. DBI->errstr; |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
0
|
|
|
|
|
0
|
my ($cur_ver, $db_ver) = $self->schema_current; |
|
120
|
0
|
0
|
|
|
|
0
|
if ($db_ver < $cur_ver) { |
|
|
|
0
|
|
|
|
|
|
|
121
|
0
|
|
|
|
|
0
|
croak "Database schema version $db_ver is too old (need $cur_ver)"; |
|
122
|
|
|
|
|
|
|
} elsif ($db_ver > $cur_ver) { |
|
123
|
0
|
|
|
|
|
0
|
croak "Database schema version $db_ver is too new (need $cur_ver)"; |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
|
|
126
|
0
|
|
|
|
|
0
|
return $self; |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# Internal method to get attributes for passing to DBI->connect(). |
|
130
|
|
|
|
|
|
|
# Override in subclasses to add database-dependent attributes. |
|
131
|
|
|
|
|
|
|
sub _get_dbh_connect_attr { |
|
132
|
|
|
|
|
|
|
return { |
|
133
|
0
|
|
|
0
|
|
0
|
PrintError => 0, |
|
134
|
|
|
|
|
|
|
RaiseError => 1, |
|
135
|
|
|
|
|
|
|
AutoCommit => 1, |
|
136
|
|
|
|
|
|
|
}; |
|
137
|
|
|
|
|
|
|
} |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# Internal method, used to handle the logic of how to add up return |
|
140
|
|
|
|
|
|
|
# values from pre_ plugins |
|
141
|
|
|
|
|
|
|
sub handle_pre_plugin_ret { |
|
142
|
0
|
|
|
0
|
0
|
0
|
my ($running_total_ref,$result) = @_; |
|
143
|
|
|
|
|
|
|
|
|
144
|
0
|
0
|
0
|
|
|
0
|
if(($result && $result == 0) || !$result) { |
|
|
|
0
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# No opinion, no need to change things |
|
146
|
|
|
|
|
|
|
} elsif($result == -1 || $result == 1) { |
|
147
|
|
|
|
|
|
|
# Increase or decrease as requested |
|
148
|
0
|
|
|
|
|
0
|
$$running_total_ref += $result; |
|
149
|
|
|
|
|
|
|
} else { |
|
150
|
|
|
|
|
|
|
# Invalid return code |
|
151
|
0
|
|
|
|
|
0
|
warn("Pre_ plugin returned invalid accept/deny value of '$result'"); |
|
152
|
|
|
|
|
|
|
} |
|
153
|
|
|
|
|
|
|
} |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=item B |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
my $content = $store->retrieve_node($node); |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# Or get additional meta-data too. |
|
160
|
|
|
|
|
|
|
my %node = $store->retrieve_node("HomePage"); |
|
161
|
|
|
|
|
|
|
print "Current Version: " . $node{version}; |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# Maybe we stored some metadata too. |
|
164
|
|
|
|
|
|
|
my $categories = $node{metadata}{category}; |
|
165
|
|
|
|
|
|
|
print "Categories: " . join(", ", @$categories); |
|
166
|
|
|
|
|
|
|
print "Postcode: $node{metadata}{postcode}[0]"; |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# Or get an earlier version: |
|
169
|
|
|
|
|
|
|
my %node = $store->retrieve_node(name => "HomePage", |
|
170
|
|
|
|
|
|
|
version => 2 ); |
|
171
|
|
|
|
|
|
|
print $node{content}; |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
In scalar context, returns the current (raw Wiki language) contents of |
|
175
|
|
|
|
|
|
|
the specified node. In list context, returns a hash containing the |
|
176
|
|
|
|
|
|
|
contents of the node plus additional data: |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=over 4 |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=item B |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=item B |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=item B |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
=item B - a reference to a hash containing any caller-supplied |
|
187
|
|
|
|
|
|
|
metadata sent along the last time the node was written |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=back |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
The node parameter is mandatory. The version parameter is optional and |
|
192
|
|
|
|
|
|
|
defaults to the newest version. If the node hasn't been created yet, |
|
193
|
|
|
|
|
|
|
it is considered to exist but be empty (this behaviour might change). |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
B on metadata - each hash value is returned as an array ref, |
|
196
|
|
|
|
|
|
|
even if that type of metadata only has one value. |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=cut |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub retrieve_node { |
|
201
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
202
|
0
|
0
|
|
|
|
0
|
my %args = scalar @_ == 1 ? ( name => $_[0] ) : @_; |
|
203
|
0
|
0
|
|
|
|
0
|
unless($args{'version'}) { $args{'version'} = undef; } |
|
|
0
|
|
|
|
|
0
|
|
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# Call pre_retrieve on any plugins, in case they want to tweak anything |
|
206
|
0
|
0
|
|
|
|
0
|
my @plugins = @{ $args{plugins} || [ ] }; |
|
|
0
|
|
|
|
|
0
|
|
|
207
|
0
|
|
|
|
|
0
|
foreach my $plugin (@plugins) { |
|
208
|
0
|
0
|
|
|
|
0
|
if ( $plugin->can( "pre_retrieve" ) ) { |
|
209
|
|
|
|
|
|
|
$plugin->pre_retrieve( |
|
210
|
|
|
|
|
|
|
node => \$args{'name'}, |
|
211
|
0
|
|
|
|
|
0
|
version => \$args{'version'} |
|
212
|
|
|
|
|
|
|
); |
|
213
|
|
|
|
|
|
|
} |
|
214
|
|
|
|
|
|
|
} |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# Note _retrieve_node_data is sensitive to calling context. |
|
217
|
0
|
0
|
|
|
|
0
|
unless(wantarray) { |
|
218
|
|
|
|
|
|
|
# Scalar context, will return just the content |
|
219
|
0
|
|
|
|
|
0
|
return $self->_retrieve_node_data( %args ); |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
0
|
|
|
|
|
0
|
my %data = $self->_retrieve_node_data( %args ); |
|
223
|
0
|
|
|
|
|
0
|
$data{'checksum'} = $self->_checksum(%data); |
|
224
|
0
|
|
|
|
|
0
|
return %data; |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# Returns hash or scalar depending on calling context. |
|
228
|
|
|
|
|
|
|
sub _retrieve_node_data { |
|
229
|
0
|
|
|
0
|
|
0
|
my ($self, %args) = @_; |
|
230
|
0
|
|
|
|
|
0
|
my %data = $self->_retrieve_node_content( %args ); |
|
231
|
0
|
0
|
|
|
|
0
|
unless(wantarray) { |
|
232
|
|
|
|
|
|
|
# Scalar context, return just the content |
|
233
|
0
|
|
|
|
|
0
|
return $data{content}; |
|
234
|
|
|
|
|
|
|
} |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
# If we want additional data then get it. Note that $data{version} |
|
237
|
|
|
|
|
|
|
# will already have been set by C<_retrieve_node_content>, if it wasn't |
|
238
|
|
|
|
|
|
|
# specified in the call. |
|
239
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
240
|
0
|
|
|
|
|
0
|
my $sql = "SELECT metadata_type, metadata_value " |
|
241
|
|
|
|
|
|
|
. "FROM node " |
|
242
|
|
|
|
|
|
|
. "INNER JOIN metadata ON (node_id = id) " |
|
243
|
|
|
|
|
|
|
. "WHERE name=? " |
|
244
|
|
|
|
|
|
|
. "AND metadata.version=?"; |
|
245
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare($sql); |
|
246
|
0
|
0
|
|
|
|
0
|
$sth->execute($args{name},$data{version}) or croak $dbh->errstr; |
|
247
|
|
|
|
|
|
|
|
|
248
|
0
|
|
|
|
|
0
|
my %metadata; |
|
249
|
0
|
|
|
|
|
0
|
while ( my ($type, $val) = $self->charset_decode( $sth->fetchrow_array ) ) { |
|
250
|
0
|
0
|
|
|
|
0
|
if ( defined $metadata{$type} ) { |
|
251
|
0
|
|
|
|
|
0
|
push @{$metadata{$type}}, $val; |
|
|
0
|
|
|
|
|
0
|
|
|
252
|
|
|
|
|
|
|
} else { |
|
253
|
0
|
|
|
|
|
0
|
$metadata{$type} = [ $val ]; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
} |
|
256
|
0
|
|
|
|
|
0
|
$data{metadata} = \%metadata; |
|
257
|
0
|
|
|
|
|
0
|
return %data; |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# $store->_retrieve_node_content( name => $node_name, |
|
261
|
|
|
|
|
|
|
# version => $node_version ); |
|
262
|
|
|
|
|
|
|
# Params: 'name' is compulsory, 'version' is optional and defaults to latest. |
|
263
|
|
|
|
|
|
|
# Returns a hash of data for C - content, version, last modified |
|
264
|
|
|
|
|
|
|
sub _retrieve_node_content { |
|
265
|
0
|
|
|
0
|
|
0
|
my ($self, %args) = @_; |
|
266
|
0
|
0
|
|
|
|
0
|
croak "No valid node name supplied" unless $args{name}; |
|
267
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
268
|
0
|
|
|
|
|
0
|
my $sql; |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
my $version_sql_val; |
|
271
|
0
|
|
|
|
|
0
|
my $text_source; |
|
272
|
0
|
0
|
|
|
|
0
|
if ( $args{version} ) { |
|
273
|
|
|
|
|
|
|
# Version given - get that version, and the content for that version |
|
274
|
0
|
|
|
|
|
0
|
$version_sql_val = $dbh->quote($self->charset_encode($args{version})); |
|
275
|
0
|
|
|
|
|
0
|
$text_source = "content"; |
|
276
|
|
|
|
|
|
|
} else { |
|
277
|
|
|
|
|
|
|
# No version given, grab latest version (and content for that) |
|
278
|
0
|
|
|
|
|
0
|
$version_sql_val = "node.version"; |
|
279
|
0
|
|
|
|
|
0
|
$text_source = "node"; |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
$sql = "SELECT " |
|
282
|
|
|
|
|
|
|
. " $text_source.text, content.version, " |
|
283
|
|
|
|
|
|
|
. " content.modified, content.moderated, " |
|
284
|
|
|
|
|
|
|
. " node.moderate " |
|
285
|
|
|
|
|
|
|
. "FROM node " |
|
286
|
|
|
|
|
|
|
. "INNER JOIN content ON (id = node_id) " |
|
287
|
0
|
|
|
|
|
0
|
. "WHERE name=" . $dbh->quote($self->charset_encode($args{name})) |
|
288
|
|
|
|
|
|
|
. " AND content.version=" . $version_sql_val; |
|
289
|
0
|
|
|
|
|
0
|
my @results = $self->charset_decode( $dbh->selectrow_array($sql) ); |
|
290
|
0
|
0
|
|
|
|
0
|
@results = ("", 0, "") unless scalar @results; |
|
291
|
0
|
|
|
|
|
0
|
my %data; |
|
292
|
0
|
|
|
|
|
0
|
@data{ qw( content version last_modified moderated node_requires_moderation ) } = @results; |
|
293
|
0
|
|
|
|
|
0
|
return %data; |
|
294
|
|
|
|
|
|
|
} |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# Expects a hash as returned by ->retrieve_node - it's actually slightly lax |
|
297
|
|
|
|
|
|
|
# in this, in that while ->retrieve_node always wraps up the metadata values in |
|
298
|
|
|
|
|
|
|
# (refs to) arrays, this method will accept scalar metadata values too. |
|
299
|
|
|
|
|
|
|
sub _checksum { |
|
300
|
0
|
|
|
0
|
|
0
|
my ($self, %node_data) = @_; |
|
301
|
0
|
|
|
|
|
0
|
my $string = $node_data{content}; |
|
302
|
0
|
0
|
|
|
|
0
|
my %metadata = %{ $node_data{metadata} || {} }; |
|
|
0
|
|
|
|
|
0
|
|
|
303
|
0
|
|
|
|
|
0
|
foreach my $key ( sort keys %metadata ) { |
|
304
|
0
|
|
|
|
|
0
|
$string .= "\0\0\0" . $key . "\0\0"; |
|
305
|
0
|
|
|
|
|
0
|
my $val = $metadata{$key}; |
|
306
|
0
|
0
|
|
|
|
0
|
if ( ref $val eq "ARRAY" ) { |
|
307
|
0
|
|
|
|
|
0
|
$string .= join("\0", sort @$val ); |
|
308
|
|
|
|
|
|
|
} else { |
|
309
|
0
|
|
|
|
|
0
|
$string .= $val; |
|
310
|
|
|
|
|
|
|
} |
|
311
|
|
|
|
|
|
|
} |
|
312
|
0
|
|
|
|
|
0
|
return md5_hex($self->charset_encode($string)); |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
# Expects an array of hashes whose keys and values are scalars. |
|
316
|
|
|
|
|
|
|
sub _checksum_hashes { |
|
317
|
0
|
|
|
0
|
|
0
|
my ($self, @hashes) = @_; |
|
318
|
0
|
|
|
|
|
0
|
my @strings = ""; |
|
319
|
0
|
|
|
|
|
0
|
foreach my $hashref ( @hashes ) { |
|
320
|
0
|
|
|
|
|
0
|
my %hash = %$hashref; |
|
321
|
0
|
|
|
|
|
0
|
my $substring = ""; |
|
322
|
0
|
|
|
|
|
0
|
foreach my $key ( sort keys %hash ) { |
|
323
|
0
|
|
|
|
|
0
|
$substring .= "\0\0" . $key . "\0" . $hash{$key}; |
|
324
|
|
|
|
|
|
|
} |
|
325
|
0
|
|
|
|
|
0
|
push @strings, $substring; |
|
326
|
|
|
|
|
|
|
} |
|
327
|
0
|
|
|
|
|
0
|
my $string = join("\0\0\0", sort @strings); |
|
328
|
0
|
|
|
|
|
0
|
return md5_hex($string); |
|
329
|
|
|
|
|
|
|
} |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=item B |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
my $ok = $store->node_exists( "Wombat Defenestration" ); |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# or ignore case - optional but recommended |
|
336
|
|
|
|
|
|
|
my $ok = $store->node_exists( |
|
337
|
|
|
|
|
|
|
name => "monkey brains", |
|
338
|
|
|
|
|
|
|
ignore_case => 1, |
|
339
|
|
|
|
|
|
|
); |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
Returns true if the node has ever been created (even if it is |
|
342
|
|
|
|
|
|
|
currently empty), and false otherwise. |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
By default, the case-sensitivity of C depends on your |
|
345
|
|
|
|
|
|
|
database. If you supply a true value to the C parameter, |
|
346
|
|
|
|
|
|
|
then you can be sure of its being case-insensitive. This is |
|
347
|
|
|
|
|
|
|
recommended. |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=cut |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
sub node_exists { |
|
352
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
353
|
0
|
0
|
|
|
|
0
|
if ( scalar @_ == 1 ) { |
|
354
|
0
|
|
|
|
|
0
|
my $node = shift; |
|
355
|
0
|
|
|
|
|
0
|
return $self->_do_old_node_exists( $node ); |
|
356
|
|
|
|
|
|
|
} else { |
|
357
|
0
|
|
|
|
|
0
|
my %args = @_; |
|
358
|
|
|
|
|
|
|
return $self->_do_old_node_exists( $args{name} ) |
|
359
|
0
|
0
|
|
|
|
0
|
unless $args{ignore_case}; |
|
360
|
0
|
|
|
|
|
0
|
my $sql = $self->_get_node_exists_ignore_case_sql; |
|
361
|
0
|
|
|
|
|
0
|
my $sth = $self->dbh->prepare( $sql ); |
|
362
|
0
|
|
|
|
|
0
|
$sth->execute( $args{name} ); |
|
363
|
0
|
|
0
|
|
|
0
|
my $found_name = $sth->fetchrow_array || ""; |
|
364
|
0
|
|
|
|
|
0
|
$sth->finish; |
|
365
|
0
|
0
|
|
|
|
0
|
return lc($found_name) eq lc($args{name}) ? 1 : 0; |
|
366
|
|
|
|
|
|
|
} |
|
367
|
|
|
|
|
|
|
} |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
sub _do_old_node_exists { |
|
370
|
0
|
|
|
0
|
|
0
|
my ($self, $node) = @_; |
|
371
|
0
|
0
|
|
|
|
0
|
my %data = $self->retrieve_node($node) or return (); |
|
372
|
0
|
|
|
|
|
0
|
return $data{version}; # will be 0 if node doesn't exist, >=1 otherwise |
|
373
|
|
|
|
|
|
|
} |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=item B |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
my $ok = $store->verify_checksum($node, $checksum); |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
Sees whether your checksum is current for the given node. Returns true |
|
380
|
|
|
|
|
|
|
if so, false if not. |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
B Be aware that when called directly and without locking, this |
|
383
|
|
|
|
|
|
|
might not be accurate, since there is a small window between the |
|
384
|
|
|
|
|
|
|
checking and the returning where the node might be changed, so |
|
385
|
|
|
|
|
|
|
B rely on it for safe commits; use C for that. It |
|
386
|
|
|
|
|
|
|
can however be useful when previewing edits, for example. |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=cut |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
sub verify_checksum { |
|
391
|
0
|
|
|
0
|
1
|
0
|
my ($self, $node, $checksum) = @_; |
|
392
|
|
|
|
|
|
|
#warn $self; |
|
393
|
0
|
|
|
|
|
0
|
my %node_data = $self->_retrieve_node_data( name => $node ); |
|
394
|
0
|
|
|
|
|
0
|
return ( $checksum eq $self->_checksum( %node_data ) ); |
|
395
|
|
|
|
|
|
|
} |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
=item B |
|
398
|
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
# List all nodes that link to the Home Page. |
|
400
|
|
|
|
|
|
|
my @links = $store->list_backlinks( node => "Home Page" ); |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
=cut |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
sub list_backlinks { |
|
405
|
0
|
|
|
0
|
1
|
0
|
my ( $self, %args ) = @_; |
|
406
|
0
|
|
|
|
|
0
|
my $node = $args{node}; |
|
407
|
0
|
0
|
|
|
|
0
|
croak "Must supply a node name" unless $node; |
|
408
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
409
|
|
|
|
|
|
|
# XXX see comment in list_dangling_links |
|
410
|
0
|
|
|
|
|
0
|
my $sql = "SELECT link_from FROM internal_links INNER JOIN |
|
411
|
|
|
|
|
|
|
node AS node_from ON node_from.name=internal_links.link_from |
|
412
|
|
|
|
|
|
|
WHERE link_to=" |
|
413
|
|
|
|
|
|
|
. $dbh->quote($node); |
|
414
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare($sql); |
|
415
|
0
|
0
|
|
|
|
0
|
$sth->execute or croak $dbh->errstr; |
|
416
|
0
|
|
|
|
|
0
|
my @backlinks; |
|
417
|
0
|
|
|
|
|
0
|
while ( my ($backlink) = $self->charset_decode( $sth->fetchrow_array ) ) { |
|
418
|
0
|
|
|
|
|
0
|
push @backlinks, $backlink; |
|
419
|
|
|
|
|
|
|
} |
|
420
|
0
|
|
|
|
|
0
|
return @backlinks; |
|
421
|
|
|
|
|
|
|
} |
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=item B |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
# List all nodes that have been linked to from other nodes but don't |
|
426
|
|
|
|
|
|
|
# yet exist. |
|
427
|
|
|
|
|
|
|
my @links = $store->list_dangling_links; |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
Each node is returned once only, regardless of how many other nodes |
|
430
|
|
|
|
|
|
|
link to it. |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=cut |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
sub list_dangling_links { |
|
435
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
436
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
437
|
|
|
|
|
|
|
# XXX this is really hiding an inconsistency in the database; |
|
438
|
|
|
|
|
|
|
# should really fix the constraints so that this inconsistency |
|
439
|
|
|
|
|
|
|
# cannot be introduced; also rework this table completely so |
|
440
|
|
|
|
|
|
|
# that it uses IDs, not node names (will simplify rename_node too) |
|
441
|
0
|
|
|
|
|
0
|
my $sql = "SELECT DISTINCT internal_links.link_to |
|
442
|
|
|
|
|
|
|
FROM internal_links INNER JOIN node AS node_from ON |
|
443
|
|
|
|
|
|
|
node_from.name=internal_links.link_from LEFT JOIN node |
|
444
|
|
|
|
|
|
|
AS node_to ON node_to.name=internal_links.link_to |
|
445
|
|
|
|
|
|
|
WHERE node_to.version IS NULL"; |
|
446
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare($sql); |
|
447
|
0
|
0
|
|
|
|
0
|
$sth->execute or croak $dbh->errstr; |
|
448
|
0
|
|
|
|
|
0
|
my @links; |
|
449
|
0
|
|
|
|
|
0
|
while ( my ($link) = $self->charset_decode( $sth->fetchrow_array ) ) { |
|
450
|
0
|
|
|
|
|
0
|
push @links, $link; |
|
451
|
|
|
|
|
|
|
} |
|
452
|
0
|
|
|
|
|
0
|
return @links; |
|
453
|
|
|
|
|
|
|
} |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
=item B |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
$store->write_node_post_locking( node => $node, |
|
458
|
|
|
|
|
|
|
content => $content, |
|
459
|
|
|
|
|
|
|
links_to => \@links_to, |
|
460
|
|
|
|
|
|
|
metadata => \%metadata, |
|
461
|
|
|
|
|
|
|
requires_moderation => $requires_moderation, |
|
462
|
|
|
|
|
|
|
plugins => \@plugins ) |
|
463
|
|
|
|
|
|
|
or handle_error(); |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
Writes the specified content into the specified node, then calls |
|
466
|
|
|
|
|
|
|
C on all supplied plugins, with arguments C, |
|
467
|
|
|
|
|
|
|
C, C, C. |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
Making sure that locking/unlocking/transactions happen is left up to |
|
470
|
|
|
|
|
|
|
you (or your chosen subclass). This method shouldn't really be used |
|
471
|
|
|
|
|
|
|
directly as it might overwrite someone else's changes. Croaks on error |
|
472
|
|
|
|
|
|
|
but otherwise returns the version number of the update just made. A |
|
473
|
|
|
|
|
|
|
return value of -1 indicates that the change was not applied. This |
|
474
|
|
|
|
|
|
|
may be because the plugins voted against the change, or because the |
|
475
|
|
|
|
|
|
|
content and metadata in the proposed new version were identical to the |
|
476
|
|
|
|
|
|
|
current version (a "null" change). |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
Supplying a ref to an array of nodes that this ones links to is |
|
479
|
|
|
|
|
|
|
optional, but if you do supply it then this node will be returned when |
|
480
|
|
|
|
|
|
|
calling C on the nodes in C<@links_to>. B that |
|
481
|
|
|
|
|
|
|
if you don't supply the ref then the store will assume that this node |
|
482
|
|
|
|
|
|
|
doesn't link to any others, and update itself accordingly. |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
The metadata hashref is also optional, as is requires_moderation. |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
B on the metadata hashref: Any data in here that you wish to |
|
487
|
|
|
|
|
|
|
access directly later must be a key-value pair in which the value is |
|
488
|
|
|
|
|
|
|
either a scalar or a reference to an array of scalars. For example: |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
$wiki->write_node( "Calthorpe Arms", "nice pub", $checksum, |
|
491
|
|
|
|
|
|
|
{ category => [ "Pubs", "Bloomsbury" ], |
|
492
|
|
|
|
|
|
|
postcode => "WC1X 8JR" } ); |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
# and later |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
my @nodes = $wiki->list_nodes_by_metadata( |
|
497
|
|
|
|
|
|
|
metadata_type => "category", |
|
498
|
|
|
|
|
|
|
metadata_value => "Pubs" ); |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
For more advanced usage (passing data through to registered plugins) |
|
501
|
|
|
|
|
|
|
you may if you wish pass key-value pairs in which the value is a |
|
502
|
|
|
|
|
|
|
hashref or an array of hashrefs. The data in the hashrefs will not be |
|
503
|
|
|
|
|
|
|
stored as metadata; it will be checksummed and the checksum will be |
|
504
|
|
|
|
|
|
|
stored instead (as C<__metadatatypename__checksum>). Such data can |
|
505
|
|
|
|
|
|
|
I be accessed via plugins. |
|
506
|
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=cut |
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
sub write_node_post_locking { |
|
510
|
0
|
|
|
0
|
1
|
0
|
my ($self, %args) = @_; |
|
511
|
|
|
|
|
|
|
my ($node, $content, $links_to_ref, $metadata_ref, $requires_moderation) = |
|
512
|
0
|
|
|
|
|
0
|
@args{ qw( node content links_to metadata requires_moderation) }; |
|
513
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
514
|
|
|
|
|
|
|
|
|
515
|
0
|
|
|
|
|
0
|
my $timestamp = $self->_get_timestamp(); |
|
516
|
0
|
0
|
|
|
|
0
|
my @links_to = @{ $links_to_ref || [] }; # default to empty array |
|
|
0
|
|
|
|
|
0
|
|
|
517
|
0
|
|
|
|
|
0
|
my $version; |
|
518
|
0
|
0
|
|
|
|
0
|
unless($requires_moderation) { $requires_moderation = 0; } |
|
|
0
|
|
|
|
|
0
|
|
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
# Call pre_write on any plugins, in case they want to tweak anything |
|
521
|
0
|
0
|
|
|
|
0
|
my @preplugins = @{ $args{plugins} || [ ] }; |
|
|
0
|
|
|
|
|
0
|
|
|
522
|
0
|
|
|
|
|
0
|
my $write_allowed = 1; |
|
523
|
0
|
|
|
|
|
0
|
foreach my $plugin (@preplugins) { |
|
524
|
0
|
0
|
|
|
|
0
|
if ( $plugin->can( "pre_write" ) ) { |
|
525
|
0
|
|
|
|
|
0
|
handle_pre_plugin_ret( |
|
526
|
|
|
|
|
|
|
\$write_allowed, |
|
527
|
|
|
|
|
|
|
$plugin->pre_write( |
|
528
|
|
|
|
|
|
|
node => \$node, |
|
529
|
|
|
|
|
|
|
content => \$content, |
|
530
|
|
|
|
|
|
|
metadata => \$metadata_ref ) |
|
531
|
|
|
|
|
|
|
); |
|
532
|
|
|
|
|
|
|
} |
|
533
|
|
|
|
|
|
|
} |
|
534
|
0
|
0
|
|
|
|
0
|
if($write_allowed < 1) { |
|
535
|
|
|
|
|
|
|
# The plugins didn't want to allow this action |
|
536
|
0
|
|
|
|
|
0
|
return -1; |
|
537
|
|
|
|
|
|
|
} |
|
538
|
|
|
|
|
|
|
|
|
539
|
0
|
0
|
|
|
|
0
|
if ( $self->_checksum( %args ) eq $args{checksum} ) { |
|
540
|
|
|
|
|
|
|
# Refuse to commit as nothing has changed |
|
541
|
0
|
|
|
|
|
0
|
return -1; |
|
542
|
|
|
|
|
|
|
} |
|
543
|
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
# Either inserting a new page or updating an old one. |
|
545
|
0
|
|
|
|
|
0
|
my $sql = "SELECT count(*) FROM node WHERE name=" . $dbh->quote($node); |
|
546
|
0
|
|
0
|
|
|
0
|
my $exists = @{ $dbh->selectcol_arrayref($sql) }[0] || 0; |
|
547
|
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
# If it doesn't exist, add it right now |
|
550
|
0
|
0
|
|
|
|
0
|
if(! $exists) { |
|
551
|
|
|
|
|
|
|
# Add in a new version |
|
552
|
0
|
|
|
|
|
0
|
$version = 1; |
|
553
|
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
# Handle initial moderation |
|
555
|
0
|
|
|
|
|
0
|
my $node_content = $content; |
|
556
|
0
|
0
|
|
|
|
0
|
if($requires_moderation) { |
|
557
|
0
|
|
|
|
|
0
|
$node_content = "=== This page has yet to be moderated. ==="; |
|
558
|
|
|
|
|
|
|
} |
|
559
|
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
# Add the node and content |
|
561
|
0
|
|
|
|
|
0
|
my $add_sql = |
|
562
|
|
|
|
|
|
|
"INSERT INTO node " |
|
563
|
|
|
|
|
|
|
." (name, version, text, modified, moderate) " |
|
564
|
|
|
|
|
|
|
."VALUES (?, ?, ?, ?, ?)"; |
|
565
|
0
|
|
|
|
|
0
|
my $add_sth = $dbh->prepare($add_sql); |
|
566
|
|
|
|
|
|
|
$add_sth->execute( |
|
567
|
0
|
0
|
|
|
|
0
|
map{ $self->charset_encode($_) } |
|
|
0
|
|
|
|
|
0
|
|
|
568
|
|
|
|
|
|
|
($node, $version, $node_content, $timestamp, $requires_moderation) |
|
569
|
|
|
|
|
|
|
) or croak "Error updating database: " . DBI->errstr; |
|
570
|
|
|
|
|
|
|
} |
|
571
|
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
# Get the ID of the node we've added / we're about to update |
|
573
|
|
|
|
|
|
|
# Also get the moderation status for it |
|
574
|
0
|
|
|
|
|
0
|
$sql = "SELECT id, moderate FROM node WHERE name=" . $dbh->quote($node); |
|
575
|
0
|
|
|
|
|
0
|
my ($node_id,$node_requires_moderation) = $dbh->selectrow_array($sql); |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
# Only update node if it exists, and moderation isn't enabled on the node |
|
578
|
|
|
|
|
|
|
# Whatever happens, if it exists, generate a new version number |
|
579
|
0
|
0
|
|
|
|
0
|
if($exists) { |
|
580
|
|
|
|
|
|
|
# Get the new version number |
|
581
|
0
|
|
|
|
|
0
|
$sql = "SELECT max(content.version) FROM node |
|
582
|
|
|
|
|
|
|
INNER JOIN content ON (id = node_id) |
|
583
|
|
|
|
|
|
|
WHERE name=" . $dbh->quote($node); |
|
584
|
0
|
|
0
|
|
|
0
|
$version = @{ $dbh->selectcol_arrayref($sql) }[0] || 0; |
|
585
|
0
|
0
|
|
|
|
0
|
croak "Can't get version number" unless $version; |
|
586
|
0
|
|
|
|
|
0
|
$version++; |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
# Update the node only if node doesn't require moderation |
|
589
|
0
|
0
|
|
|
|
0
|
if(!$node_requires_moderation) { |
|
590
|
0
|
|
|
|
|
0
|
$sql = "UPDATE node SET version=" . $dbh->quote($version) |
|
591
|
|
|
|
|
|
|
. ", text=" . $dbh->quote($self->charset_encode($content)) |
|
592
|
|
|
|
|
|
|
. ", modified=" . $dbh->quote($timestamp) |
|
593
|
|
|
|
|
|
|
. " WHERE name=" . $dbh->quote($self->charset_encode($node)); |
|
594
|
0
|
0
|
|
|
|
0
|
$dbh->do($sql) or croak "Error updating database: " . DBI->errstr; |
|
595
|
|
|
|
|
|
|
} |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
# You can't use this to enable moderation on an existing node |
|
598
|
0
|
0
|
|
|
|
0
|
if($requires_moderation) { |
|
599
|
0
|
|
|
|
|
0
|
warn("Moderation not added to existing node '$node', use normal moderation methods instead"); |
|
600
|
|
|
|
|
|
|
} |
|
601
|
|
|
|
|
|
|
} |
|
602
|
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
# Now node is updated (if required), add to the history |
|
605
|
0
|
|
|
|
|
0
|
my $add_sql = |
|
606
|
|
|
|
|
|
|
"INSERT INTO content " |
|
607
|
|
|
|
|
|
|
." (node_id, version, text, modified, moderated) " |
|
608
|
|
|
|
|
|
|
."VALUES (?, ?, ?, ?, ?)"; |
|
609
|
0
|
|
|
|
|
0
|
my $add_sth = $dbh->prepare($add_sql); |
|
610
|
|
|
|
|
|
|
$add_sth->execute( |
|
611
|
0
|
0
|
|
|
|
0
|
map { $self->charset_encode($_) } |
|
|
0
|
|
|
|
|
0
|
|
|
612
|
|
|
|
|
|
|
($node_id, $version, $content, $timestamp, (1-$node_requires_moderation)) |
|
613
|
|
|
|
|
|
|
) or croak "Error updating database: " . DBI->errstr; |
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
# Update the backlinks. |
|
617
|
0
|
0
|
|
|
|
0
|
$dbh->do("DELETE FROM internal_links WHERE link_from=" |
|
618
|
|
|
|
|
|
|
. $dbh->quote($self->charset_encode($node)) ) or croak $dbh->errstr; |
|
619
|
0
|
|
|
|
|
0
|
foreach my $links_to ( @links_to ) { |
|
620
|
|
|
|
|
|
|
$sql = "INSERT INTO internal_links (link_from, link_to) VALUES (" |
|
621
|
0
|
|
|
|
|
0
|
. join(", ", map { $dbh->quote($self->charset_encode($_)) } ( $node, $links_to ) ) . ")"; |
|
|
0
|
|
|
|
|
0
|
|
|
622
|
|
|
|
|
|
|
# Better to drop a backlink or two than to lose the whole update. |
|
623
|
|
|
|
|
|
|
# Shevek wants a case-sensitive wiki, Jerakeen wants a case-insensitive |
|
624
|
|
|
|
|
|
|
# one, MySQL compares case-sensitively on varchars unless you add |
|
625
|
|
|
|
|
|
|
# the binary keyword. Case-sensitivity to be revisited. |
|
626
|
0
|
|
|
|
|
0
|
eval { $dbh->do($sql); }; |
|
|
0
|
|
|
|
|
0
|
|
|
627
|
0
|
0
|
|
|
|
0
|
carp "Couldn't index backlink: " . $dbh->errstr if $@; |
|
628
|
|
|
|
|
|
|
} |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
# And also store any metadata. Note that any entries already in the |
|
631
|
|
|
|
|
|
|
# metadata table refer to old versions, so we don't need to delete them. |
|
632
|
0
|
0
|
|
|
|
0
|
my %metadata = %{ $metadata_ref || {} }; # default to no metadata |
|
|
0
|
|
|
|
|
0
|
|
|
633
|
0
|
|
|
|
|
0
|
foreach my $type ( keys %metadata ) { |
|
634
|
0
|
|
|
|
|
0
|
my $val = $metadata{$type}; |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
# We might have one or many values; make an array now to merge cases. |
|
637
|
0
|
0
|
0
|
|
|
0
|
my @values = (ref $val and ref $val eq 'ARRAY') ? @$val : ( $val ); |
|
638
|
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
# Find out whether all values for this type are scalars. |
|
640
|
0
|
|
|
|
|
0
|
my $all_scalars = 1; |
|
641
|
0
|
|
|
|
|
0
|
foreach my $value (@values) { |
|
642
|
0
|
0
|
|
|
|
0
|
$all_scalars = 0 if ref $value; |
|
643
|
|
|
|
|
|
|
} |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
# For adding to metadata |
|
646
|
0
|
|
|
|
|
0
|
my $add_sql = |
|
647
|
|
|
|
|
|
|
"INSERT INTO metadata " |
|
648
|
|
|
|
|
|
|
." (node_id, version, metadata_type, metadata_value) " |
|
649
|
|
|
|
|
|
|
."VALUES (?, ?, ?, ?)"; |
|
650
|
0
|
|
|
|
|
0
|
my $add_sth = $dbh->prepare($add_sql); |
|
651
|
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
# If all values for this type are scalars, strip out any duplicates |
|
653
|
|
|
|
|
|
|
# and store the data. |
|
654
|
0
|
0
|
|
|
|
0
|
if ( $all_scalars ) { |
|
655
|
0
|
|
|
|
|
0
|
my %unique = map { $_ => 1 } @values; |
|
|
0
|
|
|
|
|
0
|
|
|
656
|
0
|
|
|
|
|
0
|
@values = keys %unique; |
|
657
|
|
|
|
|
|
|
|
|
658
|
0
|
|
|
|
|
0
|
foreach my $value ( @values ) { |
|
659
|
|
|
|
|
|
|
$add_sth->execute( |
|
660
|
0
|
0
|
|
|
|
0
|
map { $self->charset_encode($_) } |
|
|
0
|
|
|
|
|
0
|
|
|
661
|
|
|
|
|
|
|
( $node_id, $version, $type, $value ) |
|
662
|
|
|
|
|
|
|
) or croak $dbh->errstr; |
|
663
|
|
|
|
|
|
|
} |
|
664
|
|
|
|
|
|
|
} else { |
|
665
|
|
|
|
|
|
|
# Otherwise grab a checksum and store that. |
|
666
|
0
|
|
|
|
|
0
|
my $type_to_store = "__" . $type . "__checksum"; |
|
667
|
0
|
|
|
|
|
0
|
my $value_to_store = $self->_checksum_hashes( @values ); |
|
668
|
|
|
|
|
|
|
$add_sth->execute( |
|
669
|
0
|
0
|
|
|
|
0
|
map { $self->charset_encode($_) } |
|
|
0
|
|
|
|
|
0
|
|
|
670
|
|
|
|
|
|
|
( $node_id, $version, $type_to_store, $value_to_store ) |
|
671
|
|
|
|
|
|
|
) or croak $dbh->errstr; |
|
672
|
|
|
|
|
|
|
} |
|
673
|
|
|
|
|
|
|
} |
|
674
|
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
# Finally call post_write on any plugins. |
|
676
|
0
|
0
|
|
|
|
0
|
my @postplugins = @{ $args{plugins} || [ ] }; |
|
|
0
|
|
|
|
|
0
|
|
|
677
|
0
|
|
|
|
|
0
|
foreach my $plugin (@postplugins) { |
|
678
|
0
|
0
|
|
|
|
0
|
if ( $plugin->can( "post_write" ) ) { |
|
679
|
0
|
|
|
|
|
0
|
$plugin->post_write( |
|
680
|
|
|
|
|
|
|
node => $node, |
|
681
|
|
|
|
|
|
|
node_id => $node_id, |
|
682
|
|
|
|
|
|
|
version => $version, |
|
683
|
|
|
|
|
|
|
content => $content, |
|
684
|
|
|
|
|
|
|
metadata => $metadata_ref ); |
|
685
|
|
|
|
|
|
|
} |
|
686
|
|
|
|
|
|
|
} |
|
687
|
|
|
|
|
|
|
|
|
688
|
0
|
|
|
|
|
0
|
return $version; |
|
689
|
|
|
|
|
|
|
} |
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
# Returns the timestamp of now, unless epoch is supplied. |
|
692
|
|
|
|
|
|
|
sub _get_timestamp { |
|
693
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
694
|
|
|
|
|
|
|
# I don't care about no steenkin' timezones (yet). |
|
695
|
0
|
|
0
|
|
|
0
|
my $time = shift || localtime; # Overloaded by Time::Piece. |
|
696
|
0
|
0
|
|
|
|
0
|
unless( ref $time ) { |
|
697
|
0
|
|
|
|
|
0
|
$time = localtime($time); # Make it into an object for strftime |
|
698
|
|
|
|
|
|
|
} |
|
699
|
0
|
|
|
|
|
0
|
return $time->strftime($timestamp_fmt); # global |
|
700
|
|
|
|
|
|
|
} |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=item B |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
$store->rename_node( |
|
705
|
|
|
|
|
|
|
old_name => $node, |
|
706
|
|
|
|
|
|
|
new_name => $new_node, |
|
707
|
|
|
|
|
|
|
wiki => $wiki, |
|
708
|
|
|
|
|
|
|
create_new_versions => $create_new_versions, |
|
709
|
|
|
|
|
|
|
); |
|
710
|
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
Renames a node, updating any references to it as required (assuming your |
|
712
|
|
|
|
|
|
|
chosen formatter supports rename, that is). |
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
Uses the internal_links table to identify the nodes that link to this |
|
715
|
|
|
|
|
|
|
one, and re-writes any wiki links in these to point to the new name. |
|
716
|
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=cut |
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
sub rename_node { |
|
720
|
0
|
|
|
0
|
1
|
0
|
my ($self, %args) = @_; |
|
721
|
|
|
|
|
|
|
my ($old_name,$new_name,$wiki,$create_new_versions) = |
|
722
|
0
|
|
|
|
|
0
|
@args{ qw( old_name new_name wiki create_new_versions ) }; |
|
723
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
724
|
0
|
|
|
|
|
0
|
my $formatter = $wiki->{_formatter}; |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
# For formatters that support it, run the new name through the node name |
|
727
|
|
|
|
|
|
|
# to param conversion and back again, to make sure any necessary munging |
|
728
|
|
|
|
|
|
|
# gets done. |
|
729
|
0
|
0
|
0
|
|
|
0
|
if ( $formatter->can( "node_name_to_node_param" ) |
|
730
|
|
|
|
|
|
|
&& $formatter->can( "node_param_to_node_name" ) ) { |
|
731
|
0
|
|
|
|
|
0
|
$new_name = $formatter->node_param_to_node_name( |
|
732
|
|
|
|
|
|
|
$formatter->node_name_to_node_param( $new_name ) ); |
|
733
|
|
|
|
|
|
|
} |
|
734
|
|
|
|
|
|
|
|
|
735
|
0
|
|
|
|
|
0
|
my $timestamp = $self->_get_timestamp(); |
|
736
|
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
# Call pre_rename on any plugins, in case they want to tweak anything |
|
738
|
0
|
0
|
|
|
|
0
|
my @preplugins = @{ $args{plugins} || [ ] }; |
|
|
0
|
|
|
|
|
0
|
|
|
739
|
0
|
|
|
|
|
0
|
my $rename_allowed = 1; |
|
740
|
0
|
|
|
|
|
0
|
foreach my $plugin (@preplugins) { |
|
741
|
0
|
0
|
|
|
|
0
|
if ( $plugin->can( "pre_rename" ) ) { |
|
742
|
0
|
|
|
|
|
0
|
handle_pre_plugin_ret( |
|
743
|
|
|
|
|
|
|
\$rename_allowed, |
|
744
|
|
|
|
|
|
|
$plugin->pre_rename( |
|
745
|
|
|
|
|
|
|
old_name => \$old_name, |
|
746
|
|
|
|
|
|
|
new_name => \$new_name, |
|
747
|
|
|
|
|
|
|
create_new_versions => \$create_new_versions, |
|
748
|
|
|
|
|
|
|
) |
|
749
|
|
|
|
|
|
|
); |
|
750
|
|
|
|
|
|
|
} |
|
751
|
|
|
|
|
|
|
} |
|
752
|
0
|
0
|
|
|
|
0
|
if($rename_allowed < 1) { |
|
753
|
|
|
|
|
|
|
# The plugins didn't want to allow this action |
|
754
|
0
|
|
|
|
|
0
|
return -1; |
|
755
|
|
|
|
|
|
|
} |
|
756
|
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
# Get the ID of the node |
|
758
|
0
|
|
|
|
|
0
|
my $sql = "SELECT id FROM node WHERE name=?"; |
|
759
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare($sql); |
|
760
|
0
|
|
|
|
|
0
|
$sth->execute($old_name); |
|
761
|
0
|
|
|
|
|
0
|
my ($node_id) = $sth->fetchrow_array; |
|
762
|
0
|
|
|
|
|
0
|
$sth->finish; |
|
763
|
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
# If the formatter supports it, get a list of the internal |
|
766
|
|
|
|
|
|
|
# links to the page, which will have their links re-written |
|
767
|
|
|
|
|
|
|
# (Do now before we update the name of the node, in case of |
|
768
|
|
|
|
|
|
|
# self links) |
|
769
|
0
|
|
|
|
|
0
|
my @links; |
|
770
|
0
|
0
|
|
|
|
0
|
if($formatter->can("rename_links")) { |
|
771
|
|
|
|
|
|
|
# Get a list of the pages that link to the page |
|
772
|
0
|
|
|
|
|
0
|
$sql = "SELECT id, name, version " |
|
773
|
|
|
|
|
|
|
."FROM internal_links " |
|
774
|
|
|
|
|
|
|
."INNER JOIN node " |
|
775
|
|
|
|
|
|
|
." ON (link_from = name) " |
|
776
|
|
|
|
|
|
|
."WHERE link_to = ?"; |
|
777
|
0
|
|
|
|
|
0
|
$sth = $dbh->prepare($sql); |
|
778
|
0
|
|
|
|
|
0
|
$sth->execute($old_name); |
|
779
|
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
# Grab them all, then update, so no locking problems |
|
781
|
0
|
|
|
|
|
0
|
while(my @l = $sth->fetchrow_array) { push (@links, \@l); } |
|
|
0
|
|
|
|
|
0
|
|
|
782
|
|
|
|
|
|
|
} |
|
783
|
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
# Rename the node |
|
786
|
0
|
|
|
|
|
0
|
$sql = "UPDATE node SET name=? WHERE id=?"; |
|
787
|
0
|
|
|
|
|
0
|
$sth = $dbh->prepare($sql); |
|
788
|
0
|
|
|
|
|
0
|
$sth->execute($new_name,$node_id); |
|
789
|
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
# Fix the internal links from this page |
|
792
|
|
|
|
|
|
|
# (Otherwise write_node will get confused if we rename links later on) |
|
793
|
0
|
|
|
|
|
0
|
$sql = "UPDATE internal_links SET link_from=? WHERE link_from=?"; |
|
794
|
0
|
|
|
|
|
0
|
$sth = $dbh->prepare($sql); |
|
795
|
0
|
|
|
|
|
0
|
$sth->execute($new_name,$old_name); |
|
796
|
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
# Update the text of internal links, if the formatter supports it |
|
799
|
0
|
0
|
|
|
|
0
|
if($formatter->can("rename_links")) { |
|
800
|
|
|
|
|
|
|
# Update the linked pages (may include renamed page) |
|
801
|
0
|
|
|
|
|
0
|
foreach my $l (@links) { |
|
802
|
0
|
|
|
|
|
0
|
my ($page_id, $page_name, $page_version) = @$l; |
|
803
|
|
|
|
|
|
|
# Self link special case |
|
804
|
0
|
0
|
|
|
|
0
|
if($page_name eq $old_name) { $page_name = $new_name; } |
|
|
0
|
|
|
|
|
0
|
|
|
805
|
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
# Grab the latest version of that page |
|
807
|
0
|
|
|
|
|
0
|
my %page = $self->retrieve_node( |
|
808
|
|
|
|
|
|
|
name=>$page_name, version=>$page_version |
|
809
|
|
|
|
|
|
|
); |
|
810
|
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
# Update the content of the page |
|
812
|
|
|
|
|
|
|
my $new_content = |
|
813
|
0
|
|
|
|
|
0
|
$formatter->rename_links($old_name,$new_name,$page{'content'}); |
|
814
|
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
# Did it change? |
|
816
|
0
|
0
|
|
|
|
0
|
if($new_content ne $page{'content'}) { |
|
817
|
|
|
|
|
|
|
# Write the updated page out |
|
818
|
0
|
0
|
|
|
|
0
|
if($create_new_versions) { |
|
819
|
|
|
|
|
|
|
# Write out as a new version of the node |
|
820
|
|
|
|
|
|
|
# (This will also fix our internal links) |
|
821
|
|
|
|
|
|
|
$wiki->write_node( |
|
822
|
|
|
|
|
|
|
$page_name, |
|
823
|
|
|
|
|
|
|
$new_content, |
|
824
|
|
|
|
|
|
|
$page{checksum}, |
|
825
|
|
|
|
|
|
|
$page{metadata} |
|
826
|
0
|
|
|
|
|
0
|
); |
|
827
|
|
|
|
|
|
|
} else { |
|
828
|
|
|
|
|
|
|
# Just update the content |
|
829
|
0
|
|
|
|
|
0
|
my $update_sql_a = "UPDATE node SET text=? WHERE id=?"; |
|
830
|
0
|
|
|
|
|
0
|
my $update_sql_b = "UPDATE content SET text=? ". |
|
831
|
|
|
|
|
|
|
"WHERE node_id=? AND version=?"; |
|
832
|
|
|
|
|
|
|
|
|
833
|
0
|
|
|
|
|
0
|
my $u_sth = $dbh->prepare($update_sql_a); |
|
834
|
0
|
|
|
|
|
0
|
$u_sth->execute($new_content,$page_id); |
|
835
|
0
|
|
|
|
|
0
|
$u_sth = $dbh->prepare($update_sql_b); |
|
836
|
0
|
|
|
|
|
0
|
$u_sth->execute($new_content,$page_id,$page_version); |
|
837
|
|
|
|
|
|
|
} |
|
838
|
|
|
|
|
|
|
} |
|
839
|
|
|
|
|
|
|
} |
|
840
|
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
# Fix the internal links if we didn't create new versions of the node |
|
842
|
0
|
0
|
|
|
|
0
|
if(! $create_new_versions) { |
|
843
|
0
|
|
|
|
|
0
|
$sql = "UPDATE internal_links SET link_to=? WHERE link_to=?"; |
|
844
|
0
|
|
|
|
|
0
|
$sth = $dbh->prepare($sql); |
|
845
|
0
|
|
|
|
|
0
|
$sth->execute($new_name,$old_name); |
|
846
|
|
|
|
|
|
|
} |
|
847
|
|
|
|
|
|
|
} else { |
|
848
|
0
|
|
|
|
|
0
|
warn("Internal links not updated following node rename - unsupported by formatter"); |
|
849
|
|
|
|
|
|
|
} |
|
850
|
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
# Call post_rename on any plugins, in case they want to do anything |
|
852
|
0
|
0
|
|
|
|
0
|
my @postplugins = @{ $args{plugins} || [ ] }; |
|
|
0
|
|
|
|
|
0
|
|
|
853
|
0
|
|
|
|
|
0
|
foreach my $plugin (@postplugins) { |
|
854
|
0
|
0
|
|
|
|
0
|
if ( $plugin->can( "post_rename" ) ) { |
|
855
|
0
|
|
|
|
|
0
|
$plugin->post_rename( |
|
856
|
|
|
|
|
|
|
old_name => $old_name, |
|
857
|
|
|
|
|
|
|
new_name => $new_name, |
|
858
|
|
|
|
|
|
|
node_id => $node_id, |
|
859
|
|
|
|
|
|
|
); |
|
860
|
|
|
|
|
|
|
} |
|
861
|
|
|
|
|
|
|
} |
|
862
|
|
|
|
|
|
|
} |
|
863
|
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
=item B |
|
865
|
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
$store->moderate_node( |
|
867
|
|
|
|
|
|
|
name => $node, |
|
868
|
|
|
|
|
|
|
version => $version |
|
869
|
|
|
|
|
|
|
); |
|
870
|
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
Marks the given version of the node as moderated. If this is the |
|
872
|
|
|
|
|
|
|
highest moderated version, then update the node's contents to hold |
|
873
|
|
|
|
|
|
|
this version. |
|
874
|
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
=cut |
|
876
|
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
sub moderate_node { |
|
878
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
879
|
0
|
0
|
|
|
|
0
|
my %args = scalar @_ == 2 ? ( name => $_[0], version => $_[1] ) : @_; |
|
880
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
881
|
|
|
|
|
|
|
|
|
882
|
0
|
|
|
|
|
0
|
my ($name,$version) = ($args{name},$args{version}); |
|
883
|
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
# Call pre_moderate on any plugins. |
|
885
|
0
|
0
|
|
|
|
0
|
my @plugins = @{ $args{plugins} || [ ] }; |
|
|
0
|
|
|
|
|
0
|
|
|
886
|
0
|
|
|
|
|
0
|
my $moderation_allowed = 1; |
|
887
|
0
|
|
|
|
|
0
|
foreach my $plugin (@plugins) { |
|
888
|
0
|
0
|
|
|
|
0
|
if ( $plugin->can( "pre_moderate" ) ) { |
|
889
|
0
|
|
|
|
|
0
|
handle_pre_plugin_ret( |
|
890
|
|
|
|
|
|
|
\$moderation_allowed, |
|
891
|
|
|
|
|
|
|
$plugin->pre_moderate( |
|
892
|
|
|
|
|
|
|
node => \$name, |
|
893
|
|
|
|
|
|
|
version => \$version ) |
|
894
|
|
|
|
|
|
|
); |
|
895
|
|
|
|
|
|
|
} |
|
896
|
|
|
|
|
|
|
} |
|
897
|
0
|
0
|
|
|
|
0
|
if($moderation_allowed < 1) { |
|
898
|
|
|
|
|
|
|
# The plugins didn't want to allow this action |
|
899
|
0
|
|
|
|
|
0
|
return -1; |
|
900
|
|
|
|
|
|
|
} |
|
901
|
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
# Get the ID of this node |
|
903
|
0
|
|
|
|
|
0
|
my $id_sql = "SELECT id FROM node WHERE name=?"; |
|
904
|
0
|
|
|
|
|
0
|
my $id_sth = $dbh->prepare($id_sql); |
|
905
|
0
|
|
|
|
|
0
|
$id_sth->execute($name); |
|
906
|
0
|
|
|
|
|
0
|
my ($node_id) = $id_sth->fetchrow_array; |
|
907
|
0
|
|
|
|
|
0
|
$id_sth->finish; |
|
908
|
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
# Check what the current highest moderated version is |
|
910
|
0
|
|
|
|
|
0
|
my $hv_sql = |
|
911
|
|
|
|
|
|
|
"SELECT max(version) " |
|
912
|
|
|
|
|
|
|
."FROM content " |
|
913
|
|
|
|
|
|
|
."WHERE node_id = ? " |
|
914
|
|
|
|
|
|
|
."AND moderated = ?"; |
|
915
|
0
|
|
|
|
|
0
|
my $hv_sth = $dbh->prepare($hv_sql); |
|
916
|
0
|
0
|
|
|
|
0
|
$hv_sth->execute($node_id, "1") or croak $dbh->errstr; |
|
917
|
0
|
|
|
|
|
0
|
my ($highest_mod_version) = $hv_sth->fetchrow_array; |
|
918
|
0
|
|
|
|
|
0
|
$hv_sth->finish; |
|
919
|
0
|
0
|
|
|
|
0
|
unless($highest_mod_version) { $highest_mod_version = 0; } |
|
|
0
|
|
|
|
|
0
|
|
|
920
|
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
# Mark this version as moderated |
|
922
|
0
|
|
|
|
|
0
|
my $update_sql = |
|
923
|
|
|
|
|
|
|
"UPDATE content " |
|
924
|
|
|
|
|
|
|
."SET moderated = ? " |
|
925
|
|
|
|
|
|
|
."WHERE node_id = ? " |
|
926
|
|
|
|
|
|
|
."AND version = ?"; |
|
927
|
0
|
|
|
|
|
0
|
my $update_sth = $dbh->prepare($update_sql); |
|
928
|
0
|
0
|
|
|
|
0
|
$update_sth->execute("1", $node_id, $version) or croak $dbh->errstr; |
|
929
|
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
# Are we now the highest moderated version? |
|
931
|
0
|
0
|
|
|
|
0
|
if(int($version) > int($highest_mod_version)) { |
|
932
|
|
|
|
|
|
|
# Newly moderated version is newer than previous moderated version |
|
933
|
|
|
|
|
|
|
# So, make the current version the latest version |
|
934
|
0
|
|
|
|
|
0
|
my %new_data = $self->retrieve_node( name => $name, version => $version ); |
|
935
|
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
# Make sure last modified is properly null, if not set |
|
937
|
0
|
0
|
|
|
|
0
|
unless($new_data{last_modified}) { $new_data{last_modified} = undef; } |
|
|
0
|
|
|
|
|
0
|
|
|
938
|
|
|
|
|
|
|
|
|
939
|
0
|
|
|
|
|
0
|
my $newv_sql = |
|
940
|
|
|
|
|
|
|
"UPDATE node " |
|
941
|
|
|
|
|
|
|
."SET version=?, text=?, modified=? " |
|
942
|
|
|
|
|
|
|
."WHERE id = ?"; |
|
943
|
0
|
|
|
|
|
0
|
my $newv_sth = $dbh->prepare($newv_sql); |
|
944
|
|
|
|
|
|
|
$newv_sth->execute( |
|
945
|
|
|
|
|
|
|
$version, $self->charset_encode($new_data{content}), |
|
946
|
0
|
0
|
|
|
|
0
|
$new_data{last_modified}, $node_id |
|
947
|
|
|
|
|
|
|
) or croak $dbh->errstr; |
|
948
|
|
|
|
|
|
|
} else { |
|
949
|
|
|
|
|
|
|
# A higher version is already moderated, so don't change node |
|
950
|
|
|
|
|
|
|
} |
|
951
|
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
# TODO: Do something about internal links, if required |
|
953
|
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
# Finally call post_moderate on any plugins. |
|
955
|
0
|
0
|
|
|
|
0
|
@plugins = @{ $args{plugins} || [ ] }; |
|
|
0
|
|
|
|
|
0
|
|
|
956
|
0
|
|
|
|
|
0
|
foreach my $plugin (@plugins) { |
|
957
|
0
|
0
|
|
|
|
0
|
if ( $plugin->can( "post_moderate" ) ) { |
|
958
|
0
|
|
|
|
|
0
|
$plugin->post_moderate( |
|
959
|
|
|
|
|
|
|
node => $name, |
|
960
|
|
|
|
|
|
|
node_id => $node_id, |
|
961
|
|
|
|
|
|
|
version => $version ); |
|
962
|
|
|
|
|
|
|
} |
|
963
|
|
|
|
|
|
|
} |
|
964
|
|
|
|
|
|
|
|
|
965
|
0
|
|
|
|
|
0
|
return 1; |
|
966
|
|
|
|
|
|
|
} |
|
967
|
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
=item B |
|
969
|
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
$store->set_node_moderation( |
|
971
|
|
|
|
|
|
|
name => $node, |
|
972
|
|
|
|
|
|
|
required => $required |
|
973
|
|
|
|
|
|
|
); |
|
974
|
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
Sets if new node versions will require moderation or not |
|
976
|
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
=cut |
|
978
|
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
sub set_node_moderation { |
|
980
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
981
|
0
|
0
|
|
|
|
0
|
my %args = scalar @_ == 2 ? ( name => $_[0], required => $_[1] ) : @_; |
|
982
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
983
|
|
|
|
|
|
|
|
|
984
|
0
|
|
|
|
|
0
|
my ($name,$required) = ($args{name},$args{required}); |
|
985
|
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
# Get the ID of this node |
|
987
|
0
|
|
|
|
|
0
|
my $id_sql = "SELECT id FROM node WHERE name=?"; |
|
988
|
0
|
|
|
|
|
0
|
my $id_sth = $dbh->prepare($id_sql); |
|
989
|
0
|
|
|
|
|
0
|
$id_sth->execute($name); |
|
990
|
0
|
|
|
|
|
0
|
my ($node_id) = $id_sth->fetchrow_array; |
|
991
|
0
|
|
|
|
|
0
|
$id_sth->finish; |
|
992
|
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
# Check we really got an ID |
|
994
|
0
|
0
|
|
|
|
0
|
unless($node_id) { |
|
995
|
0
|
|
|
|
|
0
|
return 0; |
|
996
|
|
|
|
|
|
|
} |
|
997
|
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
# Mark it as requiring / not requiring moderation |
|
999
|
0
|
|
|
|
|
0
|
my $mod_sql = |
|
1000
|
|
|
|
|
|
|
"UPDATE node " |
|
1001
|
|
|
|
|
|
|
."SET moderate = ? " |
|
1002
|
|
|
|
|
|
|
."WHERE id = ? "; |
|
1003
|
0
|
|
|
|
|
0
|
my $mod_sth = $dbh->prepare($mod_sql); |
|
1004
|
0
|
0
|
|
|
|
0
|
$mod_sth->execute("$required", $node_id) or croak $dbh->errstr; |
|
1005
|
|
|
|
|
|
|
|
|
1006
|
0
|
|
|
|
|
0
|
return 1; |
|
1007
|
|
|
|
|
|
|
} |
|
1008
|
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
=item B |
|
1010
|
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
$store->delete_node( |
|
1012
|
|
|
|
|
|
|
name => $node, |
|
1013
|
|
|
|
|
|
|
version => $version, |
|
1014
|
|
|
|
|
|
|
wiki => $wiki |
|
1015
|
|
|
|
|
|
|
); |
|
1016
|
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
C is optional. If it is supplied then only that version of |
|
1018
|
|
|
|
|
|
|
the node will be deleted. Otherwise the node and all its history will |
|
1019
|
|
|
|
|
|
|
be completely deleted. |
|
1020
|
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
C is also optional, but if you care about updating the backlinks |
|
1022
|
|
|
|
|
|
|
you want to include it. |
|
1023
|
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
Again, doesn't do any locking. You probably don't want to let anyone |
|
1025
|
|
|
|
|
|
|
except Wiki admins call this. You may not want to use it at all. |
|
1026
|
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
Croaks on error, silently does nothing if the node or version doesn't |
|
1028
|
|
|
|
|
|
|
exist, returns true if no error. |
|
1029
|
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
=cut |
|
1031
|
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
sub delete_node { |
|
1033
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1034
|
|
|
|
|
|
|
# Backwards compatibility. |
|
1035
|
0
|
0
|
|
|
|
0
|
my %args = ( scalar @_ == 1 ) ? ( name => $_[0] ) : @_; |
|
1036
|
|
|
|
|
|
|
|
|
1037
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
1038
|
0
|
|
|
|
|
0
|
my ($name, $version, $wiki) = @args{ qw( name version wiki ) }; |
|
1039
|
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
# Grab the ID of this node |
|
1041
|
|
|
|
|
|
|
# (It will only ever have one entry in node, but might have entries |
|
1042
|
|
|
|
|
|
|
# for other versions in metadata and content) |
|
1043
|
0
|
|
|
|
|
0
|
my $id_sql = "SELECT id FROM node WHERE name=?"; |
|
1044
|
0
|
|
|
|
|
0
|
my $id_sth = $dbh->prepare($id_sql); |
|
1045
|
0
|
|
|
|
|
0
|
$id_sth->execute($name); |
|
1046
|
0
|
|
|
|
|
0
|
my ($node_id) = $id_sth->fetchrow_array; |
|
1047
|
0
|
|
|
|
|
0
|
$id_sth->finish; |
|
1048
|
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
# Trivial case - delete the whole node and all its history. |
|
1050
|
0
|
0
|
|
|
|
0
|
unless ( $version ) { |
|
1051
|
0
|
|
|
|
|
0
|
my $sql; |
|
1052
|
|
|
|
|
|
|
# Should start a transaction here. FIXME. |
|
1053
|
|
|
|
|
|
|
# Do deletes |
|
1054
|
0
|
|
|
|
|
0
|
$sql = "DELETE FROM content WHERE node_id = $node_id"; |
|
1055
|
0
|
0
|
|
|
|
0
|
$dbh->do($sql) or croak "Deletion failed: " . DBI->errstr; |
|
1056
|
0
|
|
|
|
|
0
|
$sql = "DELETE FROM internal_links WHERE link_from=".$dbh->quote($name); |
|
1057
|
0
|
0
|
|
|
|
0
|
$dbh->do($sql) or croak $dbh->errstr; |
|
1058
|
0
|
|
|
|
|
0
|
$sql = "DELETE FROM metadata WHERE node_id = $node_id"; |
|
1059
|
0
|
0
|
|
|
|
0
|
$dbh->do($sql) or croak $dbh->errstr; |
|
1060
|
0
|
|
|
|
|
0
|
$sql = "DELETE FROM node WHERE id = $node_id"; |
|
1061
|
0
|
0
|
|
|
|
0
|
$dbh->do($sql) or croak "Deletion failed: " . DBI->errstr; |
|
1062
|
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
# And finish it here. |
|
1064
|
0
|
|
|
|
|
0
|
post_delete_node($name,$node_id,$version,$args{plugins}); |
|
1065
|
0
|
|
|
|
|
0
|
return 1; |
|
1066
|
|
|
|
|
|
|
} |
|
1067
|
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
# Skip out early if we're trying to delete a nonexistent version. |
|
1069
|
0
|
|
|
|
|
0
|
my %verdata = $self->retrieve_node( name => $name, version => $version ); |
|
1070
|
0
|
0
|
|
|
|
0
|
unless($verdata{version}) { |
|
1071
|
0
|
|
|
|
|
0
|
warn( "Asked to delete nonexistent version $version of node " |
|
1072
|
|
|
|
|
|
|
. "$node_id ($name)" ); |
|
1073
|
0
|
|
|
|
|
0
|
return 1; |
|
1074
|
|
|
|
|
|
|
} |
|
1075
|
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
# Reduce to trivial case if deleting the only version. |
|
1077
|
0
|
|
|
|
|
0
|
my $sql = "SELECT COUNT(*) FROM content WHERE node_id = $node_id"; |
|
1078
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare( $sql ); |
|
1079
|
0
|
0
|
|
|
|
0
|
$sth->execute() or croak "Deletion failed: " . $dbh->errstr; |
|
1080
|
0
|
|
|
|
|
0
|
my ($count) = $sth->fetchrow_array; |
|
1081
|
0
|
|
|
|
|
0
|
$sth->finish; |
|
1082
|
0
|
0
|
|
|
|
0
|
if($count == 1) { |
|
1083
|
|
|
|
|
|
|
# Only one version, so can do the non version delete |
|
1084
|
0
|
|
|
|
|
0
|
return $self->delete_node( name=>$name, plugins=>$args{plugins} ); |
|
1085
|
|
|
|
|
|
|
} |
|
1086
|
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
# Check whether we're deleting the latest (moderated) version. |
|
1088
|
0
|
|
|
|
|
0
|
my %currdata = $self->retrieve_node( name => $name ); |
|
1089
|
0
|
0
|
|
|
|
0
|
if ( $currdata{version} == $version ) { |
|
1090
|
|
|
|
|
|
|
# Deleting latest version, so need to update the copy in node |
|
1091
|
|
|
|
|
|
|
# (Can't just grab version ($version - 1) since it may have been |
|
1092
|
|
|
|
|
|
|
# deleted itself, or might not be moderated.) |
|
1093
|
0
|
|
|
|
|
0
|
my $try = $version - 1; |
|
1094
|
0
|
|
|
|
|
0
|
my %prevdata; |
|
1095
|
0
|
|
0
|
|
|
0
|
until ( $prevdata{version} && $prevdata{moderated} ) { |
|
1096
|
0
|
|
|
|
|
0
|
%prevdata = $self->retrieve_node( |
|
1097
|
|
|
|
|
|
|
name => $name, |
|
1098
|
|
|
|
|
|
|
version => $try, |
|
1099
|
|
|
|
|
|
|
); |
|
1100
|
0
|
|
|
|
|
0
|
$try--; |
|
1101
|
|
|
|
|
|
|
} |
|
1102
|
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
# Move to new (old) version |
|
1104
|
0
|
|
|
|
|
0
|
my $sql="UPDATE node |
|
1105
|
|
|
|
|
|
|
SET version=?, text=?, modified=? |
|
1106
|
|
|
|
|
|
|
WHERE name=?"; |
|
1107
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare( $sql ); |
|
1108
|
0
|
0
|
|
|
|
0
|
$sth->execute( @prevdata{ qw( version content last_modified ) }, $name) |
|
1109
|
|
|
|
|
|
|
or croak "Deletion failed: " . $dbh->errstr; |
|
1110
|
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
# Remove the current version from content |
|
1112
|
0
|
|
|
|
|
0
|
$sql = "DELETE FROM content |
|
1113
|
|
|
|
|
|
|
WHERE node_id = $node_id |
|
1114
|
|
|
|
|
|
|
AND version = $version"; |
|
1115
|
0
|
|
|
|
|
0
|
$sth = $dbh->prepare( $sql ); |
|
1116
|
0
|
0
|
|
|
|
0
|
$sth->execute() |
|
1117
|
|
|
|
|
|
|
or croak "Deletion failed: " . $dbh->errstr; |
|
1118
|
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
# Update the internal links to reflect the new version |
|
1120
|
0
|
|
|
|
|
0
|
$sql = "DELETE FROM internal_links WHERE link_from=?"; |
|
1121
|
0
|
|
|
|
|
0
|
$sth = $dbh->prepare( $sql ); |
|
1122
|
0
|
0
|
|
|
|
0
|
$sth->execute( $name ) |
|
1123
|
|
|
|
|
|
|
or croak "Deletion failed: " . $dbh->errstr; |
|
1124
|
0
|
|
|
|
|
0
|
my @links_to; |
|
1125
|
0
|
|
|
|
|
0
|
my $formatter = $wiki->formatter; |
|
1126
|
0
|
0
|
|
|
|
0
|
if ( $formatter->can( "find_internal_links" ) ) { |
|
1127
|
|
|
|
|
|
|
# Supply $metadata to formatter in case it's needed to alter the |
|
1128
|
|
|
|
|
|
|
# behaviour of the formatter, eg for Wiki::Toolkit::Formatter::Multiple |
|
1129
|
|
|
|
|
|
|
my @all = $formatter->find_internal_links( |
|
1130
|
0
|
|
|
|
|
0
|
$prevdata{content}, $prevdata{metadata} ); |
|
1131
|
0
|
|
|
|
|
0
|
my %unique = map { $_ => 1 } @all; |
|
|
0
|
|
|
|
|
0
|
|
|
1132
|
0
|
|
|
|
|
0
|
@links_to = keys %unique; |
|
1133
|
|
|
|
|
|
|
} |
|
1134
|
0
|
|
|
|
|
0
|
$sql = "INSERT INTO internal_links (link_from, link_to) VALUES (?,?)"; |
|
1135
|
0
|
|
|
|
|
0
|
$sth = $dbh->prepare( $sql ); |
|
1136
|
0
|
|
|
|
|
0
|
foreach my $link ( @links_to ) { |
|
1137
|
0
|
|
|
|
|
0
|
eval { $sth->execute( $name, $link ); }; |
|
|
0
|
|
|
|
|
0
|
|
|
1138
|
0
|
0
|
|
|
|
0
|
carp "Couldn't index backlink: " . $dbh->errstr if $@; |
|
1139
|
|
|
|
|
|
|
} |
|
1140
|
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
# Delete the metadata for the old version |
|
1142
|
0
|
|
|
|
|
0
|
$sql = "DELETE FROM metadata |
|
1143
|
|
|
|
|
|
|
WHERE node_id = $node_id |
|
1144
|
|
|
|
|
|
|
AND version = $version"; |
|
1145
|
0
|
|
|
|
|
0
|
$sth = $dbh->prepare( $sql ); |
|
1146
|
0
|
0
|
|
|
|
0
|
$sth->execute() |
|
1147
|
|
|
|
|
|
|
or croak "Deletion failed: " . $dbh->errstr; |
|
1148
|
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
# All done |
|
1150
|
0
|
|
|
|
|
0
|
post_delete_node($name,$node_id,$version,$args{plugins}); |
|
1151
|
0
|
|
|
|
|
0
|
return 1; |
|
1152
|
|
|
|
|
|
|
} |
|
1153
|
|
|
|
|
|
|
|
|
1154
|
|
|
|
|
|
|
# If we're still here, then we're deleting neither the latest |
|
1155
|
|
|
|
|
|
|
# nor the only version. |
|
1156
|
0
|
|
|
|
|
0
|
$sql = "DELETE FROM content |
|
1157
|
|
|
|
|
|
|
WHERE node_id = $node_id |
|
1158
|
|
|
|
|
|
|
AND version=?"; |
|
1159
|
0
|
|
|
|
|
0
|
$sth = $dbh->prepare( $sql ); |
|
1160
|
0
|
0
|
|
|
|
0
|
$sth->execute( $version ) |
|
1161
|
|
|
|
|
|
|
or croak "Deletion failed: " . $dbh->errstr; |
|
1162
|
0
|
|
|
|
|
0
|
$sql = "DELETE FROM metadata |
|
1163
|
|
|
|
|
|
|
WHERE node_id = $node_id |
|
1164
|
|
|
|
|
|
|
AND version=?"; |
|
1165
|
0
|
|
|
|
|
0
|
$sth = $dbh->prepare( $sql ); |
|
1166
|
0
|
0
|
|
|
|
0
|
$sth->execute( $version ) |
|
1167
|
|
|
|
|
|
|
or croak "Deletion failed: " . $dbh->errstr; |
|
1168
|
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
# All done |
|
1170
|
0
|
|
|
|
|
0
|
post_delete_node($name,$node_id,$version,$args{plugins}); |
|
1171
|
0
|
|
|
|
|
0
|
return 1; |
|
1172
|
|
|
|
|
|
|
} |
|
1173
|
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
# Returns the name of the node with the given ID |
|
1175
|
|
|
|
|
|
|
# Not normally used except when doing low-level maintenance |
|
1176
|
|
|
|
|
|
|
sub node_name_for_id { |
|
1177
|
0
|
|
|
0
|
0
|
0
|
my ($self, $node_id) = @_; |
|
1178
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
1179
|
|
|
|
|
|
|
|
|
1180
|
0
|
|
|
|
|
0
|
my $name_sql = "SELECT name FROM node WHERE id=?"; |
|
1181
|
0
|
|
|
|
|
0
|
my $name_sth = $dbh->prepare($name_sql); |
|
1182
|
0
|
|
|
|
|
0
|
$name_sth->execute($node_id); |
|
1183
|
0
|
|
|
|
|
0
|
my ($name) = $name_sth->fetchrow_array; |
|
1184
|
0
|
|
|
|
|
0
|
$name_sth->finish; |
|
1185
|
|
|
|
|
|
|
|
|
1186
|
0
|
|
|
|
|
0
|
return $name; |
|
1187
|
|
|
|
|
|
|
} |
|
1188
|
|
|
|
|
|
|
|
|
1189
|
|
|
|
|
|
|
# Internal Method |
|
1190
|
|
|
|
|
|
|
sub post_delete_node { |
|
1191
|
0
|
|
|
0
|
0
|
0
|
my ($name,$node_id,$version,$plugins) = @_; |
|
1192
|
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
# Call post_delete on any plugins, having done the delete |
|
1194
|
0
|
0
|
|
|
|
0
|
my @plugins = @{ $plugins || [ ] }; |
|
|
0
|
|
|
|
|
0
|
|
|
1195
|
0
|
|
|
|
|
0
|
foreach my $plugin (@plugins) { |
|
1196
|
0
|
0
|
|
|
|
0
|
if ( $plugin->can( "post_delete" ) ) { |
|
1197
|
0
|
|
|
|
|
0
|
$plugin->post_delete( |
|
1198
|
|
|
|
|
|
|
node => $name, |
|
1199
|
|
|
|
|
|
|
node_id => $node_id, |
|
1200
|
|
|
|
|
|
|
version => $version ); |
|
1201
|
|
|
|
|
|
|
} |
|
1202
|
|
|
|
|
|
|
} |
|
1203
|
|
|
|
|
|
|
} |
|
1204
|
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
=item B |
|
1206
|
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
# Nodes changed in last 7 days - each node listed only once. |
|
1208
|
|
|
|
|
|
|
my @nodes = $store->list_recent_changes( days => 7 ); |
|
1209
|
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
# Nodes added in the last 7 days. |
|
1211
|
|
|
|
|
|
|
my @nodes = $store->list_recent_changes( |
|
1212
|
|
|
|
|
|
|
days => 7, |
|
1213
|
|
|
|
|
|
|
new_only => 1, |
|
1214
|
|
|
|
|
|
|
); |
|
1215
|
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
# All changes in last 7 days - nodes changed more than once will |
|
1217
|
|
|
|
|
|
|
# be listed more than once. |
|
1218
|
|
|
|
|
|
|
my @nodes = $store->list_recent_changes( |
|
1219
|
|
|
|
|
|
|
days => 7, |
|
1220
|
|
|
|
|
|
|
include_all_changes => 1, |
|
1221
|
|
|
|
|
|
|
); |
|
1222
|
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
# Nodes changed between 1 and 7 days ago. |
|
1224
|
|
|
|
|
|
|
my @nodes = $store->list_recent_changes( between_days => [ 1, 7 ] ); |
|
1225
|
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
# Nodes changed since a given time. |
|
1227
|
|
|
|
|
|
|
my @nodes = $store->list_recent_changes( since => 1036235131 ); |
|
1228
|
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
# Most recent change and its details. |
|
1230
|
|
|
|
|
|
|
my @nodes = $store->list_recent_changes( last_n_changes => 1 ); |
|
1231
|
|
|
|
|
|
|
print "Node: $nodes[0]{name}"; |
|
1232
|
|
|
|
|
|
|
print "Last modified: $nodes[0]{last_modified}"; |
|
1233
|
|
|
|
|
|
|
print "Comment: $nodes[0]{metadata}{comment}"; |
|
1234
|
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
# Last 5 restaurant nodes edited. |
|
1236
|
|
|
|
|
|
|
my @nodes = $store->list_recent_changes( |
|
1237
|
|
|
|
|
|
|
last_n_changes => 5, |
|
1238
|
|
|
|
|
|
|
metadata_is => { category => "Restaurants" } |
|
1239
|
|
|
|
|
|
|
); |
|
1240
|
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
# Last 5 nodes edited by Kake. |
|
1242
|
|
|
|
|
|
|
my @nodes = $store->list_recent_changes( |
|
1243
|
|
|
|
|
|
|
last_n_changes => 5, |
|
1244
|
|
|
|
|
|
|
metadata_was => { username => "Kake" } |
|
1245
|
|
|
|
|
|
|
); |
|
1246
|
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
# All minor edits made by Earle in the last week. |
|
1248
|
|
|
|
|
|
|
my @nodes = $store->list_recent_changes( |
|
1249
|
|
|
|
|
|
|
days => 7, |
|
1250
|
|
|
|
|
|
|
metadata_was => { username => "Earle", |
|
1251
|
|
|
|
|
|
|
edit_type => "Minor tidying." } |
|
1252
|
|
|
|
|
|
|
); |
|
1253
|
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
# Last 10 changes that weren't minor edits. |
|
1255
|
|
|
|
|
|
|
my @nodes = $store->list_recent_changes( |
|
1256
|
|
|
|
|
|
|
last_n_changes => 10, |
|
1257
|
|
|
|
|
|
|
metadata_wasnt => { edit_type => "Minor tidying" } |
|
1258
|
|
|
|
|
|
|
); |
|
1259
|
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
You I supply one of the following constraints: C |
|
1261
|
|
|
|
|
|
|
(integer), C (epoch), C (integer). |
|
1262
|
|
|
|
|
|
|
|
|
1263
|
|
|
|
|
|
|
You I also supply moderation => 1 if you only want to see versions |
|
1264
|
|
|
|
|
|
|
that are moderated. |
|
1265
|
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
Another optional parameter is C, which if set to 1 will only |
|
1267
|
|
|
|
|
|
|
return newly added nodes. |
|
1268
|
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
You I also supply I C (and optionally |
|
1270
|
|
|
|
|
|
|
C), I C (and optionally |
|
1271
|
|
|
|
|
|
|
C). Each of these should be a ref to a hash with |
|
1272
|
|
|
|
|
|
|
scalar keys and values. If the hash has more than one entry, then |
|
1273
|
|
|
|
|
|
|
only changes satisfying I criteria will be returned when using |
|
1274
|
|
|
|
|
|
|
C or C, but all changes which fail to |
|
1275
|
|
|
|
|
|
|
satisfy any one of the criteria will be returned when using |
|
1276
|
|
|
|
|
|
|
C or C. |
|
1277
|
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
C and C look only at the metadata that the |
|
1279
|
|
|
|
|
|
|
node I has. C and C take into |
|
1280
|
|
|
|
|
|
|
account the metadata of previous versions of a node. Don't mix C |
|
1281
|
|
|
|
|
|
|
with C - there's no check for this, but the results are undefined. |
|
1282
|
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
Returns results as an array, in reverse chronological order. Each |
|
1284
|
|
|
|
|
|
|
element of the array is a reference to a hash with the following entries: |
|
1285
|
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
=over 4 |
|
1287
|
|
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
=item * B: the name of the node |
|
1289
|
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
=item * B: the version number of the node |
|
1291
|
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
=item * B: timestamp showing when this version was written |
|
1293
|
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
=item * B: a ref to a hash containing any metadata attached |
|
1295
|
|
|
|
|
|
|
to this version of the node |
|
1296
|
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
=back |
|
1298
|
|
|
|
|
|
|
|
|
1299
|
|
|
|
|
|
|
Unless you supply C, C or |
|
1300
|
|
|
|
|
|
|
C, each node will only be returned once regardless of |
|
1301
|
|
|
|
|
|
|
how many times it has been changed recently. |
|
1302
|
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
By default, the case-sensitivity of both C and |
|
1304
|
|
|
|
|
|
|
C depends on your database - if it will return rows |
|
1305
|
|
|
|
|
|
|
with an attribute value of "Pubs" when you asked for "pubs", or not. |
|
1306
|
|
|
|
|
|
|
If you supply a true value to the C parameter, then you |
|
1307
|
|
|
|
|
|
|
can be sure of its being case-insensitive. This is recommended. |
|
1308
|
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
=cut |
|
1310
|
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
sub list_recent_changes { |
|
1312
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1313
|
0
|
|
|
|
|
0
|
my %args = @_; |
|
1314
|
0
|
0
|
|
|
|
0
|
if ($args{since}) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1315
|
0
|
|
|
|
|
0
|
return $self->_find_recent_changes_by_criteria( %args ); |
|
1316
|
|
|
|
|
|
|
} elsif ($args{between_days}) { |
|
1317
|
0
|
|
|
|
|
0
|
return $self->_find_recent_changes_by_criteria( %args ); |
|
1318
|
|
|
|
|
|
|
} elsif ( $args{days} ) { |
|
1319
|
0
|
|
|
|
|
0
|
my $now = localtime; |
|
1320
|
0
|
|
|
|
|
0
|
my $then = $now - ( ONE_DAY * $args{days} ); |
|
1321
|
0
|
|
|
|
|
0
|
$args{since} = $then; |
|
1322
|
0
|
|
|
|
|
0
|
delete $args{days}; |
|
1323
|
0
|
|
|
|
|
0
|
return $self->_find_recent_changes_by_criteria( %args ); |
|
1324
|
|
|
|
|
|
|
} elsif ( $args{last_n_changes} ) { |
|
1325
|
0
|
|
|
|
|
0
|
$args{limit} = delete $args{last_n_changes}; |
|
1326
|
0
|
|
|
|
|
0
|
return $self->_find_recent_changes_by_criteria( %args ); |
|
1327
|
|
|
|
|
|
|
} else { |
|
1328
|
0
|
|
|
|
|
0
|
croak "Need to supply some criteria to list_recent_changes."; |
|
1329
|
|
|
|
|
|
|
} |
|
1330
|
|
|
|
|
|
|
} |
|
1331
|
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
sub _find_recent_changes_by_criteria { |
|
1333
|
0
|
|
|
0
|
|
0
|
my ($self, %args) = @_; |
|
1334
|
|
|
|
|
|
|
my ($since, $limit, $between_days, $ignore_case, $new_only, |
|
1335
|
|
|
|
|
|
|
$metadata_is, $metadata_isnt, $metadata_was, $metadata_wasnt, |
|
1336
|
|
|
|
|
|
|
$moderation, $include_all_changes ) = |
|
1337
|
0
|
|
|
|
|
0
|
@args{ qw( since limit between_days ignore_case new_only |
|
1338
|
|
|
|
|
|
|
metadata_is metadata_isnt metadata_was metadata_wasnt |
|
1339
|
|
|
|
|
|
|
moderation include_all_changes) }; |
|
1340
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
1341
|
|
|
|
|
|
|
|
|
1342
|
0
|
|
|
|
|
0
|
my @where; |
|
1343
|
|
|
|
|
|
|
my @metadata_joins; |
|
1344
|
0
|
|
|
|
|
0
|
my $use_content_table; # some queries won't need this |
|
1345
|
|
|
|
|
|
|
|
|
1346
|
0
|
0
|
|
|
|
0
|
if ( $metadata_is ) { |
|
1347
|
0
|
|
|
|
|
0
|
my $main_table = "node"; |
|
1348
|
0
|
0
|
|
|
|
0
|
if ( $include_all_changes ) { |
|
1349
|
0
|
|
|
|
|
0
|
$main_table = "content"; |
|
1350
|
0
|
|
|
|
|
0
|
$use_content_table = 1; |
|
1351
|
|
|
|
|
|
|
} |
|
1352
|
0
|
|
|
|
|
0
|
my $i = 0; |
|
1353
|
0
|
|
|
|
|
0
|
foreach my $type ( keys %$metadata_is ) { |
|
1354
|
0
|
|
|
|
|
0
|
$i++; |
|
1355
|
0
|
|
|
|
|
0
|
my $value = $metadata_is->{$type}; |
|
1356
|
0
|
0
|
|
|
|
0
|
croak "metadata_is must have scalar values" if ref $value; |
|
1357
|
0
|
|
|
|
|
0
|
my $mdt = "md_is_$i"; |
|
1358
|
0
|
0
|
|
|
|
0
|
push @metadata_joins, "LEFT JOIN metadata AS $mdt |
|
1359
|
|
|
|
|
|
|
ON $main_table." |
|
1360
|
|
|
|
|
|
|
. ( ($main_table eq "node") ? "id" |
|
1361
|
|
|
|
|
|
|
: "node_id" ) |
|
1362
|
|
|
|
|
|
|
. "=$mdt.node_id |
|
1363
|
|
|
|
|
|
|
AND $main_table.version=$mdt.version\n"; |
|
1364
|
|
|
|
|
|
|
# Why is this inside 'if ( $metadata_is )'? |
|
1365
|
|
|
|
|
|
|
# Shouldn't it apply to all cases? |
|
1366
|
|
|
|
|
|
|
# What's it doing in @metadata_joins? |
|
1367
|
0
|
0
|
|
|
|
0
|
if (defined $moderation) { |
|
1368
|
0
|
|
|
|
|
0
|
push @metadata_joins, "AND $main_table.moderate=$moderation"; |
|
1369
|
|
|
|
|
|
|
} |
|
1370
|
0
|
|
|
|
|
0
|
push @where, "( " |
|
1371
|
|
|
|
|
|
|
. $self->_get_comparison_sql( |
|
1372
|
|
|
|
|
|
|
thing1 => "$mdt.metadata_type", |
|
1373
|
|
|
|
|
|
|
thing2 => $dbh->quote($type), |
|
1374
|
|
|
|
|
|
|
ignore_case => $ignore_case, |
|
1375
|
|
|
|
|
|
|
) |
|
1376
|
|
|
|
|
|
|
. " AND " |
|
1377
|
|
|
|
|
|
|
. $self->_get_comparison_sql( |
|
1378
|
|
|
|
|
|
|
thing1 => "$mdt.metadata_value", |
|
1379
|
|
|
|
|
|
|
thing2 => $dbh->quote( $self->charset_encode($value) ), |
|
1380
|
|
|
|
|
|
|
Ignore_case => $ignore_case, |
|
1381
|
|
|
|
|
|
|
) |
|
1382
|
|
|
|
|
|
|
. " )"; |
|
1383
|
|
|
|
|
|
|
} |
|
1384
|
|
|
|
|
|
|
} |
|
1385
|
|
|
|
|
|
|
|
|
1386
|
0
|
0
|
|
|
|
0
|
if ( $metadata_isnt ) { |
|
1387
|
0
|
|
|
|
|
0
|
foreach my $type ( keys %$metadata_isnt ) { |
|
1388
|
0
|
|
|
|
|
0
|
my $value = $metadata_isnt->{$type}; |
|
1389
|
0
|
0
|
|
|
|
0
|
croak "metadata_isnt must have scalar values" if ref $value; |
|
1390
|
|
|
|
|
|
|
} |
|
1391
|
0
|
|
|
|
|
0
|
my @omits = $self->_find_recent_changes_by_criteria( |
|
1392
|
|
|
|
|
|
|
since => $since, |
|
1393
|
|
|
|
|
|
|
between_days => $between_days, |
|
1394
|
|
|
|
|
|
|
metadata_is => $metadata_isnt, |
|
1395
|
|
|
|
|
|
|
ignore_case => $ignore_case, |
|
1396
|
|
|
|
|
|
|
); |
|
1397
|
0
|
|
|
|
|
0
|
foreach my $omit ( @omits ) { |
|
1398
|
|
|
|
|
|
|
push @where, "( node.name != " . $dbh->quote($omit->{name}) |
|
1399
|
|
|
|
|
|
|
. " OR node.version != " . $dbh->quote($omit->{version}) |
|
1400
|
0
|
|
|
|
|
0
|
. ")"; |
|
1401
|
|
|
|
|
|
|
} |
|
1402
|
|
|
|
|
|
|
} |
|
1403
|
|
|
|
|
|
|
|
|
1404
|
0
|
0
|
|
|
|
0
|
if ( $metadata_was ) { |
|
1405
|
0
|
|
|
|
|
0
|
$use_content_table = 1; |
|
1406
|
0
|
|
|
|
|
0
|
my $i = 0; |
|
1407
|
0
|
|
|
|
|
0
|
foreach my $type ( keys %$metadata_was ) { |
|
1408
|
0
|
|
|
|
|
0
|
$i++; |
|
1409
|
0
|
|
|
|
|
0
|
my $value = $metadata_was->{$type}; |
|
1410
|
0
|
0
|
|
|
|
0
|
croak "metadata_was must have scalar values" if ref $value; |
|
1411
|
0
|
|
|
|
|
0
|
my $mdt = "md_was_$i"; |
|
1412
|
0
|
|
|
|
|
0
|
push @metadata_joins, "LEFT JOIN metadata AS $mdt |
|
1413
|
|
|
|
|
|
|
ON content.node_id=$mdt.node_id |
|
1414
|
|
|
|
|
|
|
AND content.version=$mdt.version\n"; |
|
1415
|
0
|
|
|
|
|
0
|
push @where, "( " |
|
1416
|
|
|
|
|
|
|
. $self->_get_comparison_sql( |
|
1417
|
|
|
|
|
|
|
thing1 => "$mdt.metadata_type", |
|
1418
|
|
|
|
|
|
|
thing2 => $dbh->quote($type), |
|
1419
|
|
|
|
|
|
|
ignore_case => $ignore_case, |
|
1420
|
|
|
|
|
|
|
) |
|
1421
|
|
|
|
|
|
|
. " AND " |
|
1422
|
|
|
|
|
|
|
. $self->_get_comparison_sql( |
|
1423
|
|
|
|
|
|
|
thing1 => "$mdt.metadata_value", |
|
1424
|
|
|
|
|
|
|
thing2 => $dbh->quote( $self->charset_encode($value) ), |
|
1425
|
|
|
|
|
|
|
ignore_case => $ignore_case, |
|
1426
|
|
|
|
|
|
|
) |
|
1427
|
|
|
|
|
|
|
. " )"; |
|
1428
|
|
|
|
|
|
|
} |
|
1429
|
|
|
|
|
|
|
} |
|
1430
|
|
|
|
|
|
|
|
|
1431
|
0
|
0
|
|
|
|
0
|
if ( $metadata_wasnt ) { |
|
1432
|
0
|
|
|
|
|
0
|
foreach my $type ( keys %$metadata_wasnt ) { |
|
1433
|
0
|
|
|
|
|
0
|
my $value = $metadata_was->{$type}; |
|
1434
|
0
|
0
|
|
|
|
0
|
croak "metadata_was must have scalar values" if ref $value; |
|
1435
|
|
|
|
|
|
|
} |
|
1436
|
0
|
|
|
|
|
0
|
my @omits = $self->_find_recent_changes_by_criteria( |
|
1437
|
|
|
|
|
|
|
since => $since, |
|
1438
|
|
|
|
|
|
|
between_days => $between_days, |
|
1439
|
|
|
|
|
|
|
metadata_was => $metadata_wasnt, |
|
1440
|
|
|
|
|
|
|
ignore_case => $ignore_case, |
|
1441
|
|
|
|
|
|
|
); |
|
1442
|
0
|
|
|
|
|
0
|
foreach my $omit ( @omits ) { |
|
1443
|
|
|
|
|
|
|
push @where, "( node.name != " . $dbh->quote($omit->{name}) |
|
1444
|
|
|
|
|
|
|
. " OR content.version != " . $dbh->quote($omit->{version}) |
|
1445
|
0
|
|
|
|
|
0
|
. ")"; |
|
1446
|
|
|
|
|
|
|
} |
|
1447
|
0
|
|
|
|
|
0
|
$use_content_table = 1; |
|
1448
|
|
|
|
|
|
|
} |
|
1449
|
|
|
|
|
|
|
|
|
1450
|
|
|
|
|
|
|
# Figure out which table we should be joining to to check the dates and |
|
1451
|
|
|
|
|
|
|
# versions - node or content. |
|
1452
|
0
|
|
|
|
|
0
|
my $date_table = "node"; |
|
1453
|
0
|
0
|
0
|
|
|
0
|
if ( $include_all_changes || $new_only |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1454
|
|
|
|
|
|
|
|| $metadata_was || $metadata_wasnt ) { |
|
1455
|
0
|
|
|
|
|
0
|
$date_table = "content"; |
|
1456
|
0
|
|
|
|
|
0
|
$use_content_table = 1; |
|
1457
|
|
|
|
|
|
|
} |
|
1458
|
0
|
0
|
|
|
|
0
|
if ( $new_only ) { |
|
1459
|
0
|
|
|
|
|
0
|
push @where, "content.version=1"; |
|
1460
|
|
|
|
|
|
|
} |
|
1461
|
|
|
|
|
|
|
|
|
1462
|
0
|
0
|
|
|
|
0
|
if ( $since ) { |
|
|
|
0
|
|
|
|
|
|
|
1463
|
0
|
|
|
|
|
0
|
my $timestamp = $self->_get_timestamp( $since ); |
|
1464
|
0
|
|
|
|
|
0
|
push @where, "$date_table.modified >= " . $dbh->quote($timestamp); |
|
1465
|
|
|
|
|
|
|
} elsif ( $between_days ) { |
|
1466
|
0
|
|
|
|
|
0
|
my $now = localtime; |
|
1467
|
|
|
|
|
|
|
# Start is the larger number of days ago. |
|
1468
|
0
|
|
|
|
|
0
|
my ($start, $end) = @$between_days; |
|
1469
|
0
|
0
|
|
|
|
0
|
($start, $end) = ($end, $start) if $start < $end; |
|
1470
|
0
|
|
|
|
|
0
|
my $ts_start = $self->_get_timestamp( $now - (ONE_DAY * $start) ); |
|
1471
|
0
|
|
|
|
|
0
|
my $ts_end = $self->_get_timestamp( $now - (ONE_DAY * $end) ); |
|
1472
|
0
|
|
|
|
|
0
|
push @where, "$date_table.modified >= " . $dbh->quote($ts_start); |
|
1473
|
0
|
|
|
|
|
0
|
push @where, "$date_table.modified <= " . $dbh->quote($ts_end); |
|
1474
|
|
|
|
|
|
|
} |
|
1475
|
|
|
|
|
|
|
|
|
1476
|
0
|
|
|
|
|
0
|
my $sql = "SELECT DISTINCT |
|
1477
|
|
|
|
|
|
|
node.name, |
|
1478
|
|
|
|
|
|
|
"; |
|
1479
|
0
|
0
|
0
|
|
|
0
|
if ( $include_all_changes || $new_only || $use_content_table ) { |
|
|
|
|
0
|
|
|
|
|
|
1480
|
0
|
|
|
|
|
0
|
$sql .= " content.version, content.modified "; |
|
1481
|
|
|
|
|
|
|
} else { |
|
1482
|
0
|
|
|
|
|
0
|
$sql .= " node.version, node.modified "; |
|
1483
|
|
|
|
|
|
|
} |
|
1484
|
0
|
|
|
|
|
0
|
$sql .= " FROM node "; |
|
1485
|
0
|
0
|
|
|
|
0
|
if ( $use_content_table ) { |
|
1486
|
0
|
|
|
|
|
0
|
$sql .= " INNER JOIN content ON (node.id = content.node_id ) "; |
|
1487
|
|
|
|
|
|
|
} |
|
1488
|
|
|
|
|
|
|
|
|
1489
|
0
|
0
|
|
|
|
0
|
$sql .= join("\n", @metadata_joins) |
|
|
|
0
|
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
. ( |
|
1491
|
|
|
|
|
|
|
scalar @where |
|
1492
|
|
|
|
|
|
|
? " WHERE " . join(" AND ",@where) |
|
1493
|
|
|
|
|
|
|
: "" |
|
1494
|
|
|
|
|
|
|
) |
|
1495
|
|
|
|
|
|
|
. " ORDER BY " |
|
1496
|
|
|
|
|
|
|
. ( $use_content_table ? "content" : "node" ) |
|
1497
|
|
|
|
|
|
|
. ".modified DESC"; |
|
1498
|
0
|
0
|
|
|
|
0
|
if ( $limit ) { |
|
1499
|
0
|
0
|
|
|
|
0
|
croak "Bad argument $limit" unless $limit =~ /^\d+$/; |
|
1500
|
0
|
|
|
|
|
0
|
$sql .= " LIMIT $limit"; |
|
1501
|
|
|
|
|
|
|
} |
|
1502
|
0
|
|
|
|
|
0
|
my $nodesref = $dbh->selectall_arrayref($sql); |
|
1503
|
0
|
|
|
|
|
0
|
my @finds = map { { name => $_->[0], |
|
|
0
|
|
|
|
|
0
|
|
|
1504
|
|
|
|
|
|
|
version => $_->[1], |
|
1505
|
|
|
|
|
|
|
last_modified => $_->[2] } |
|
1506
|
|
|
|
|
|
|
} @$nodesref; |
|
1507
|
0
|
|
|
|
|
0
|
foreach my $find ( @finds ) { |
|
1508
|
0
|
|
|
|
|
0
|
my %metadata; |
|
1509
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare( "SELECT metadata_type, metadata_value |
|
1510
|
|
|
|
|
|
|
FROM node |
|
1511
|
|
|
|
|
|
|
INNER JOIN metadata |
|
1512
|
|
|
|
|
|
|
ON (id = node_id) |
|
1513
|
|
|
|
|
|
|
WHERE name=? |
|
1514
|
|
|
|
|
|
|
AND metadata.version=?" ); |
|
1515
|
0
|
|
|
|
|
0
|
$sth->execute( $find->{name}, $find->{version} ); |
|
1516
|
0
|
|
|
|
|
0
|
while ( my ($type, $value) = $self->charset_decode( $sth->fetchrow_array ) ) { |
|
1517
|
0
|
0
|
|
|
|
0
|
if ( defined $metadata{$type} ) { |
|
1518
|
0
|
|
|
|
|
0
|
push @{$metadata{$type}}, $value; |
|
|
0
|
|
|
|
|
0
|
|
|
1519
|
|
|
|
|
|
|
} else { |
|
1520
|
0
|
|
|
|
|
0
|
$metadata{$type} = [ $value ]; |
|
1521
|
|
|
|
|
|
|
} |
|
1522
|
|
|
|
|
|
|
} |
|
1523
|
0
|
|
|
|
|
0
|
$find->{metadata} = \%metadata; |
|
1524
|
|
|
|
|
|
|
} |
|
1525
|
0
|
|
|
|
|
0
|
return @finds; |
|
1526
|
|
|
|
|
|
|
} |
|
1527
|
|
|
|
|
|
|
|
|
1528
|
|
|
|
|
|
|
=item B |
|
1529
|
|
|
|
|
|
|
|
|
1530
|
|
|
|
|
|
|
my @nodes = $store->list_all_nodes(); |
|
1531
|
|
|
|
|
|
|
print "First node is $nodes[0]\n"; |
|
1532
|
|
|
|
|
|
|
|
|
1533
|
|
|
|
|
|
|
my @nodes = $store->list_all_nodes( with_details=> 1 ); |
|
1534
|
|
|
|
|
|
|
print "First node is ".$nodes[0]->{'name'}." at version ".$nodes[0]->{'version'}."\n"; |
|
1535
|
|
|
|
|
|
|
|
|
1536
|
|
|
|
|
|
|
Returns a list containing the name of every existing node. The list |
|
1537
|
|
|
|
|
|
|
won't be in any kind of order; do any sorting in your calling script. |
|
1538
|
|
|
|
|
|
|
|
|
1539
|
|
|
|
|
|
|
Optionally also returns the id, version and moderation flag. |
|
1540
|
|
|
|
|
|
|
|
|
1541
|
|
|
|
|
|
|
=cut |
|
1542
|
|
|
|
|
|
|
|
|
1543
|
|
|
|
|
|
|
sub list_all_nodes { |
|
1544
|
0
|
|
|
0
|
1
|
0
|
my ($self,%args) = @_; |
|
1545
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
1546
|
0
|
|
|
|
|
0
|
my @nodes; |
|
1547
|
|
|
|
|
|
|
|
|
1548
|
0
|
0
|
|
|
|
0
|
if($args{with_details}) { |
|
1549
|
0
|
|
|
|
|
0
|
my $sql = "SELECT id, name, version, moderate FROM node;"; |
|
1550
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare( $sql ); |
|
1551
|
0
|
|
|
|
|
0
|
$sth->execute(); |
|
1552
|
|
|
|
|
|
|
|
|
1553
|
0
|
|
|
|
|
0
|
while(my @results = $sth->fetchrow_array) { |
|
1554
|
0
|
|
|
|
|
0
|
my %data; |
|
1555
|
0
|
|
|
|
|
0
|
@data{ qw( node_id name version moderate ) } = @results; |
|
1556
|
0
|
|
|
|
|
0
|
push @nodes, \%data; |
|
1557
|
|
|
|
|
|
|
} |
|
1558
|
|
|
|
|
|
|
} else { |
|
1559
|
0
|
|
|
|
|
0
|
my $sql = "SELECT name FROM node;"; |
|
1560
|
0
|
|
|
|
|
0
|
my $raw_nodes = $dbh->selectall_arrayref($sql); |
|
1561
|
0
|
|
|
|
|
0
|
@nodes = ( map { $self->charset_decode( $_->[0] ) } (@$raw_nodes) ); |
|
|
0
|
|
|
|
|
0
|
|
|
1562
|
|
|
|
|
|
|
} |
|
1563
|
0
|
|
|
|
|
0
|
return @nodes; |
|
1564
|
|
|
|
|
|
|
} |
|
1565
|
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
=item B |
|
1567
|
|
|
|
|
|
|
|
|
1568
|
|
|
|
|
|
|
my @all_versions = $store->list_node_all_versions( |
|
1569
|
|
|
|
|
|
|
name => 'HomePage', |
|
1570
|
|
|
|
|
|
|
with_content => 1, |
|
1571
|
|
|
|
|
|
|
with_metadata => 0 |
|
1572
|
|
|
|
|
|
|
); |
|
1573
|
|
|
|
|
|
|
|
|
1574
|
|
|
|
|
|
|
Returns all the versions of a node, optionally including the content |
|
1575
|
|
|
|
|
|
|
and metadata, as an array of hashes (newest versions first). |
|
1576
|
|
|
|
|
|
|
|
|
1577
|
|
|
|
|
|
|
=cut |
|
1578
|
|
|
|
|
|
|
|
|
1579
|
|
|
|
|
|
|
sub list_node_all_versions { |
|
1580
|
0
|
|
|
0
|
1
|
0
|
my ($self, %args) = @_; |
|
1581
|
|
|
|
|
|
|
|
|
1582
|
|
|
|
|
|
|
my ($node_id,$name,$with_content,$with_metadata) = |
|
1583
|
0
|
|
|
|
|
0
|
@args{ qw( node_id name with_content with_metadata ) }; |
|
1584
|
|
|
|
|
|
|
|
|
1585
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
1586
|
0
|
|
|
|
|
0
|
my $sql; |
|
1587
|
|
|
|
|
|
|
|
|
1588
|
|
|
|
|
|
|
# If they only gave us the node name, get the node id |
|
1589
|
0
|
0
|
|
|
|
0
|
unless ($node_id) { |
|
1590
|
0
|
|
|
|
|
0
|
$sql = "SELECT id FROM node WHERE name=" . $dbh->quote($name); |
|
1591
|
0
|
|
|
|
|
0
|
$node_id = $dbh->selectrow_array($sql); |
|
1592
|
|
|
|
|
|
|
} |
|
1593
|
|
|
|
|
|
|
|
|
1594
|
|
|
|
|
|
|
# If they didn't tell us what they wanted / we couldn't find it, |
|
1595
|
|
|
|
|
|
|
# return an empty array |
|
1596
|
0
|
0
|
|
|
|
0
|
return () unless($node_id); |
|
1597
|
|
|
|
|
|
|
|
|
1598
|
|
|
|
|
|
|
# Build up our SQL |
|
1599
|
0
|
|
|
|
|
0
|
$sql = "SELECT id, name, content.version, content.modified "; |
|
1600
|
0
|
0
|
|
|
|
0
|
if ( $with_content ) { |
|
1601
|
0
|
|
|
|
|
0
|
$sql .= ", content.text "; |
|
1602
|
|
|
|
|
|
|
} |
|
1603
|
0
|
0
|
|
|
|
0
|
if ( $with_metadata ) { |
|
1604
|
0
|
|
|
|
|
0
|
$sql .= ", metadata_type, metadata_value "; |
|
1605
|
|
|
|
|
|
|
} |
|
1606
|
0
|
|
|
|
|
0
|
$sql .= " FROM node INNER JOIN content ON (id = content.node_id) "; |
|
1607
|
0
|
0
|
|
|
|
0
|
if ( $with_metadata ) { |
|
1608
|
0
|
|
|
|
|
0
|
$sql .= " LEFT OUTER JOIN metadata ON " |
|
1609
|
|
|
|
|
|
|
. "(id = metadata.node_id AND content.version = metadata.version) "; |
|
1610
|
|
|
|
|
|
|
} |
|
1611
|
0
|
|
|
|
|
0
|
$sql .= " WHERE id = ? ORDER BY content.version DESC"; |
|
1612
|
|
|
|
|
|
|
|
|
1613
|
|
|
|
|
|
|
# Do the fetch |
|
1614
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare( $sql ); |
|
1615
|
0
|
|
|
|
|
0
|
$sth->execute( $node_id ); |
|
1616
|
|
|
|
|
|
|
|
|
1617
|
|
|
|
|
|
|
# Need to hold onto the last row by hash ref, so we don't trash |
|
1618
|
|
|
|
|
|
|
# it every time |
|
1619
|
0
|
|
|
|
|
0
|
my %first_data; |
|
1620
|
0
|
|
|
|
|
0
|
my $dataref = \%first_data; |
|
1621
|
|
|
|
|
|
|
|
|
1622
|
|
|
|
|
|
|
# Haul out the data |
|
1623
|
0
|
|
|
|
|
0
|
my @versions; |
|
1624
|
0
|
|
|
|
|
0
|
while ( my @results = $sth->fetchrow_array ) { |
|
1625
|
0
|
|
|
|
|
0
|
my %data = %$dataref; |
|
1626
|
|
|
|
|
|
|
|
|
1627
|
|
|
|
|
|
|
# Is it the same version as last time? |
|
1628
|
0
|
0
|
0
|
|
|
0
|
if ( %data && $data{'version'} != $results[2] ) { |
|
1629
|
|
|
|
|
|
|
# New version |
|
1630
|
0
|
|
|
|
|
0
|
push @versions, $dataref; |
|
1631
|
0
|
|
|
|
|
0
|
%data = (); |
|
1632
|
|
|
|
|
|
|
} else { |
|
1633
|
|
|
|
|
|
|
# Same version as last time, must be more metadata |
|
1634
|
|
|
|
|
|
|
} |
|
1635
|
|
|
|
|
|
|
|
|
1636
|
|
|
|
|
|
|
# Grab the core data (will be the same on multi-row for metadata) |
|
1637
|
0
|
|
|
|
|
0
|
@data{ qw( node_id name version last_modified ) } = @results; |
|
1638
|
|
|
|
|
|
|
|
|
1639
|
0
|
|
|
|
|
0
|
my $i = 4; |
|
1640
|
0
|
0
|
|
|
|
0
|
if ( $with_content ) { |
|
1641
|
0
|
|
|
|
|
0
|
$data{'content'} = $results[$i]; |
|
1642
|
0
|
|
|
|
|
0
|
$i++; |
|
1643
|
|
|
|
|
|
|
} |
|
1644
|
0
|
0
|
|
|
|
0
|
if ( $with_metadata ) { |
|
1645
|
0
|
|
|
|
|
0
|
my ($m_type,$m_value) = @results[$i,($i+1)]; |
|
1646
|
0
|
0
|
|
|
|
0
|
unless ( $data{'metadata'} ) { $data{'metadata'} = {}; } |
|
|
0
|
|
|
|
|
0
|
|
|
1647
|
|
|
|
|
|
|
|
|
1648
|
0
|
0
|
|
|
|
0
|
if ( $m_type ) { |
|
1649
|
|
|
|
|
|
|
# If we have existing data, then put it into an array |
|
1650
|
0
|
0
|
|
|
|
0
|
if ( $data{'metadata'}->{$m_type} ) { |
|
1651
|
0
|
0
|
|
|
|
0
|
unless ( ref($data{'metadata'}->{$m_type}) eq "ARRAY" ) { |
|
1652
|
|
|
|
|
|
|
$data{'metadata'}->{$m_type} = |
|
1653
|
0
|
|
|
|
|
0
|
[ $data{'metadata'}->{$m_type} ]; |
|
1654
|
|
|
|
|
|
|
} |
|
1655
|
0
|
|
|
|
|
0
|
push @{$data{'metadata'}->{$m_type}}, $m_value; |
|
|
0
|
|
|
|
|
0
|
|
|
1656
|
|
|
|
|
|
|
} else { |
|
1657
|
|
|
|
|
|
|
# Otherwise, just store it in a normal string |
|
1658
|
0
|
|
|
|
|
0
|
$data{'metadata'}->{$m_type} = $m_value; |
|
1659
|
|
|
|
|
|
|
} |
|
1660
|
|
|
|
|
|
|
} |
|
1661
|
|
|
|
|
|
|
} |
|
1662
|
|
|
|
|
|
|
|
|
1663
|
|
|
|
|
|
|
# Save where we've got to |
|
1664
|
0
|
|
|
|
|
0
|
$dataref = \%data; |
|
1665
|
|
|
|
|
|
|
} |
|
1666
|
|
|
|
|
|
|
|
|
1667
|
|
|
|
|
|
|
# Handle final row saving |
|
1668
|
0
|
0
|
|
|
|
0
|
if ( $dataref ) { |
|
1669
|
0
|
|
|
|
|
0
|
push @versions, $dataref; |
|
1670
|
|
|
|
|
|
|
} |
|
1671
|
|
|
|
|
|
|
|
|
1672
|
|
|
|
|
|
|
# Return |
|
1673
|
0
|
|
|
|
|
0
|
return @versions; |
|
1674
|
|
|
|
|
|
|
} |
|
1675
|
|
|
|
|
|
|
|
|
1676
|
|
|
|
|
|
|
=item B |
|
1677
|
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
# All documentation nodes. |
|
1679
|
|
|
|
|
|
|
my @nodes = $store->list_nodes_by_metadata( |
|
1680
|
|
|
|
|
|
|
metadata_type => "category", |
|
1681
|
|
|
|
|
|
|
metadata_value => "documentation", |
|
1682
|
|
|
|
|
|
|
ignore_case => 1, # optional but recommended (see below) |
|
1683
|
|
|
|
|
|
|
); |
|
1684
|
|
|
|
|
|
|
|
|
1685
|
|
|
|
|
|
|
# All pubs in Hammersmith. |
|
1686
|
|
|
|
|
|
|
my @pubs = $store->list_nodes_by_metadata( |
|
1687
|
|
|
|
|
|
|
metadata_type => "category", |
|
1688
|
|
|
|
|
|
|
metadata_value => "Pub", |
|
1689
|
|
|
|
|
|
|
); |
|
1690
|
|
|
|
|
|
|
my @hsm = $store->list_nodes_by_metadata( |
|
1691
|
|
|
|
|
|
|
metadata_type => "category", |
|
1692
|
|
|
|
|
|
|
metadata_value => "Hammersmith", |
|
1693
|
|
|
|
|
|
|
); |
|
1694
|
|
|
|
|
|
|
my @results = my_l33t_method_for_ANDing_arrays( \@pubs, \@hsm ); |
|
1695
|
|
|
|
|
|
|
|
|
1696
|
|
|
|
|
|
|
Returns a list containing the name of every node whose caller-supplied |
|
1697
|
|
|
|
|
|
|
metadata matches the criteria given in the parameters. |
|
1698
|
|
|
|
|
|
|
|
|
1699
|
|
|
|
|
|
|
By default, the case-sensitivity of both C and |
|
1700
|
|
|
|
|
|
|
C depends on your database - if it will return rows |
|
1701
|
|
|
|
|
|
|
with an attribute value of "Pubs" when you asked for "pubs", or not. |
|
1702
|
|
|
|
|
|
|
If you supply a true value to the C parameter, then you |
|
1703
|
|
|
|
|
|
|
can be sure of its being case-insensitive. This is recommended. |
|
1704
|
|
|
|
|
|
|
|
|
1705
|
|
|
|
|
|
|
If you don't supply any criteria then you'll get an empty list. |
|
1706
|
|
|
|
|
|
|
|
|
1707
|
|
|
|
|
|
|
This is a really really really simple way of finding things; if you |
|
1708
|
|
|
|
|
|
|
want to be more complicated then you'll need to call the method |
|
1709
|
|
|
|
|
|
|
multiple times and combine the results yourself, or write a plugin. |
|
1710
|
|
|
|
|
|
|
|
|
1711
|
|
|
|
|
|
|
=cut |
|
1712
|
|
|
|
|
|
|
|
|
1713
|
|
|
|
|
|
|
sub list_nodes_by_metadata { |
|
1714
|
0
|
|
|
0
|
1
|
0
|
my ($self, %args) = @_; |
|
1715
|
0
|
|
|
|
|
0
|
my ( $type, $value ) = @args{ qw( metadata_type metadata_value ) }; |
|
1716
|
0
|
0
|
|
|
|
0
|
return () unless $type; |
|
1717
|
|
|
|
|
|
|
|
|
1718
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
1719
|
0
|
0
|
|
|
|
0
|
if ( $args{ignore_case} ) { |
|
1720
|
0
|
|
|
|
|
0
|
$type = lc( $type ); |
|
1721
|
0
|
|
|
|
|
0
|
$value = lc( $value ); |
|
1722
|
|
|
|
|
|
|
} |
|
1723
|
|
|
|
|
|
|
my $sql = |
|
1724
|
0
|
|
|
|
|
0
|
$self->_get_list_by_metadata_sql( ignore_case => $args{ignore_case} ); |
|
1725
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare( $sql ); |
|
1726
|
0
|
|
|
|
|
0
|
$sth->execute( $type, $self->charset_encode($value) ); |
|
1727
|
0
|
|
|
|
|
0
|
my @nodes; |
|
1728
|
0
|
|
|
|
|
0
|
while ( my ($id, $node) = $sth->fetchrow_array ) { |
|
1729
|
0
|
|
|
|
|
0
|
push @nodes, $node; |
|
1730
|
|
|
|
|
|
|
} |
|
1731
|
0
|
|
|
|
|
0
|
return @nodes; |
|
1732
|
|
|
|
|
|
|
} |
|
1733
|
|
|
|
|
|
|
|
|
1734
|
|
|
|
|
|
|
=item B |
|
1735
|
|
|
|
|
|
|
Returns nodes where either the metadata doesn't exist, or is blank |
|
1736
|
|
|
|
|
|
|
|
|
1737
|
|
|
|
|
|
|
Unlike list_nodes_by_metadata(), the metadata value is optional. |
|
1738
|
|
|
|
|
|
|
|
|
1739
|
|
|
|
|
|
|
# All nodes missing documentation |
|
1740
|
|
|
|
|
|
|
my @nodes = $store->list_nodes_by_missing_metadata( |
|
1741
|
|
|
|
|
|
|
metadata_type => "category", |
|
1742
|
|
|
|
|
|
|
metadata_value => "documentation", |
|
1743
|
|
|
|
|
|
|
ignore_case => 1, # optional but recommended (see below) |
|
1744
|
|
|
|
|
|
|
); |
|
1745
|
|
|
|
|
|
|
|
|
1746
|
|
|
|
|
|
|
# All nodes which don't have a latitude defined |
|
1747
|
|
|
|
|
|
|
my @nodes = $store->list_nodes_by_missing_metadata( |
|
1748
|
|
|
|
|
|
|
metadata_type => "latitude" |
|
1749
|
|
|
|
|
|
|
); |
|
1750
|
|
|
|
|
|
|
|
|
1751
|
|
|
|
|
|
|
=cut |
|
1752
|
|
|
|
|
|
|
|
|
1753
|
|
|
|
|
|
|
sub list_nodes_by_missing_metadata { |
|
1754
|
0
|
|
|
0
|
1
|
0
|
my ($self, %args) = @_; |
|
1755
|
0
|
|
|
|
|
0
|
my ( $type, $value ) = @args{ qw( metadata_type metadata_value ) }; |
|
1756
|
0
|
0
|
|
|
|
0
|
return () unless $type; |
|
1757
|
|
|
|
|
|
|
|
|
1758
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
1759
|
0
|
0
|
|
|
|
0
|
if ( $args{ignore_case} ) { |
|
1760
|
0
|
|
|
|
|
0
|
$type = lc( $type ); |
|
1761
|
0
|
|
|
|
|
0
|
$value = lc( $value ); |
|
1762
|
|
|
|
|
|
|
} |
|
1763
|
|
|
|
|
|
|
|
|
1764
|
0
|
|
|
|
|
0
|
my @nodes; |
|
1765
|
|
|
|
|
|
|
|
|
1766
|
|
|
|
|
|
|
# If the don't want to match by value, then we can do it with |
|
1767
|
|
|
|
|
|
|
# a LEFT OUTER JOIN, and either NULL or LENGTH() = 0 |
|
1768
|
0
|
0
|
|
|
|
0
|
if( ! $value ) { |
|
1769
|
|
|
|
|
|
|
my $sql = $self->_get_list_by_missing_metadata_sql( |
|
1770
|
|
|
|
|
|
|
ignore_case => $args{ignore_case} |
|
1771
|
0
|
|
|
|
|
0
|
); |
|
1772
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare( $sql ); |
|
1773
|
0
|
|
|
|
|
0
|
$sth->execute( $type ); |
|
1774
|
|
|
|
|
|
|
|
|
1775
|
0
|
|
|
|
|
0
|
while ( my ($id, $node) = $sth->fetchrow_array ) { |
|
1776
|
0
|
|
|
|
|
0
|
push @nodes, $node; |
|
1777
|
|
|
|
|
|
|
} |
|
1778
|
|
|
|
|
|
|
} else { |
|
1779
|
|
|
|
|
|
|
# To find those without the value in this case would involve |
|
1780
|
|
|
|
|
|
|
# some seriously brain hurting SQL. |
|
1781
|
|
|
|
|
|
|
# So, cheat - find those with, and return everything else |
|
1782
|
0
|
|
|
|
|
0
|
my @with = $self->list_nodes_by_metadata(%args); |
|
1783
|
0
|
|
|
|
|
0
|
my %with_hash; |
|
1784
|
0
|
|
|
|
|
0
|
foreach my $node (@with) { $with_hash{$node} = 1; } |
|
|
0
|
|
|
|
|
0
|
|
|
1785
|
|
|
|
|
|
|
|
|
1786
|
0
|
|
|
|
|
0
|
my @all_nodes = $self->list_all_nodes(); |
|
1787
|
0
|
|
|
|
|
0
|
foreach my $node (@all_nodes) { |
|
1788
|
0
|
0
|
|
|
|
0
|
unless($with_hash{$node}) { |
|
1789
|
0
|
|
|
|
|
0
|
push @nodes, $node; |
|
1790
|
|
|
|
|
|
|
} |
|
1791
|
|
|
|
|
|
|
} |
|
1792
|
|
|
|
|
|
|
} |
|
1793
|
|
|
|
|
|
|
|
|
1794
|
0
|
|
|
|
|
0
|
return @nodes; |
|
1795
|
|
|
|
|
|
|
} |
|
1796
|
|
|
|
|
|
|
|
|
1797
|
|
|
|
|
|
|
=item B<_get_list_by_metadata_sql> |
|
1798
|
|
|
|
|
|
|
|
|
1799
|
|
|
|
|
|
|
Return the SQL to do a match by metadata. Should expect the metadata type |
|
1800
|
|
|
|
|
|
|
as the first SQL parameter, and the metadata value as the second. |
|
1801
|
|
|
|
|
|
|
|
|
1802
|
|
|
|
|
|
|
If possible, should take account of $args{ignore_case} |
|
1803
|
|
|
|
|
|
|
|
|
1804
|
|
|
|
|
|
|
=cut |
|
1805
|
|
|
|
|
|
|
|
|
1806
|
|
|
|
|
|
|
sub _get_list_by_metadata_sql { |
|
1807
|
|
|
|
|
|
|
# SQL 99 version |
|
1808
|
|
|
|
|
|
|
# Can be over-ridden by database-specific subclasses |
|
1809
|
0
|
|
|
0
|
|
0
|
my ($self, %args) = @_; |
|
1810
|
0
|
0
|
|
|
|
0
|
if ( $args{ignore_case} ) { |
|
1811
|
0
|
|
|
|
|
0
|
return "SELECT node.id, node.name " |
|
1812
|
|
|
|
|
|
|
. "FROM node " |
|
1813
|
|
|
|
|
|
|
. "INNER JOIN metadata " |
|
1814
|
|
|
|
|
|
|
. " ON (node.id = metadata.node_id " |
|
1815
|
|
|
|
|
|
|
. " AND node.version=metadata.version) " |
|
1816
|
|
|
|
|
|
|
. "WHERE ". $self->_get_lowercase_compare_sql("metadata.metadata_type") |
|
1817
|
|
|
|
|
|
|
. " AND ". $self->_get_lowercase_compare_sql("metadata.metadata_value"); |
|
1818
|
|
|
|
|
|
|
} else { |
|
1819
|
0
|
|
|
|
|
0
|
return "SELECT node.id, node.name " |
|
1820
|
|
|
|
|
|
|
. "FROM node " |
|
1821
|
|
|
|
|
|
|
. "INNER JOIN metadata " |
|
1822
|
|
|
|
|
|
|
. " ON (node.id = metadata.node_id " |
|
1823
|
|
|
|
|
|
|
. " AND node.version=metadata.version) " |
|
1824
|
|
|
|
|
|
|
. "WHERE ". $self->_get_casesensitive_compare_sql("metadata.metadata_type") |
|
1825
|
|
|
|
|
|
|
. " AND ". $self->_get_casesensitive_compare_sql("metadata.metadata_value"); |
|
1826
|
|
|
|
|
|
|
} |
|
1827
|
|
|
|
|
|
|
} |
|
1828
|
|
|
|
|
|
|
|
|
1829
|
|
|
|
|
|
|
=item B<_get_list_by_missing_metadata_sql> |
|
1830
|
|
|
|
|
|
|
|
|
1831
|
|
|
|
|
|
|
Return the SQL to do a match by missing metadata. Should expect the metadata |
|
1832
|
|
|
|
|
|
|
type as the first SQL parameter. |
|
1833
|
|
|
|
|
|
|
|
|
1834
|
|
|
|
|
|
|
If possible, should take account of $args{ignore_case} |
|
1835
|
|
|
|
|
|
|
|
|
1836
|
|
|
|
|
|
|
=cut |
|
1837
|
|
|
|
|
|
|
|
|
1838
|
|
|
|
|
|
|
sub _get_list_by_missing_metadata_sql { |
|
1839
|
|
|
|
|
|
|
# SQL 99 version |
|
1840
|
|
|
|
|
|
|
# Can be over-ridden by database-specific subclasses |
|
1841
|
0
|
|
|
0
|
|
0
|
my ($self, %args) = @_; |
|
1842
|
|
|
|
|
|
|
|
|
1843
|
0
|
|
|
|
|
0
|
my $sql = ""; |
|
1844
|
0
|
0
|
|
|
|
0
|
if ( $args{ignore_case} ) { |
|
1845
|
0
|
|
|
|
|
0
|
$sql = "SELECT node.id, node.name " |
|
1846
|
|
|
|
|
|
|
. "FROM node " |
|
1847
|
|
|
|
|
|
|
. "LEFT OUTER JOIN metadata " |
|
1848
|
|
|
|
|
|
|
. " ON (node.id = metadata.node_id " |
|
1849
|
|
|
|
|
|
|
. " AND node.version=metadata.version " |
|
1850
|
|
|
|
|
|
|
. " AND ". $self->_get_lowercase_compare_sql("metadata.metadata_type") |
|
1851
|
|
|
|
|
|
|
. ")"; |
|
1852
|
|
|
|
|
|
|
} else { |
|
1853
|
0
|
|
|
|
|
0
|
$sql = "SELECT node.id, node.name " |
|
1854
|
|
|
|
|
|
|
. "FROM node " |
|
1855
|
|
|
|
|
|
|
. "LEFT OUTER JOIN metadata " |
|
1856
|
|
|
|
|
|
|
. " ON (node.id = metadata.node_id " |
|
1857
|
|
|
|
|
|
|
. " AND node.version=metadata.version " |
|
1858
|
|
|
|
|
|
|
. " AND ". $self->_get_casesensitive_compare_sql("metadata.metadata_type") |
|
1859
|
|
|
|
|
|
|
. ")"; |
|
1860
|
|
|
|
|
|
|
} |
|
1861
|
|
|
|
|
|
|
|
|
1862
|
0
|
|
|
|
|
0
|
$sql .= "WHERE (metadata.metadata_value IS NULL OR LENGTH(metadata.metadata_value) = 0) "; |
|
1863
|
0
|
|
|
|
|
0
|
return $sql; |
|
1864
|
|
|
|
|
|
|
} |
|
1865
|
|
|
|
|
|
|
|
|
1866
|
|
|
|
|
|
|
sub _get_lowercase_compare_sql { |
|
1867
|
0
|
|
|
0
|
|
0
|
my ($self, $column) = @_; |
|
1868
|
|
|
|
|
|
|
# SQL 99 version |
|
1869
|
|
|
|
|
|
|
# Can be over-ridden by database-specific subclasses |
|
1870
|
0
|
|
|
|
|
0
|
return "lower($column) = ?"; |
|
1871
|
|
|
|
|
|
|
} |
|
1872
|
|
|
|
|
|
|
sub _get_casesensitive_compare_sql { |
|
1873
|
0
|
|
|
0
|
|
0
|
my ($self, $column) = @_; |
|
1874
|
|
|
|
|
|
|
# SQL 99 version |
|
1875
|
|
|
|
|
|
|
# Can be over-ridden by database-specific subclasses |
|
1876
|
0
|
|
|
|
|
0
|
return "$column = ?"; |
|
1877
|
|
|
|
|
|
|
} |
|
1878
|
|
|
|
|
|
|
|
|
1879
|
|
|
|
|
|
|
sub _get_comparison_sql { |
|
1880
|
0
|
|
|
0
|
|
0
|
my ($self, %args) = @_; |
|
1881
|
|
|
|
|
|
|
# SQL 99 version |
|
1882
|
|
|
|
|
|
|
# Can be over-ridden by database-specific subclasses |
|
1883
|
0
|
|
|
|
|
0
|
return "$args{thing1} = $args{thing2}"; |
|
1884
|
|
|
|
|
|
|
} |
|
1885
|
|
|
|
|
|
|
|
|
1886
|
|
|
|
|
|
|
sub _get_node_exists_ignore_case_sql { |
|
1887
|
|
|
|
|
|
|
# SQL 99 version |
|
1888
|
|
|
|
|
|
|
# Can be over-ridden by database-specific subclasses |
|
1889
|
0
|
|
|
0
|
|
0
|
return "SELECT name FROM node WHERE name = ? "; |
|
1890
|
|
|
|
|
|
|
} |
|
1891
|
|
|
|
|
|
|
|
|
1892
|
|
|
|
|
|
|
=item B |
|
1893
|
|
|
|
|
|
|
|
|
1894
|
|
|
|
|
|
|
my @nodes = $wiki->list_unmoderated_nodes(); |
|
1895
|
|
|
|
|
|
|
my @nodes = $wiki->list_unmoderated_nodes( |
|
1896
|
|
|
|
|
|
|
only_where_latest => 1 |
|
1897
|
|
|
|
|
|
|
); |
|
1898
|
|
|
|
|
|
|
|
|
1899
|
|
|
|
|
|
|
$nodes[0]->{'name'} # The name of the node |
|
1900
|
|
|
|
|
|
|
$nodes[0]->{'node_id'} # The id of the node |
|
1901
|
|
|
|
|
|
|
$nodes[0]->{'version'} # The version in need of moderation |
|
1902
|
|
|
|
|
|
|
$nodes[0]->{'moderated_version'} # The newest moderated version |
|
1903
|
|
|
|
|
|
|
|
|
1904
|
|
|
|
|
|
|
With only_where_latest set, return the id, name and version of all the |
|
1905
|
|
|
|
|
|
|
nodes where the most recent version needs moderation. |
|
1906
|
|
|
|
|
|
|
Otherwise, returns the id, name and version of all node versions that need |
|
1907
|
|
|
|
|
|
|
to be moderated. |
|
1908
|
|
|
|
|
|
|
|
|
1909
|
|
|
|
|
|
|
=cut |
|
1910
|
|
|
|
|
|
|
|
|
1911
|
|
|
|
|
|
|
sub list_unmoderated_nodes { |
|
1912
|
0
|
|
|
0
|
1
|
0
|
my ($self,%args) = @_; |
|
1913
|
|
|
|
|
|
|
|
|
1914
|
0
|
|
|
|
|
0
|
my $only_where_lastest = $args{'only_where_latest'}; |
|
1915
|
|
|
|
|
|
|
|
|
1916
|
0
|
|
|
|
|
0
|
my $sql = |
|
1917
|
|
|
|
|
|
|
"SELECT " |
|
1918
|
|
|
|
|
|
|
." id, name, " |
|
1919
|
|
|
|
|
|
|
." node.version AS last_moderated_version, " |
|
1920
|
|
|
|
|
|
|
." content.version AS version " |
|
1921
|
|
|
|
|
|
|
."FROM content " |
|
1922
|
|
|
|
|
|
|
."INNER JOIN node " |
|
1923
|
|
|
|
|
|
|
." ON (id = node_id) " |
|
1924
|
|
|
|
|
|
|
."WHERE moderated = ? " |
|
1925
|
|
|
|
|
|
|
; |
|
1926
|
0
|
0
|
|
|
|
0
|
if($only_where_lastest) { |
|
1927
|
0
|
|
|
|
|
0
|
$sql .= "AND node.version = content.version "; |
|
1928
|
|
|
|
|
|
|
} |
|
1929
|
0
|
|
|
|
|
0
|
$sql .= "ORDER BY name, content.version "; |
|
1930
|
|
|
|
|
|
|
|
|
1931
|
|
|
|
|
|
|
# Query |
|
1932
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
1933
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare( $sql ); |
|
1934
|
0
|
|
|
|
|
0
|
$sth->execute( "0" ); |
|
1935
|
|
|
|
|
|
|
|
|
1936
|
0
|
|
|
|
|
0
|
my @nodes; |
|
1937
|
0
|
|
|
|
|
0
|
while(my @results = $sth->fetchrow_array) { |
|
1938
|
0
|
|
|
|
|
0
|
my %data; |
|
1939
|
0
|
|
|
|
|
0
|
@data{ qw( node_id name moderated_version version ) } = @results; |
|
1940
|
0
|
|
|
|
|
0
|
push @nodes, \%data; |
|
1941
|
|
|
|
|
|
|
} |
|
1942
|
|
|
|
|
|
|
|
|
1943
|
0
|
|
|
|
|
0
|
return @nodes; |
|
1944
|
|
|
|
|
|
|
} |
|
1945
|
|
|
|
|
|
|
|
|
1946
|
|
|
|
|
|
|
=item B |
|
1947
|
|
|
|
|
|
|
|
|
1948
|
|
|
|
|
|
|
List the last version of every node before a given date. |
|
1949
|
|
|
|
|
|
|
If no version existed before that date, will return undef for version. |
|
1950
|
|
|
|
|
|
|
Returns a hash of id, name, version and date |
|
1951
|
|
|
|
|
|
|
|
|
1952
|
|
|
|
|
|
|
my @nv = $wiki->list_last_version_before('2007-01-02 10:34:11') |
|
1953
|
|
|
|
|
|
|
foreach my $data (@nv) { |
|
1954
|
|
|
|
|
|
|
|
|
1955
|
|
|
|
|
|
|
} |
|
1956
|
|
|
|
|
|
|
|
|
1957
|
|
|
|
|
|
|
=cut |
|
1958
|
|
|
|
|
|
|
|
|
1959
|
|
|
|
|
|
|
sub list_last_version_before { |
|
1960
|
0
|
|
|
0
|
1
|
0
|
my ($self, $date) = @_; |
|
1961
|
|
|
|
|
|
|
|
|
1962
|
0
|
|
|
|
|
0
|
my $sql = |
|
1963
|
|
|
|
|
|
|
"SELECT " |
|
1964
|
|
|
|
|
|
|
." id, name, " |
|
1965
|
|
|
|
|
|
|
."MAX(content.version) AS version, MAX(content.modified) AS modified " |
|
1966
|
|
|
|
|
|
|
."FROM node " |
|
1967
|
|
|
|
|
|
|
."LEFT OUTER JOIN content " |
|
1968
|
|
|
|
|
|
|
." ON (id = node_id " |
|
1969
|
|
|
|
|
|
|
." AND content.modified <= ?) " |
|
1970
|
|
|
|
|
|
|
."GROUP BY id, name " |
|
1971
|
|
|
|
|
|
|
."ORDER BY id " |
|
1972
|
|
|
|
|
|
|
; |
|
1973
|
|
|
|
|
|
|
|
|
1974
|
|
|
|
|
|
|
# Query |
|
1975
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
1976
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare( $sql ); |
|
1977
|
0
|
|
|
|
|
0
|
$sth->execute( $date ); |
|
1978
|
|
|
|
|
|
|
|
|
1979
|
0
|
|
|
|
|
0
|
my @nodes; |
|
1980
|
0
|
|
|
|
|
0
|
while(my @results = $sth->fetchrow_array) { |
|
1981
|
0
|
|
|
|
|
0
|
my %data; |
|
1982
|
0
|
|
|
|
|
0
|
@data{ qw( id name version modified ) } = @results; |
|
1983
|
0
|
|
|
|
|
0
|
$data{'node_id'} = $data{'id'}; |
|
1984
|
0
|
0
|
|
|
|
0
|
unless($data{'version'}) { $data{'version'} = undef; } |
|
|
0
|
|
|
|
|
0
|
|
|
1985
|
0
|
|
|
|
|
0
|
push @nodes, \%data; |
|
1986
|
|
|
|
|
|
|
} |
|
1987
|
|
|
|
|
|
|
|
|
1988
|
0
|
|
|
|
|
0
|
return @nodes; |
|
1989
|
|
|
|
|
|
|
} |
|
1990
|
|
|
|
|
|
|
|
|
1991
|
|
|
|
|
|
|
|
|
1992
|
|
|
|
|
|
|
# Internal function only, used when querying latest metadata |
|
1993
|
|
|
|
|
|
|
sub _current_node_id_versions { |
|
1994
|
0
|
|
|
0
|
|
0
|
my ($self) = @_; |
|
1995
|
|
|
|
|
|
|
|
|
1996
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
1997
|
|
|
|
|
|
|
|
|
1998
|
0
|
|
|
|
|
0
|
my $nv_sql = |
|
1999
|
|
|
|
|
|
|
"SELECT node_id, MAX(version) ". |
|
2000
|
|
|
|
|
|
|
"FROM content ". |
|
2001
|
|
|
|
|
|
|
"WHERE moderated ". |
|
2002
|
|
|
|
|
|
|
"GROUP BY node_id "; |
|
2003
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare( $nv_sql ); |
|
2004
|
0
|
|
|
|
|
0
|
$sth->execute(); |
|
2005
|
|
|
|
|
|
|
|
|
2006
|
0
|
|
|
|
|
0
|
my @nv_where; |
|
2007
|
0
|
|
|
|
|
0
|
while(my @results = $sth->fetchrow_array) { |
|
2008
|
0
|
|
|
|
|
0
|
my ($node_id, $version) = @results; |
|
2009
|
0
|
|
|
|
|
0
|
my $where = "(node_id=$node_id AND version=$version)"; |
|
2010
|
0
|
|
|
|
|
0
|
push @nv_where, $where; |
|
2011
|
|
|
|
|
|
|
} |
|
2012
|
0
|
|
|
|
|
0
|
return @nv_where; |
|
2013
|
|
|
|
|
|
|
} |
|
2014
|
|
|
|
|
|
|
|
|
2015
|
|
|
|
|
|
|
=item B |
|
2016
|
|
|
|
|
|
|
|
|
2017
|
|
|
|
|
|
|
List all the currently defined values of the given type of metadata. |
|
2018
|
|
|
|
|
|
|
|
|
2019
|
|
|
|
|
|
|
Will only return data from the latest moderated version of each node |
|
2020
|
|
|
|
|
|
|
|
|
2021
|
|
|
|
|
|
|
# List all of the different metadata values with the type 'category' |
|
2022
|
|
|
|
|
|
|
my @categories = $wiki->list_metadata_by_type('category'); |
|
2023
|
|
|
|
|
|
|
|
|
2024
|
|
|
|
|
|
|
=cut |
|
2025
|
|
|
|
|
|
|
sub list_metadata_by_type { |
|
2026
|
0
|
|
|
0
|
1
|
0
|
my ($self, $type) = @_; |
|
2027
|
|
|
|
|
|
|
|
|
2028
|
0
|
0
|
|
|
|
0
|
return undef unless $type; |
|
2029
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
2030
|
|
|
|
|
|
|
|
|
2031
|
|
|
|
|
|
|
# Ideally we'd do this as one big query |
|
2032
|
|
|
|
|
|
|
# However, this would need a temporary table on many |
|
2033
|
|
|
|
|
|
|
# database engines, so we cheat and do it as two |
|
2034
|
0
|
|
|
|
|
0
|
my @nv_where = $self->_current_node_id_versions(); |
|
2035
|
|
|
|
|
|
|
|
|
2036
|
|
|
|
|
|
|
# Now the metadata bit |
|
2037
|
0
|
|
|
|
|
0
|
my $sql = |
|
2038
|
|
|
|
|
|
|
"SELECT DISTINCT metadata_value ". |
|
2039
|
|
|
|
|
|
|
"FROM metadata ". |
|
2040
|
|
|
|
|
|
|
"WHERE metadata_type = ? ". |
|
2041
|
|
|
|
|
|
|
"AND (". |
|
2042
|
|
|
|
|
|
|
join(" OR ", @nv_where). |
|
2043
|
|
|
|
|
|
|
")"; |
|
2044
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare( $sql ); |
|
2045
|
0
|
|
|
|
|
0
|
$sth->execute($type); |
|
2046
|
|
|
|
|
|
|
|
|
2047
|
0
|
|
|
|
|
0
|
my $values = $sth->fetchall_arrayref([0]); |
|
2048
|
0
|
|
|
|
|
0
|
return ( map { $self->charset_decode( $_->[0] ) } (@$values) ); |
|
|
0
|
|
|
|
|
0
|
|
|
2049
|
|
|
|
|
|
|
} |
|
2050
|
|
|
|
|
|
|
|
|
2051
|
|
|
|
|
|
|
|
|
2052
|
|
|
|
|
|
|
=item B |
|
2053
|
|
|
|
|
|
|
|
|
2054
|
|
|
|
|
|
|
List all the currently defined kinds of metadata, eg Locale, Postcode |
|
2055
|
|
|
|
|
|
|
|
|
2056
|
|
|
|
|
|
|
Will only return data from the latest moderated version of each node |
|
2057
|
|
|
|
|
|
|
|
|
2058
|
|
|
|
|
|
|
# List all of the different kinds of metadata |
|
2059
|
|
|
|
|
|
|
my @metadata_types = $wiki->list_metadata_names() |
|
2060
|
|
|
|
|
|
|
|
|
2061
|
|
|
|
|
|
|
=cut |
|
2062
|
|
|
|
|
|
|
sub list_metadata_names { |
|
2063
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
2064
|
|
|
|
|
|
|
|
|
2065
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
2066
|
|
|
|
|
|
|
|
|
2067
|
|
|
|
|
|
|
# Ideally we'd do this as one big query |
|
2068
|
|
|
|
|
|
|
# However, this would need a temporary table on many |
|
2069
|
|
|
|
|
|
|
# database engines, so we cheat and do it as two |
|
2070
|
0
|
|
|
|
|
0
|
my @nv_where = $self->_current_node_id_versions(); |
|
2071
|
|
|
|
|
|
|
|
|
2072
|
|
|
|
|
|
|
# Now the metadata bit |
|
2073
|
0
|
|
|
|
|
0
|
my $sql = |
|
2074
|
|
|
|
|
|
|
"SELECT DISTINCT metadata_type ". |
|
2075
|
|
|
|
|
|
|
"FROM metadata ". |
|
2076
|
|
|
|
|
|
|
"WHERE (". |
|
2077
|
|
|
|
|
|
|
join(" OR ", @nv_where). |
|
2078
|
|
|
|
|
|
|
")"; |
|
2079
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare( $sql ); |
|
2080
|
0
|
|
|
|
|
0
|
$sth->execute(); |
|
2081
|
|
|
|
|
|
|
|
|
2082
|
0
|
|
|
|
|
0
|
my $types = $sth->fetchall_arrayref([0]); |
|
2083
|
0
|
|
|
|
|
0
|
return ( map { $self->charset_decode( $_->[0] ) } (@$types) ); |
|
|
0
|
|
|
|
|
0
|
|
|
2084
|
|
|
|
|
|
|
} |
|
2085
|
|
|
|
|
|
|
|
|
2086
|
|
|
|
|
|
|
|
|
2087
|
|
|
|
|
|
|
=item B |
|
2088
|
|
|
|
|
|
|
|
|
2089
|
|
|
|
|
|
|
my ($code_version, $db_version) = $store->schema_current; |
|
2090
|
|
|
|
|
|
|
if ($code_version == $db_version) |
|
2091
|
|
|
|
|
|
|
# Do stuff |
|
2092
|
|
|
|
|
|
|
} else { |
|
2093
|
|
|
|
|
|
|
# Bail |
|
2094
|
|
|
|
|
|
|
} |
|
2095
|
|
|
|
|
|
|
|
|
2096
|
|
|
|
|
|
|
=cut |
|
2097
|
|
|
|
|
|
|
|
|
2098
|
|
|
|
|
|
|
sub schema_current { |
|
2099
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
2100
|
0
|
|
|
|
|
0
|
my $dbh = $self->dbh; |
|
2101
|
0
|
|
|
|
|
0
|
my $sth; |
|
2102
|
0
|
|
|
|
|
0
|
eval { $sth = $dbh->prepare("SELECT version FROM schema_info") }; |
|
|
0
|
|
|
|
|
0
|
|
|
2103
|
0
|
0
|
|
|
|
0
|
if ($@) { |
|
2104
|
0
|
|
|
|
|
0
|
return ($SCHEMA_VER, 0); |
|
2105
|
|
|
|
|
|
|
} |
|
2106
|
0
|
|
|
|
|
0
|
eval { $sth->execute }; |
|
|
0
|
|
|
|
|
0
|
|
|
2107
|
0
|
0
|
|
|
|
0
|
if ($@) { |
|
2108
|
0
|
|
|
|
|
0
|
return ($SCHEMA_VER, 0); |
|
2109
|
|
|
|
|
|
|
} |
|
2110
|
0
|
|
|
|
|
0
|
my $version; |
|
2111
|
0
|
|
|
|
|
0
|
eval { $version = $sth->fetchrow_array }; |
|
|
0
|
|
|
|
|
0
|
|
|
2112
|
0
|
0
|
|
|
|
0
|
if ($@) { |
|
2113
|
0
|
|
|
|
|
0
|
return ($SCHEMA_VER, 0); |
|
2114
|
|
|
|
|
|
|
} else { |
|
2115
|
0
|
|
|
|
|
0
|
return ($SCHEMA_VER, $version); |
|
2116
|
|
|
|
|
|
|
} |
|
2117
|
|
|
|
|
|
|
} |
|
2118
|
|
|
|
|
|
|
|
|
2119
|
|
|
|
|
|
|
|
|
2120
|
|
|
|
|
|
|
=item B |
|
2121
|
|
|
|
|
|
|
|
|
2122
|
|
|
|
|
|
|
my $dbh = $store->dbh; |
|
2123
|
|
|
|
|
|
|
|
|
2124
|
|
|
|
|
|
|
Returns the database handle belonging to this storage backend instance. |
|
2125
|
|
|
|
|
|
|
|
|
2126
|
|
|
|
|
|
|
=cut |
|
2127
|
|
|
|
|
|
|
|
|
2128
|
|
|
|
|
|
|
sub dbh { |
|
2129
|
3
|
|
|
3
|
1
|
7
|
my $self = shift; |
|
2130
|
3
|
|
|
|
|
7
|
return $self->{_dbh}; |
|
2131
|
|
|
|
|
|
|
} |
|
2132
|
|
|
|
|
|
|
|
|
2133
|
|
|
|
|
|
|
=item B |
|
2134
|
|
|
|
|
|
|
|
|
2135
|
|
|
|
|
|
|
my $dbname = $store->dbname; |
|
2136
|
|
|
|
|
|
|
|
|
2137
|
|
|
|
|
|
|
Returns the name of the database used for backend storage. |
|
2138
|
|
|
|
|
|
|
|
|
2139
|
|
|
|
|
|
|
=cut |
|
2140
|
|
|
|
|
|
|
|
|
2141
|
|
|
|
|
|
|
sub dbname { |
|
2142
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
2143
|
0
|
|
|
|
|
0
|
return $self->{_dbname}; |
|
2144
|
|
|
|
|
|
|
} |
|
2145
|
|
|
|
|
|
|
|
|
2146
|
|
|
|
|
|
|
=item B |
|
2147
|
|
|
|
|
|
|
|
|
2148
|
|
|
|
|
|
|
my $dbuser = $store->dbuser; |
|
2149
|
|
|
|
|
|
|
|
|
2150
|
|
|
|
|
|
|
Returns the username used to connect to the database used for backend storage. |
|
2151
|
|
|
|
|
|
|
|
|
2152
|
|
|
|
|
|
|
=cut |
|
2153
|
|
|
|
|
|
|
|
|
2154
|
|
|
|
|
|
|
sub dbuser { |
|
2155
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
2156
|
0
|
|
|
|
|
0
|
return $self->{_dbuser}; |
|
2157
|
|
|
|
|
|
|
} |
|
2158
|
|
|
|
|
|
|
|
|
2159
|
|
|
|
|
|
|
=item B |
|
2160
|
|
|
|
|
|
|
|
|
2161
|
|
|
|
|
|
|
my $dbpass = $store->dbpass; |
|
2162
|
|
|
|
|
|
|
|
|
2163
|
|
|
|
|
|
|
Returns the password used to connect to the database used for backend storage. |
|
2164
|
|
|
|
|
|
|
|
|
2165
|
|
|
|
|
|
|
=cut |
|
2166
|
|
|
|
|
|
|
|
|
2167
|
|
|
|
|
|
|
sub dbpass { |
|
2168
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
2169
|
0
|
|
|
|
|
0
|
return $self->{_dbpass}; |
|
2170
|
|
|
|
|
|
|
} |
|
2171
|
|
|
|
|
|
|
|
|
2172
|
|
|
|
|
|
|
=item B |
|
2173
|
|
|
|
|
|
|
|
|
2174
|
|
|
|
|
|
|
my $dbhost = $store->dbhost; |
|
2175
|
|
|
|
|
|
|
|
|
2176
|
|
|
|
|
|
|
Returns the optional host used to connect to the database used for |
|
2177
|
|
|
|
|
|
|
backend storage. |
|
2178
|
|
|
|
|
|
|
|
|
2179
|
|
|
|
|
|
|
=cut |
|
2180
|
|
|
|
|
|
|
|
|
2181
|
|
|
|
|
|
|
sub dbhost { |
|
2182
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
2183
|
0
|
|
|
|
|
0
|
return $self->{_dbhost}; |
|
2184
|
|
|
|
|
|
|
} |
|
2185
|
|
|
|
|
|
|
|
|
2186
|
|
|
|
|
|
|
# Cleanup. |
|
2187
|
|
|
|
|
|
|
sub DESTROY { |
|
2188
|
3
|
|
|
3
|
|
12
|
my $self = shift; |
|
2189
|
3
|
50
|
|
|
|
23
|
return if $self->{_external_dbh}; |
|
2190
|
3
|
|
|
|
|
19
|
my $dbh = $self->dbh; |
|
2191
|
3
|
50
|
|
|
|
22
|
$dbh->disconnect if $dbh; |
|
2192
|
|
|
|
|
|
|
} |
|
2193
|
|
|
|
|
|
|
|
|
2194
|
|
|
|
|
|
|
# decode a string of octets into perl's internal encoding, based on the |
|
2195
|
|
|
|
|
|
|
# charset parameter we were passed. Takes a list, returns a list. |
|
2196
|
|
|
|
|
|
|
sub charset_decode { |
|
2197
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
2198
|
0
|
|
|
|
|
|
my @input = @_; |
|
2199
|
0
|
0
|
|
|
|
|
if ($CAN_USE_ENCODE) { |
|
2200
|
0
|
|
|
|
|
|
my @output; |
|
2201
|
0
|
|
|
|
|
|
for (@input) { |
|
2202
|
0
|
|
|
|
|
|
push( @output, Encode::decode( $self->{_charset}, $_ ) ); |
|
2203
|
|
|
|
|
|
|
} |
|
2204
|
0
|
|
|
|
|
|
return @output; |
|
2205
|
|
|
|
|
|
|
} |
|
2206
|
0
|
|
|
|
|
|
return @input; |
|
2207
|
|
|
|
|
|
|
} |
|
2208
|
|
|
|
|
|
|
|
|
2209
|
|
|
|
|
|
|
# convert a perl string into a series of octets we can put into the database |
|
2210
|
|
|
|
|
|
|
# takes a list, returns a list |
|
2211
|
|
|
|
|
|
|
sub charset_encode { |
|
2212
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
2213
|
0
|
|
|
|
|
|
my @input = @_; |
|
2214
|
0
|
0
|
|
|
|
|
if ($CAN_USE_ENCODE) { |
|
2215
|
0
|
|
|
|
|
|
my @output; |
|
2216
|
0
|
|
|
|
|
|
for (@input) { |
|
2217
|
0
|
|
|
|
|
|
push( @output, Encode::encode( $self->{_charset}, $_ ) ); |
|
2218
|
|
|
|
|
|
|
} |
|
2219
|
0
|
|
|
|
|
|
return @output; |
|
2220
|
|
|
|
|
|
|
} |
|
2221
|
0
|
|
|
|
|
|
return @input; |
|
2222
|
|
|
|
|
|
|
} |
|
2223
|
|
|
|
|
|
|
|
|
2224
|
|
|
|
|
|
|
=back |
|
2225
|
|
|
|
|
|
|
|
|
2226
|
|
|
|
|
|
|
=cut |
|
2227
|
|
|
|
|
|
|
|
|
2228
|
|
|
|
|
|
|
1; |