line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package MojoMojo; |
2
|
|
|
|
|
|
|
|
3
|
35
|
|
|
35
|
|
17565820
|
use strict; |
|
35
|
|
|
|
|
89
|
|
|
35
|
|
|
|
|
999
|
|
4
|
35
|
|
|
35
|
|
562
|
use Path::Class 'file'; |
|
35
|
|
|
|
|
27566
|
|
|
35
|
|
|
|
|
1912
|
|
5
|
|
|
|
|
|
|
|
6
|
35
|
|
|
|
|
284
|
use Catalyst qw/ |
7
|
|
|
|
|
|
|
ConfigLoader |
8
|
|
|
|
|
|
|
Authentication |
9
|
|
|
|
|
|
|
Cache |
10
|
|
|
|
|
|
|
Session |
11
|
|
|
|
|
|
|
Session::Store::Cache |
12
|
|
|
|
|
|
|
Session::State::Cookie |
13
|
|
|
|
|
|
|
Static::Simple |
14
|
|
|
|
|
|
|
SubRequest |
15
|
|
|
|
|
|
|
I18N |
16
|
|
|
|
|
|
|
Setenv |
17
|
35
|
|
|
35
|
|
27135
|
/; |
|
35
|
|
|
|
|
35688807
|
|
18
|
|
|
|
|
|
|
|
19
|
35
|
|
|
35
|
|
403746
|
use Storable; |
|
35
|
|
|
|
|
90
|
|
|
35
|
|
|
|
|
2218
|
|
20
|
35
|
|
|
35
|
|
216
|
use Digest::MD5; |
|
35
|
|
|
|
|
96
|
|
|
35
|
|
|
|
|
1160
|
|
21
|
35
|
|
|
35
|
|
15254
|
use Data::Dumper; |
|
35
|
|
|
|
|
106749
|
|
|
35
|
|
|
|
|
1789
|
|
22
|
35
|
|
|
35
|
|
21907
|
use DateTime; |
|
35
|
|
|
|
|
11461127
|
|
|
35
|
|
|
|
|
1441
|
|
23
|
35
|
|
|
35
|
|
293
|
use MRO::Compat; |
|
35
|
|
|
|
|
81
|
|
|
35
|
|
|
|
|
732
|
|
24
|
35
|
|
|
35
|
|
18947
|
use DBIx::Class::ResultClass::HashRefInflator; |
|
35
|
|
|
|
|
9841
|
|
|
35
|
|
|
|
|
987
|
|
25
|
35
|
|
|
35
|
|
305
|
use Encode (); |
|
35
|
|
|
|
|
117
|
|
|
35
|
|
|
|
|
466
|
|
26
|
35
|
|
|
35
|
|
158
|
use URI::Escape (); |
|
35
|
|
|
|
|
89
|
|
|
35
|
|
|
|
|
468
|
|
27
|
35
|
|
|
35
|
|
13048
|
use MojoMojo::Formatter::Wiki; |
|
35
|
|
|
|
|
118
|
|
|
35
|
|
|
|
|
1812
|
|
28
|
|
|
|
|
|
|
use Module::Pluggable::Ordered |
29
|
35
|
|
|
|
|
355
|
search_path => 'MojoMojo::Formatter', |
30
|
|
|
|
|
|
|
except => qr/^MojoMojo::Plugin::/, |
31
|
35
|
|
|
35
|
|
17057
|
require => 1; |
|
35
|
|
|
|
|
96312
|
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
our $VERSION = '1.12'; |
34
|
35
|
|
|
35
|
|
4999
|
use 5.008004; |
|
35
|
|
|
|
|
139
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
MojoMojo->config->{authentication}{dbic} = { |
37
|
|
|
|
|
|
|
user_class => 'DBIC::Person', |
38
|
|
|
|
|
|
|
user_field => 'login', |
39
|
|
|
|
|
|
|
password_field => 'pass' |
40
|
|
|
|
|
|
|
}; |
41
|
|
|
|
|
|
|
MojoMojo->config->{default_view} = 'TT'; |
42
|
|
|
|
|
|
|
MojoMojo->config->{'Plugin::Cache'}{backend} = { |
43
|
|
|
|
|
|
|
class => "Cache::FastMmap", |
44
|
|
|
|
|
|
|
unlink_on_exit => 1, |
45
|
|
|
|
|
|
|
share_file => '' |
46
|
|
|
|
|
|
|
. Path::Class::file( |
47
|
|
|
|
|
|
|
File::Spec->tmpdir, |
48
|
|
|
|
|
|
|
'mojomojo-sharefile-' . Digest::MD5::md5_hex(MojoMojo->config->{home}) |
49
|
|
|
|
|
|
|
), |
50
|
|
|
|
|
|
|
}; |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
__PACKAGE__->config( |
53
|
|
|
|
|
|
|
authentication => { |
54
|
|
|
|
|
|
|
default_realm => 'members', |
55
|
|
|
|
|
|
|
use_session => 1, |
56
|
|
|
|
|
|
|
realms => { |
57
|
|
|
|
|
|
|
members => { |
58
|
|
|
|
|
|
|
credential => { |
59
|
|
|
|
|
|
|
class => 'Password', |
60
|
|
|
|
|
|
|
password_field => 'pass', |
61
|
|
|
|
|
|
|
password_type => 'hashed', |
62
|
|
|
|
|
|
|
password_hash_type => 'SHA-1', |
63
|
|
|
|
|
|
|
}, |
64
|
|
|
|
|
|
|
store => {class => 'DBIx::Class', user_class => 'DBIC::Person',}, |
65
|
|
|
|
|
|
|
}, |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
__PACKAGE__->config('Controller::HTML::FormFu' => |
71
|
|
|
|
|
|
|
{languages_from_context => 1, localize_from_context => 1,}); |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
__PACKAGE__->config(setup_components => {search_extra => ['::Extensions'],}); |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
MojoMojo->setup(); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# Check for deployed database |
78
|
|
|
|
|
|
|
my $has_DB = 1; |
79
|
|
|
|
|
|
|
my $NO_DB_MESSAGE = <<"EOF"; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
*********************************************** |
82
|
|
|
|
|
|
|
ERROR. Looks like you need to deploy a database. |
83
|
|
|
|
|
|
|
Run script/mojomojo_spawn_db.pl |
84
|
|
|
|
|
|
|
*********************************************** |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
EOF |
87
|
|
|
|
|
|
|
eval { |
88
|
|
|
|
|
|
|
MojoMojo->model('DBIC') |
89
|
|
|
|
|
|
|
->schema->resultset('MojoMojo::Schema::Result::Person')->next; |
90
|
|
|
|
|
|
|
}; |
91
|
|
|
|
|
|
|
if ($@) { |
92
|
|
|
|
|
|
|
$has_DB = 0; |
93
|
|
|
|
|
|
|
warn $NO_DB_MESSAGE; |
94
|
|
|
|
|
|
|
warn "(Error: $@)"; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
MojoMojo->model('DBIC') |
98
|
|
|
|
|
|
|
->schema->attachment_dir(MojoMojo->config->{attachment_dir} |
99
|
|
|
|
|
|
|
|| MojoMojo->path_to('uploads') . ''); |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=head1 NAME |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
MojoMojo - A Wiki with a tree |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head1 SYNOPSIS |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
# Set up database (see mojomojo.conf first) |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
./script/mojomojo_spawn_db.pl |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
# Standalone mode |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
./script/mojomo_server.pl |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# In apache conf |
116
|
|
|
|
|
|
|
<Location /mojomojo> |
117
|
|
|
|
|
|
|
SetHandler perl-script |
118
|
|
|
|
|
|
|
PerlHandler MojoMojo |
119
|
|
|
|
|
|
|
</Location> |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=head1 DESCRIPTION |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Mojomojo is a content management system, borrowing many concepts from |
124
|
|
|
|
|
|
|
wikis and blogs. It allows you to maintain a full tree-structure of pages, |
125
|
|
|
|
|
|
|
and to interlink them in various ways. It has full version support, so you can |
126
|
|
|
|
|
|
|
always go back to a previous version and see what's changed with an easy diff |
127
|
|
|
|
|
|
|
system. There are also a some of useful features like live AJAX preview while |
128
|
|
|
|
|
|
|
editing, tagging, built-in fulltext search, image galleries, and RSS feeds |
129
|
|
|
|
|
|
|
for every wiki page. |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
To find out more about how you can use MojoMojo, please visit |
132
|
|
|
|
|
|
|
L<http://mojomojo.org/> or read the installation instructions in |
133
|
|
|
|
|
|
|
L<MojoMojo::Installation> to try it out yourself. |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=head1 METHODS |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=head2 prepare |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
Accommodate a forcing of SSL if needed in a reverse proxy setup. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=cut |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub prepare { |
144
|
197
|
|
|
197
|
1
|
8009055
|
my $self = shift->next::method(@_); |
145
|
197
|
50
|
|
|
|
263827
|
if ($self->config->{force_ssl}) { |
146
|
0
|
|
|
|
|
0
|
my $request = $self->request; |
147
|
0
|
|
|
|
|
0
|
$request->base->scheme('https'); |
148
|
0
|
|
|
|
|
0
|
$request->uri->scheme('https'); |
149
|
|
|
|
|
|
|
} |
150
|
197
|
|
|
|
|
17450
|
return $self; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=head2 ajax |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Return whether the request is an AJAX one (used by the live preview, |
157
|
|
|
|
|
|
|
for example), as opposed to a rgular request (such as one used to view |
158
|
|
|
|
|
|
|
a page). |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=cut |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub ajax { |
163
|
0
|
|
|
0
|
1
|
0
|
my ($c) = @_; |
164
|
0
|
|
0
|
|
|
0
|
return $c->req->header('x-requested-with') |
165
|
|
|
|
|
|
|
&& $c->req->header('x-requested-with') eq 'XMLHttpRequest'; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=head2 expand_wikilink |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
Proxy method for the L<MojoMojo::Formatter::Wiki> expand_wikilink method. |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=cut |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
sub expand_wikilink { |
175
|
105
|
|
|
105
|
1
|
2495
|
my $c = shift; |
176
|
105
|
|
|
|
|
796
|
return MojoMojo::Formatter::Wiki->expand_wikilink(@_); |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=head2 wikiword |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
Format a wikiword as a link or as a wanted page, as appropriate. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=cut |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub wikiword { |
186
|
62
|
|
|
62
|
1
|
4796
|
return MojoMojo::Formatter::Wiki->format_link(@_); |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head2 pref |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Find or create a preference key. Update it if a value is passed, then |
192
|
|
|
|
|
|
|
return the current setting. |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=cut |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub pref { |
197
|
3145
|
|
|
3145
|
1
|
394071
|
my ($c, $setting, $value) = @_; |
198
|
|
|
|
|
|
|
|
199
|
3145
|
50
|
|
|
|
9223
|
return unless $setting; |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# Unfortunately there are MojoMojo->pref() calls in |
202
|
|
|
|
|
|
|
# MojoMojo::Schema::Result::Person which makes it hard |
203
|
|
|
|
|
|
|
# to get cache working for those calls - so we'll just |
204
|
|
|
|
|
|
|
# not use caching for those calls. |
205
|
3145
|
100
|
|
|
|
14505
|
return $c->pref_cached($setting, $value) if ref($c) eq 'MojoMojo'; |
206
|
|
|
|
|
|
|
|
207
|
166
|
|
|
|
|
956
|
$setting |
208
|
|
|
|
|
|
|
= $c->model('DBIC::Preference')->find_or_create({prefkey => $setting}); |
209
|
165
|
50
|
|
|
|
757259
|
if (defined $value) { |
210
|
0
|
|
|
|
|
0
|
$setting->prefvalue($value); |
211
|
0
|
|
|
|
|
0
|
$setting->update(); |
212
|
0
|
|
|
|
|
0
|
return $value; |
213
|
|
|
|
|
|
|
} |
214
|
165
|
100
|
|
|
|
7036
|
return (defined $setting->prefvalue() ? $setting->prefvalue : ""); |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=head2 pref_cached |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
Get preference key/value from cache if possible. |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=cut |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub pref_cached { |
224
|
2979
|
|
|
2979
|
1
|
7170
|
my ($c, $setting, $value) = @_; |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# Already in cache and no new value to set? |
227
|
2979
|
100
|
100
|
|
|
11879
|
if (defined $c->cache->get($setting) and not defined $value) { |
228
|
2358
|
|
|
|
|
546248
|
return $c->cache->get($setting); |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# Check that we have a database, i.e. script/mojomojo_spawn_db.pl was run. |
232
|
621
|
|
|
|
|
144161
|
my $row; |
233
|
621
|
|
|
|
|
3343
|
$row = $c->model('DBIC::Preference')->find_or_create({prefkey => $setting}); |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# Update database |
236
|
621
|
100
|
|
|
|
2472196
|
$row->update({prefvalue => $value}) if defined $value; |
237
|
|
|
|
|
|
|
|
238
|
621
|
|
|
|
|
138321
|
my $prefvalue = $row->prefvalue(); |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# if no entry in preferences, try get one from config or get default value |
241
|
621
|
100
|
|
|
|
12951
|
unless (defined $prefvalue) { |
242
|
|
|
|
|
|
|
|
243
|
502
|
100
|
|
|
|
6240
|
if ($setting eq 'main_formatter') { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
244
|
|
|
|
|
|
|
$prefvalue |
245
|
|
|
|
|
|
|
= defined $c->config->{'main_formatter'} |
246
|
14
|
50
|
|
|
|
106
|
? $c->config->{'main_formatter'} |
247
|
|
|
|
|
|
|
: 'MojoMojo::Formatter::Markdown'; |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
elsif ($setting eq 'default_lang') { |
250
|
|
|
|
|
|
|
$prefvalue |
251
|
30
|
50
|
|
|
|
186
|
= defined $c->config->{$setting} ? $c->config->{$setting} : 'en'; |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
elsif ($setting eq 'name') { |
254
|
|
|
|
|
|
|
$prefvalue |
255
|
0
|
0
|
|
|
|
0
|
= defined $c->config->{$setting} ? $c->config->{$setting} : 'MojoMojo'; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
elsif ($setting eq 'theme') { |
258
|
|
|
|
|
|
|
$prefvalue |
259
|
0
|
0
|
|
|
|
0
|
= defined $c->config->{$setting} ? $c->config->{$setting} : 'default'; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
elsif ($setting =~ /^(enforce_login|check_permission_on_view)$/) { |
262
|
|
|
|
|
|
|
$prefvalue |
263
|
|
|
|
|
|
|
= defined $c->config->{'permissions'}{$setting} |
264
|
50
|
50
|
|
|
|
301
|
? $c->config->{'permissions'}{$setting} |
265
|
|
|
|
|
|
|
: 0; |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
elsif ($setting |
268
|
|
|
|
|
|
|
=~ /^(cache_permission_data|create_allowed|delete_allowed|edit_allowed|view_allowed|attachment_allowed)$/ |
269
|
|
|
|
|
|
|
) |
270
|
|
|
|
|
|
|
{ |
271
|
|
|
|
|
|
|
$prefvalue |
272
|
|
|
|
|
|
|
= defined $c->config->{'permissions'}{$setting} |
273
|
132
|
50
|
|
|
|
770
|
? $c->config->{'permissions'}{$setting} |
274
|
|
|
|
|
|
|
: 1; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
else { |
277
|
276
|
|
|
|
|
1822
|
$prefvalue = $c->config->{$setting}; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
# Update cache |
283
|
621
|
|
|
|
|
74619
|
$c->cache->set($setting => $prefvalue); |
284
|
|
|
|
|
|
|
|
285
|
621
|
|
|
|
|
197888
|
return $c->cache->get($setting); |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=head2 fixw |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
Clean up wiki words: replace spaces with underscores and remove non-\w, / and . |
291
|
|
|
|
|
|
|
characters. |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=cut |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
sub fixw { |
296
|
0
|
|
|
0
|
1
|
0
|
my ($c, $w) = @_; |
297
|
0
|
|
|
|
|
0
|
$w =~ s/\s/\_/g; |
298
|
0
|
|
|
|
|
0
|
$w =~ s/[^\w\/\.]//g; |
299
|
0
|
|
|
|
|
0
|
return $w; |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
=head2 tz |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
Convert timezone |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=cut |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
sub tz { |
309
|
107
|
|
|
107
|
1
|
68909
|
my ($c, $dt) = @_; |
310
|
107
|
50
|
66
|
|
|
696
|
if ($c->user && $c->user->timezone) { |
311
|
0
|
|
|
|
|
0
|
eval { $dt->set_time_zone($c->user->timezone) }; |
|
0
|
|
|
|
|
0
|
|
312
|
|
|
|
|
|
|
} |
313
|
107
|
|
|
|
|
94912
|
return $dt; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=head2 prepare_action |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
Provide "No DB" message when one needs to spawn the db (script/mojomojo_spawn.pl). |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=cut |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
sub prepare_action { |
323
|
|
|
|
|
|
|
my $c = shift; |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
if ($has_DB) { |
326
|
|
|
|
|
|
|
$c->next::method(@_); |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
else { |
329
|
|
|
|
|
|
|
$c->res->status(404); |
330
|
|
|
|
|
|
|
$c->response->body($NO_DB_MESSAGE); |
331
|
|
|
|
|
|
|
return; |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=head2 prepare_path |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
We override this method to work around some of Catalyst's assumptions about |
338
|
|
|
|
|
|
|
dispatching. Since MojoMojo supports page namespaces |
339
|
|
|
|
|
|
|
(e.g. C</parent_page/child_page>), with page paths that always start with C</>, |
340
|
|
|
|
|
|
|
we strip the trailing slash from C<< $c->req->base >>. Also, since MojoMojo |
341
|
|
|
|
|
|
|
indicates actions by appending a C<.$action> to the path |
342
|
|
|
|
|
|
|
(e.g. C</parent_page/child_page.edit>), we remove the page path and save it in |
343
|
|
|
|
|
|
|
C<< $c->stash->{path} >> and reset C<< $c->req->path >> to C<< $action >>. |
344
|
|
|
|
|
|
|
We save the original URI in C<< $c->stash->{pre_hacked_uri} >>. |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=cut |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
sub prepare_path { |
349
|
197
|
|
|
197
|
1
|
1726708
|
my $c = shift; |
350
|
197
|
|
|
|
|
1308
|
$c->next::method(@_); |
351
|
197
|
|
|
|
|
102455
|
$c->stash->{pre_hacked_uri} = $c->req->uri->clone; |
352
|
197
|
|
|
|
|
24021
|
my $base = $c->req->base; |
353
|
197
|
|
|
|
|
10041
|
$base =~ s|/+$||; |
354
|
197
|
|
|
|
|
3237
|
$c->req->base(URI->new($base)); |
355
|
197
|
|
|
|
|
28488
|
my ($path, $action); |
356
|
197
|
|
|
|
|
892
|
$path = $c->req->path; |
357
|
|
|
|
|
|
|
|
358
|
197
|
100
|
|
|
|
33404
|
if ($path =~ /^special(?:\/|$)(.*)/) { |
359
|
12
|
|
|
|
|
50
|
$c->stash->{path} = $path; |
360
|
12
|
|
|
|
|
726
|
$c->req->path($1); |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
else { |
363
|
|
|
|
|
|
|
# find the *last* period, so that pages can have periods in their name. |
364
|
185
|
|
|
|
|
584
|
my $index = index($path, '.'); |
365
|
|
|
|
|
|
|
|
366
|
185
|
100
|
|
|
|
740
|
if ($index == -1) { |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# no action found, default to view |
369
|
24
|
|
|
|
|
250
|
$c->stash->{path} = $path; |
370
|
24
|
|
|
|
|
1561
|
$c->req->path('view'); |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
else { |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
# set path in stash, and set req.path to action |
375
|
161
|
|
|
|
|
866
|
$c->stash->{path} = substr($path, 0, $index); |
376
|
161
|
|
|
|
|
10028
|
$c->req->path(substr($path, $index + 1)); |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
} |
379
|
197
|
50
|
|
|
|
25621
|
$c->stash->{path} = '/' . $c->stash->{path} unless ($path =~ m!^/!); |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=head2 base_uri |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
Return C<< $c->req->base >> as an URI object. |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
=cut |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
sub base_uri { |
389
|
5
|
|
|
5
|
1
|
15
|
my $c = shift; |
390
|
5
|
|
|
|
|
23
|
return URI->new($c->req->base); |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
=head2 uri_for |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
Override C<< $c->uri_for >> to append path, if a relative path is used. |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
=cut |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
sub uri_for { |
400
|
4227
|
|
|
4227
|
1
|
149941
|
my $c = shift; |
401
|
4227
|
100
|
|
|
|
15774
|
unless ($_[0] =~ m/^\//) { |
402
|
2020
|
|
|
|
|
4329
|
my $val = shift @_; |
403
|
2020
|
50
|
|
|
|
7235
|
my $prefix = $c->stash->{path} =~ m|^/| ? '' : '/'; |
404
|
2020
|
|
|
|
|
149026
|
unshift(@_, $prefix . $c->stash->{path} . '.' . $val); |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
# do I see unicode here? |
408
|
4227
|
100
|
|
|
|
128867
|
if (Encode::is_utf8($_[0])) { |
409
|
|
|
|
|
|
|
$_[0] |
410
|
36
|
|
|
|
|
226
|
= join('/', map { URI::Escape::uri_escape_utf8($_) } split(/\//, $_[0])); |
|
82
|
|
|
|
|
1237
|
|
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
4227
|
|
|
|
|
15627
|
my $res = $c->next::method(@_); |
414
|
4227
|
50
|
|
|
|
716123
|
$res->scheme('https') if $c->config->{'force_ssl'}; |
415
|
4227
|
|
|
|
|
398513
|
return $res; |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=head2 uri_for_static |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
C</static/> has been remapped to C</.static/>. |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=cut |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
sub uri_for_static { |
425
|
1688
|
|
|
1688
|
1
|
13719
|
my ($self, $asset) = @_; |
426
|
|
|
|
|
|
|
return ( |
427
|
|
|
|
|
|
|
defined($self->config->{static_path}) |
428
|
1688
|
50
|
|
|
|
5067
|
? $self->config->{static_path} . $asset |
429
|
|
|
|
|
|
|
: $self->uri_for('/.static', $asset)); |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=head2 _cleanup_path |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
Lowercase the path and remove any double-slashes. |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=cut |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
sub _cleanup_path { |
439
|
117
|
|
|
117
|
|
381
|
my ($c, $path) = @_; |
440
|
|
|
|
|
|
|
## Make some changes to the path - we have to do this |
441
|
|
|
|
|
|
|
## because path is not always cleaned up before we get it: |
442
|
|
|
|
|
|
|
## sometimes we get caps, other times we don't. Permissions are |
443
|
|
|
|
|
|
|
## set using lowercase paths. |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
## lowercase the path - and ensure it has a leading / |
446
|
117
|
|
|
|
|
411
|
my $searchpath = lc($path); |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
# clear out any double-slashes |
449
|
117
|
|
|
|
|
385
|
$searchpath =~ s|//|/|g; |
450
|
|
|
|
|
|
|
|
451
|
117
|
|
|
|
|
359
|
return $searchpath; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=head2 _expand_path_elements |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
Generate all the intermediary paths to C</path/to/a/page>, starting from C</> |
457
|
|
|
|
|
|
|
and ending with the complete path: |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
/ |
460
|
|
|
|
|
|
|
/path |
461
|
|
|
|
|
|
|
/path/to |
462
|
|
|
|
|
|
|
/path/to/a |
463
|
|
|
|
|
|
|
/path/to/a/page |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
=cut |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
sub _expand_path_elements { |
468
|
117
|
|
|
117
|
|
17099
|
my ($c, $path) = @_; |
469
|
117
|
|
|
|
|
550
|
my $searchpath = $c->_cleanup_path($path); |
470
|
|
|
|
|
|
|
|
471
|
117
|
|
|
|
|
609
|
my @pathelements = split '/', $searchpath; |
472
|
|
|
|
|
|
|
|
473
|
117
|
100
|
66
|
|
|
747
|
if (@pathelements && $pathelements[0] eq '') { |
474
|
43
|
|
|
|
|
125
|
shift @pathelements; |
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
|
477
|
117
|
|
|
|
|
385
|
my @paths_to_check = ('/'); |
478
|
|
|
|
|
|
|
|
479
|
117
|
|
|
|
|
316
|
my $current_path = ''; |
480
|
|
|
|
|
|
|
|
481
|
117
|
|
|
|
|
334
|
foreach my $pathitem (@pathelements) { |
482
|
54
|
|
|
|
|
156
|
$current_path .= "/" . $pathitem; |
483
|
54
|
|
|
|
|
146
|
push @paths_to_check, $current_path; |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
|
486
|
117
|
|
|
|
|
428
|
return @paths_to_check; |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=head2 get_permissions_data |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
Permissions are checked prior to most actions, including C<view> if that is |
492
|
|
|
|
|
|
|
turned on in the configuration. The permission system works as follows: |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
=over |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
=item 1. |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
There is a base set of rules which may be defined in the application |
499
|
|
|
|
|
|
|
config. These are: |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
$c->config->{permissions}{view_allowed} = 1; # or 0 |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
Similar entries exist for C<delete>, C<edit>, C<create> and C<attachment>. |
504
|
|
|
|
|
|
|
If these config variables are not defined, the default is to allow anyone |
505
|
|
|
|
|
|
|
to do anything. |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=item 2. |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
Global rules that apply to everyone may be specified by creating a |
510
|
|
|
|
|
|
|
record with a role id of 0. |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=item 3. |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
Rules are defined using a combination of path(s)?, and role and may be |
515
|
|
|
|
|
|
|
applied to subpages or not. |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
TODO: clarify. |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
=item 4. |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
All rules matching a given user's roles and the current path are used to |
522
|
|
|
|
|
|
|
determine the final yes/no on each permission. Rules are evaluated from |
523
|
|
|
|
|
|
|
least-specific path to most specific. This means that when checking |
524
|
|
|
|
|
|
|
permissions on C</foo/bar/baz>, permission rules set for C</foo> will be |
525
|
|
|
|
|
|
|
overridden by rules set on C</foo/bar> when editing C</foo/bar/baz>. When two |
526
|
|
|
|
|
|
|
rules (from different roles) are found for the same path prefix, explicit |
527
|
|
|
|
|
|
|
C<allow>s override C<deny>s. Null entries for a given permission are always |
528
|
|
|
|
|
|
|
ignored and do not affect the permissions defined at earlier level. This |
529
|
|
|
|
|
|
|
allows you to change certain permissions (such as C<create>) only while not |
530
|
|
|
|
|
|
|
affecting previously determined permissions for the other actions. Finally - |
531
|
|
|
|
|
|
|
C<apply_to_subpages> C<yes>/C<no> is exclusive, meaning that a rule for C</foo> with |
532
|
|
|
|
|
|
|
C<apply_to_subpages> set to C<yes> will apply to C</foo/bar> but not to C</foo> |
533
|
|
|
|
|
|
|
alone. The endpoint in the path is always checked for a rule explicitly for that |
534
|
|
|
|
|
|
|
page - meaning C<apply_to_subpages = no>. |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=back |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=cut |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
sub get_permissions_data { |
541
|
117
|
|
|
117
|
1
|
453
|
my ($c, $current_path, $paths_to_check, $role_ids) = @_; |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
# default to roles for current user |
544
|
117
|
|
33
|
|
|
494
|
$role_ids ||= $c->user_role_ids($c->user); |
545
|
|
|
|
|
|
|
|
546
|
117
|
|
|
|
|
258
|
my $permdata; |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
## Now that we have our path elements to check, we have to figure out how we are accessing them. |
549
|
|
|
|
|
|
|
## If we have caching turned on, we load the perms from the cache and walk the tree. |
550
|
|
|
|
|
|
|
## Otherwise we pull what we need out of the DB. The structure is: |
551
|
|
|
|
|
|
|
# $permdata{$pagepath} = { |
552
|
|
|
|
|
|
|
# admin => { |
553
|
|
|
|
|
|
|
# page => { |
554
|
|
|
|
|
|
|
# create => 'yes', |
555
|
|
|
|
|
|
|
# delete => 'yes', |
556
|
|
|
|
|
|
|
# view => 'yes', |
557
|
|
|
|
|
|
|
# edit => 'yes', |
558
|
|
|
|
|
|
|
# attachment => 'yes', |
559
|
|
|
|
|
|
|
# }, |
560
|
|
|
|
|
|
|
# subpages => { |
561
|
|
|
|
|
|
|
# create => 'yes', |
562
|
|
|
|
|
|
|
# delete => 'yes', |
563
|
|
|
|
|
|
|
# view => 'yes', |
564
|
|
|
|
|
|
|
# edit => 'yes', |
565
|
|
|
|
|
|
|
# attachment => 'yes', |
566
|
|
|
|
|
|
|
# }, |
567
|
|
|
|
|
|
|
# }, |
568
|
|
|
|
|
|
|
# users => ..... |
569
|
|
|
|
|
|
|
# } |
570
|
117
|
100
|
|
|
|
485
|
if ($c->pref('cache_permission_data')) { |
571
|
113
|
|
|
|
|
22613
|
$permdata = $c->cache->get('page_permission_data'); |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
# If we don't have any permissions data, we have a problem. We need to load it. |
575
|
|
|
|
|
|
|
# We have two options here - if we are caching, we will load everything and cache it. |
576
|
|
|
|
|
|
|
# If we are not - then we load just the bits we need. |
577
|
117
|
100
|
|
|
|
24813
|
if (!$permdata) { |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
# Initialize $permdata as a reference or we end up with an error |
580
|
|
|
|
|
|
|
# when we try to dereference it further down. The error we're avoiding is: |
581
|
|
|
|
|
|
|
# Can't use string ("") as a HASH ref while "strict refs" |
582
|
26
|
|
|
|
|
80
|
$permdata = {}; |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
## Either the data hasn't been loaded, or it's expired since we used it last, |
585
|
|
|
|
|
|
|
## so we need to reload it. |
586
|
26
|
|
|
|
|
142
|
my $rs = $c->model('DBIC::PathPermissions') |
587
|
|
|
|
|
|
|
->search(undef, {order_by => 'length(path),role,apply_to_subpages'}); |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
# If we are not caching, we don't return the whole enchilada. |
590
|
26
|
100
|
|
|
|
22642
|
if (!$c->pref('cache_permission_data')) { |
591
|
|
|
|
|
|
|
## this seems odd to me - but that's what the DBIx::Class says to do. |
592
|
4
|
50
|
|
|
|
743
|
$rs = $rs->search({role => $role_ids}) if $role_ids; |
593
|
4
|
|
|
|
|
809
|
$rs = $rs->search( |
594
|
|
|
|
|
|
|
{ |
595
|
|
|
|
|
|
|
'-or' => [ |
596
|
|
|
|
|
|
|
{path => $paths_to_check, apply_to_subpages => 'yes'}, |
597
|
|
|
|
|
|
|
{path => $current_path, apply_to_subpages => 'no'} |
598
|
|
|
|
|
|
|
] |
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
); |
601
|
|
|
|
|
|
|
} |
602
|
26
|
|
|
|
|
7344
|
$rs->result_class('DBIx::Class::ResultClass::HashRefInflator'); |
603
|
|
|
|
|
|
|
|
604
|
26
|
|
|
|
|
1213
|
my $recordtype; |
605
|
26
|
|
|
|
|
212
|
while (my $record = $rs->next) { |
606
|
228
|
100
|
|
|
|
89043
|
if ($record->{'apply_to_subpages'} eq 'yes') { |
607
|
116
|
|
|
|
|
233
|
$recordtype = 'subpages'; |
608
|
|
|
|
|
|
|
} |
609
|
|
|
|
|
|
|
else { |
610
|
112
|
|
|
|
|
224
|
$recordtype = 'page'; |
611
|
|
|
|
|
|
|
} |
612
|
228
|
|
|
|
|
1800
|
%{$permdata->{$record->{'path'}}{$record->{'role'}}{$recordtype}} |
613
|
228
|
|
|
|
|
420
|
= map { $_ => $record->{$_ . "_allowed"} } |
|
1140
|
|
|
|
|
2410
|
|
614
|
|
|
|
|
|
|
qw/create edit view delete attachment/; |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
} |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
## now we re-cache it - if we need to. # !$c->cache('memory')->exists('page_permission_data') |
619
|
117
|
100
|
|
|
|
4923
|
if ($c->pref('cache_permission_data')) { |
620
|
113
|
|
|
|
|
20940
|
$c->cache->set('page_permission_data', $permdata); |
621
|
|
|
|
|
|
|
} |
622
|
|
|
|
|
|
|
|
623
|
117
|
|
|
|
|
31224
|
return $permdata; |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=head2 user_role_ids |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
Get the list of role ids for a user. |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
=cut |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
sub user_role_ids { |
633
|
117
|
|
|
117
|
1
|
348
|
my ($c, $user) = @_; |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
## always use role_id 0 - which is default role and includes everyone. |
636
|
117
|
|
|
|
|
311
|
my @role_ids = (0); |
637
|
|
|
|
|
|
|
|
638
|
117
|
50
|
|
|
|
462
|
if (ref($user)) { |
639
|
117
|
|
|
|
|
2706
|
push @role_ids, map { $_->role->id } $user->role_members->all; |
|
117
|
|
|
|
|
409072
|
|
640
|
|
|
|
|
|
|
} |
641
|
|
|
|
|
|
|
|
642
|
117
|
|
|
|
|
774688
|
return @role_ids; |
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=head2 check_permissions |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
Check user permissions for a path. |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
=cut |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
sub check_permissions { |
652
|
191
|
|
|
191
|
1
|
125073
|
my ($c, $path, $user) = @_; |
653
|
|
|
|
|
|
|
|
654
|
191
|
100
|
100
|
|
|
1689
|
return {attachment => 1, create => 1, delete => 1, edit => 1, view => 1,} |
655
|
|
|
|
|
|
|
if ($user && $user->is_admin); |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
# if no user is logged in |
658
|
117
|
100
|
|
|
|
432
|
if (not $user) { |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
# if anonymous user is allowed |
661
|
106
|
|
|
|
|
394
|
my $anonymous = $c->pref('anonymous_user'); |
662
|
106
|
50
|
|
|
|
20288
|
if ($anonymous) { |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
# get anonymous user for no logged-in users |
665
|
106
|
|
|
|
|
585
|
$user = $c->model('DBIC::Person')->search({login => $anonymous})->first; |
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
} |
668
|
|
|
|
|
|
|
|
669
|
117
|
|
|
|
|
428752
|
my @paths_to_check = $c->_expand_path_elements($path); |
670
|
117
|
|
|
|
|
294
|
my $current_path = $paths_to_check[-1]; |
671
|
|
|
|
|
|
|
|
672
|
117
|
|
|
|
|
482
|
my @role_ids = $c->user_role_ids($user); |
673
|
|
|
|
|
|
|
|
674
|
117
|
|
|
|
|
9640
|
my $permdata |
675
|
|
|
|
|
|
|
= $c->get_permissions_data($current_path, \@paths_to_check, \@role_ids); |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
# rules comparison hash |
678
|
|
|
|
|
|
|
# allow everything by default |
679
|
117
|
|
|
|
|
509
|
my %rulescomparison = ( |
680
|
|
|
|
|
|
|
'create' => { |
681
|
|
|
|
|
|
|
'allowed' => $c->pref('create_allowed'), |
682
|
|
|
|
|
|
|
'role' => '__default', |
683
|
|
|
|
|
|
|
'len' => 0, |
684
|
|
|
|
|
|
|
}, |
685
|
|
|
|
|
|
|
'delete' => { |
686
|
|
|
|
|
|
|
'allowed' => $c->pref('delete_allowed'), |
687
|
|
|
|
|
|
|
'role' => '__default', |
688
|
|
|
|
|
|
|
'len' => 0, |
689
|
|
|
|
|
|
|
}, |
690
|
|
|
|
|
|
|
'edit' => { |
691
|
|
|
|
|
|
|
'allowed' => $c->pref('edit_allowed'), |
692
|
|
|
|
|
|
|
'role' => '__default', |
693
|
|
|
|
|
|
|
'len' => 0, |
694
|
|
|
|
|
|
|
}, |
695
|
|
|
|
|
|
|
'view' => { |
696
|
|
|
|
|
|
|
'allowed' => $c->pref('view_allowed'), |
697
|
|
|
|
|
|
|
'role' => '__default', |
698
|
|
|
|
|
|
|
'len' => 0, |
699
|
|
|
|
|
|
|
}, |
700
|
|
|
|
|
|
|
'attachment' => { |
701
|
|
|
|
|
|
|
'allowed' => $c->pref('attachment_allowed'), |
702
|
|
|
|
|
|
|
'role' => '__default', |
703
|
|
|
|
|
|
|
'len' => 0, |
704
|
|
|
|
|
|
|
}, |
705
|
|
|
|
|
|
|
); |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
## The outcome of this loop is a combined permission set. |
708
|
|
|
|
|
|
|
## The rule orders are essentially based on how specific the path |
709
|
|
|
|
|
|
|
## match is. More specific paths override less specific paths. |
710
|
|
|
|
|
|
|
## When conflicting rules at the same level of path hierarchy |
711
|
|
|
|
|
|
|
## (with different roles) are discovered, the grant is given precedence |
712
|
|
|
|
|
|
|
## over the deny. Note that more-specific denies will still |
713
|
|
|
|
|
|
|
## override. |
714
|
117
|
|
|
|
|
22452
|
my $permtype = 'subpages'; |
715
|
117
|
|
|
|
|
554
|
foreach my $i (0 .. $#paths_to_check) { |
716
|
171
|
|
|
|
|
425
|
my $path = $paths_to_check[$i]; |
717
|
171
|
100
|
|
|
|
657
|
if ($i == $#paths_to_check) { |
718
|
117
|
|
|
|
|
298
|
$permtype = 'page'; |
719
|
|
|
|
|
|
|
} |
720
|
171
|
|
|
|
|
460
|
foreach my $role (@role_ids) { |
721
|
342
|
50
|
66
|
|
|
1932
|
if ( exists($permdata->{$path}) |
|
|
|
66
|
|
|
|
|
722
|
|
|
|
|
|
|
&& exists($permdata->{$path}{$role}) |
723
|
|
|
|
|
|
|
&& exists($permdata->{$path}{$role}{$permtype})) |
724
|
|
|
|
|
|
|
{ |
725
|
|
|
|
|
|
|
|
726
|
119
|
|
|
|
|
295
|
my $len = length($path); |
727
|
|
|
|
|
|
|
|
728
|
119
|
|
|
|
|
271
|
foreach my $perm (keys %{$permdata->{$path}{$role}{$permtype}}) { |
|
119
|
|
|
|
|
570
|
|
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
## if the xxxx_allowed column is null, this permission is ignored. |
731
|
595
|
50
|
|
|
|
1543
|
if (defined($permdata->{$path}{$role}{$permtype}{$perm})) { |
732
|
595
|
50
|
|
|
|
1907
|
if ($len == $rulescomparison{$perm}{'len'}) { |
|
|
50
|
|
|
|
|
|
733
|
0
|
0
|
|
|
|
0
|
if ($permdata->{$path}{$role}{$permtype}{$perm} eq 'yes') { |
734
|
0
|
|
|
|
|
0
|
$rulescomparison{$perm}{'allowed'} = 1; |
735
|
0
|
|
|
|
|
0
|
$rulescomparison{$perm}{'len'} = $len; |
736
|
0
|
|
|
|
|
0
|
$rulescomparison{$perm}{'role'} = $role; |
737
|
|
|
|
|
|
|
} |
738
|
|
|
|
|
|
|
} |
739
|
|
|
|
|
|
|
elsif ($len > $rulescomparison{$perm}{'len'}) { |
740
|
595
|
100
|
|
|
|
1444
|
if ($permdata->{$path}{$role}{$permtype}{$perm} eq 'yes') { |
741
|
365
|
|
|
|
|
708
|
$rulescomparison{$perm}{'allowed'} = 1; |
742
|
|
|
|
|
|
|
} |
743
|
|
|
|
|
|
|
else { |
744
|
230
|
|
|
|
|
463
|
$rulescomparison{$perm}{'allowed'} = 0; |
745
|
|
|
|
|
|
|
} |
746
|
595
|
|
|
|
|
960
|
$rulescomparison{$perm}{'len'} = $len; |
747
|
595
|
|
|
|
|
1412
|
$rulescomparison{$perm}{'role'} = $role; |
748
|
|
|
|
|
|
|
} |
749
|
|
|
|
|
|
|
} |
750
|
|
|
|
|
|
|
} |
751
|
|
|
|
|
|
|
} |
752
|
|
|
|
|
|
|
} |
753
|
|
|
|
|
|
|
} |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
my %perms |
756
|
117
|
|
|
|
|
474
|
= map { $_ => $rulescomparison{$_}{'allowed'} } keys %rulescomparison; |
|
585
|
|
|
|
|
1413
|
|
757
|
|
|
|
|
|
|
|
758
|
117
|
|
|
|
|
3018
|
return \%perms; |
759
|
|
|
|
|
|
|
} |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
=head2 check_view_permission |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
Check if a user can view a path. |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
=cut |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
sub check_view_permission { |
768
|
36
|
|
|
36
|
1
|
94
|
my $c = shift; |
769
|
|
|
|
|
|
|
|
770
|
36
|
50
|
|
|
|
190
|
return 1 unless $c->pref('check_permission_on_view'); |
771
|
|
|
|
|
|
|
|
772
|
36
|
|
|
|
|
7490
|
my $user; |
773
|
36
|
100
|
|
|
|
216
|
if ($c->user_exists()) { |
774
|
14
|
|
|
|
|
1948
|
$user = $c->user->obj; |
775
|
|
|
|
|
|
|
} |
776
|
|
|
|
|
|
|
|
777
|
36
|
50
|
|
|
|
24153
|
$c->log->info('Checking permissions') if $c->debug; |
778
|
|
|
|
|
|
|
|
779
|
36
|
|
|
|
|
303
|
my $perms = $c->check_permissions($c->stash->{path}, $user); |
780
|
36
|
50
|
|
|
|
999
|
if (!$perms->{view}) { |
781
|
|
|
|
|
|
|
$c->stash->{message} |
782
|
0
|
|
|
|
|
0
|
= $c->loc('Permission Denied to view x', $c->stash->{page}->name); |
783
|
0
|
|
|
|
|
0
|
$c->stash->{template} = 'message.tt'; |
784
|
0
|
|
|
|
|
0
|
return; |
785
|
|
|
|
|
|
|
} |
786
|
|
|
|
|
|
|
|
787
|
36
|
|
|
|
|
258
|
return 1; |
788
|
|
|
|
|
|
|
} |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
my $search_setup_failed = 0; |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
MojoMojo->config->{index_dir} ||= MojoMojo->path_to('index'); |
793
|
|
|
|
|
|
|
MojoMojo->config->{attachment_dir} ||= MojoMojo->path_to('uploads'); |
794
|
|
|
|
|
|
|
MojoMojo->config->{root} ||= MojoMojo->path_to('root'); |
795
|
|
|
|
|
|
|
unless (-e MojoMojo->config->{index_dir}) { |
796
|
|
|
|
|
|
|
if (not mkdir MojoMojo->config->{index_dir}) { |
797
|
|
|
|
|
|
|
warn 'Could not make index directory <' |
798
|
|
|
|
|
|
|
. MojoMojo->config->{index_dir} |
799
|
|
|
|
|
|
|
. '> - FIX IT OR SEARCH WILL NOT WORK!'; |
800
|
|
|
|
|
|
|
$search_setup_failed = 1; |
801
|
|
|
|
|
|
|
} |
802
|
|
|
|
|
|
|
} |
803
|
|
|
|
|
|
|
unless (-w MojoMojo->config->{index_dir}) { |
804
|
|
|
|
|
|
|
warn 'Require write access to index <' |
805
|
|
|
|
|
|
|
. MojoMojo->config->{index_dir} |
806
|
|
|
|
|
|
|
. '> - FIX IT OR SEARCH WILL NOT WORK!'; |
807
|
|
|
|
|
|
|
$search_setup_failed = 1; |
808
|
|
|
|
|
|
|
} |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
MojoMojo->model('Search')->prepare_search_index() |
811
|
|
|
|
|
|
|
if not -f MojoMojo->config->{index_dir} . '/segments' |
812
|
|
|
|
|
|
|
and not $search_setup_failed |
813
|
|
|
|
|
|
|
and not MojoMojo->pref('disable_search'); |
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
unless (-e MojoMojo->config->{attachment_dir}) { |
816
|
|
|
|
|
|
|
mkdir MojoMojo->config->{attachment_dir} |
817
|
|
|
|
|
|
|
or die 'Could not make attachment directory <' |
818
|
|
|
|
|
|
|
. MojoMojo->config->{attachment_dir} . '>'; |
819
|
|
|
|
|
|
|
} |
820
|
|
|
|
|
|
|
die 'Require write access to attachment_dir: <' |
821
|
|
|
|
|
|
|
. MojoMojo->config->{attachment_dir} . '>' |
822
|
|
|
|
|
|
|
unless -w MojoMojo->config->{attachment_dir}; |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
1; |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
=head1 SUPPORT |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
=over |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
=item * |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
L<http://mojomojo.org> |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
=item * |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
IRC: L<irc://irc.perl.org/mojomojo>. |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
=item * |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
Mailing list: L<http://mojomojo.2358427.n2.nabble.com/> |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
=item * |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
Commercial support and customization for MojoMojo is also provided by Nordaaker |
845
|
|
|
|
|
|
|
Ltd. Contact C<arneandmarcus@nordaaker.com> for details. |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
=back |
848
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
=head1 AUTHORS |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
Marcus Ramberg C<marcus@nordaaker.com> |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
David Naughton C<naughton@umn.edu> |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
Andy Grundman C<andy@hybridized.org> |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
Jonathan Rockway C<jrockway@jrockway.us> |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
A number of other contributors over the years: |
860
|
|
|
|
|
|
|
https://www.ohloh.net/p/mojomojo/contributors |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
=head1 COPYRIGHT |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
Unless explicitly stated otherwise, all modules and scripts in this distribution are: |
865
|
|
|
|
|
|
|
Copyright 2005-2010, Marcus Ramberg |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
=head1 LICENSE |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
You may distribute this code under the same terms as Perl itself. |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
=cut |