line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Bryar::DataSource::FlatFile; |
2
|
3
|
|
|
3
|
|
840
|
use Cwd; |
|
3
|
|
|
|
|
11
|
|
|
3
|
|
|
|
|
299
|
|
3
|
3
|
|
|
3
|
|
21
|
use File::Basename; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
242
|
|
4
|
3
|
|
|
3
|
|
1004
|
use Bryar::Document; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
56
|
|
5
|
3
|
|
|
3
|
|
3323
|
use File::Find::Rule; |
|
3
|
|
|
|
|
35538
|
|
|
3
|
|
|
|
|
29
|
|
6
|
3
|
|
|
3
|
|
243
|
use 5.006; |
|
3
|
|
|
|
|
13
|
|
|
3
|
|
|
|
|
115
|
|
7
|
3
|
|
|
3
|
|
17
|
use strict; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
440
|
|
8
|
3
|
|
|
3
|
|
20
|
use warnings; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
127
|
|
9
|
3
|
|
|
3
|
|
19
|
use Carp; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
9246
|
|
10
|
|
|
|
|
|
|
our $VERSION = '1.2'; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
my %UID_Cache; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 NAME |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
Bryar::DataSource::FlatFile - Blog entries from flat files, a la blosxom |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 SYNOPSIS |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
$self->all_documents(...); |
21
|
|
|
|
|
|
|
$self->search(...); |
22
|
|
|
|
|
|
|
$self->add_comment(...); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 DESCRIPTION |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
Just like C, this data source pulls blog entries out of flat |
27
|
|
|
|
|
|
|
files in the file system. |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head1 METHODS |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head2 all_documents |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
$self->all_documents |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
Returns all documents making up the blog. |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=cut |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub all_documents { |
40
|
|
|
|
|
|
|
# my ($self, $config) = @_; |
41
|
|
|
|
|
|
|
# croak "Must pass in a Bryar::Config object" unless UNIVERSAL::isa($config, "Bryar::Config"); |
42
|
|
|
|
|
|
|
# my $where = getcwd; |
43
|
|
|
|
|
|
|
# chdir($config->datadir); # Damn you, F::F::R. |
44
|
|
|
|
|
|
|
# my @docs = map { $self->make_document($_) } |
45
|
|
|
|
|
|
|
# File::Find::Rule->file() |
46
|
|
|
|
|
|
|
# ->name($self->entry_glob) |
47
|
|
|
|
|
|
|
# ->maxdepth($config->depth) |
48
|
|
|
|
|
|
|
# ->in("."); |
49
|
|
|
|
|
|
|
# chdir($where); |
50
|
|
|
|
|
|
|
# return @docs; |
51
|
2
|
|
|
2
|
1
|
7
|
my ($self, $config) = @_; |
52
|
2
|
50
|
|
|
|
15
|
croak "Must pass in a Bryar::Config object" unless UNIVERSAL::isa($config, "Bryar::Config"); |
53
|
2
|
|
|
|
|
13
|
my @docs = sort { $b->epoch() <=> $a->epoch() } $self->search($config); |
|
2
|
|
|
|
|
10
|
|
54
|
2
|
|
|
|
|
23
|
return @docs; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head2 all_but_recent |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
$self->all_but_recent |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
Return all documented except recent() ones. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=cut |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub all_but_recent { |
66
|
0
|
|
|
0
|
1
|
0
|
my ($self, $config) = @_; |
67
|
0
|
0
|
|
|
|
0
|
croak "Must pass in a Bryar::Config object" unless UNIVERSAL::isa($config, "Bryar::Config"); |
68
|
0
|
|
|
|
|
0
|
my @docs = sort { $b->epoch() <=> $a->epoch() } $self->search($config); |
|
0
|
|
|
|
|
0
|
|
69
|
0
|
|
|
|
|
0
|
return @docs[$config->recent() .. $#docs]; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=head2 entry_glob |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
Returns a glob pattern which matches blog posts. This defaults to C<*.txt>. |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=cut |
77
|
|
|
|
|
|
|
|
78
|
3
|
|
|
3
|
1
|
19
|
sub entry_glob { "*.txt" } |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=head2 id_to_file |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
Takes a Bryar ID, converts it to a file name. |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=head2 file_to_id |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
Vice versa. |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=cut |
89
|
|
|
|
|
|
|
|
90
|
1
|
|
|
1
|
1
|
6
|
sub id_to_file { return $_[1].".txt" } |
91
|
6
|
|
|
6
|
1
|
13
|
sub file_to_id { my $file = $_[1]; $file =~ s/.txt$//; $file; } |
|
6
|
|
|
|
|
34
|
|
|
6
|
|
|
|
|
20
|
|
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=head2 search |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
$self->search($bryar, $config, %params) |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
A more advanced search for specific documents |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=cut |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
sub search { |
102
|
3
|
|
|
3
|
1
|
9
|
my ($self, $config, %params) = @_; |
103
|
3
|
50
|
|
|
|
19
|
croak "Must pass in a Bryar::Config object" unless UNIVERSAL::isa($config, "Bryar::Config"); |
104
|
3
|
|
|
|
|
32
|
my $was = getcwd; |
105
|
3
|
|
|
|
|
19
|
my $where = $config->datadir."/"; |
106
|
3
|
50
|
|
|
|
12
|
if ($params{subblog}) { $where .= $params{subblog}; } |
|
0
|
|
|
|
|
0
|
|
107
|
3
|
|
|
|
|
87
|
chdir($where); # Damn you, F::F::R. |
108
|
|
|
|
|
|
|
|
109
|
3
|
|
|
|
|
120
|
my $find = File::Find::Rule->file(); |
110
|
3
|
50
|
|
|
|
183
|
if ($params{id}) { $find->name($self->id_to_file($params{id})) } |
|
0
|
|
|
|
|
0
|
|
111
|
3
|
|
|
|
|
13
|
else { $find->name($self->entry_glob) } |
112
|
3
|
|
|
|
|
465
|
$find->maxdepth($config->depth); |
113
|
3
|
50
|
|
|
|
35
|
if ($params{since}) { $find->mtime(">".$params{since}) } |
|
0
|
|
|
|
|
0
|
|
114
|
3
|
50
|
|
|
|
12
|
if ($params{before}) { $find->mtime("<".$params{before}) } |
|
0
|
|
|
|
|
0
|
|
115
|
3
|
|
|
|
|
5
|
my @docs; |
116
|
3
|
|
|
|
|
14
|
local $/; |
117
|
3
|
100
|
|
|
|
19
|
if ($params{content}) { $find->grep(qr/\b\Q$params{content}\E\b/i) } |
|
1
|
|
|
|
|
26
|
|
118
|
|
|
|
|
|
|
|
119
|
3
|
|
|
|
|
51
|
@docs = sort { $b->epoch() <=> $a->epoch() } grep { $_->epoch() <= time () } map { $self->make_document($_) } $find->in("."); |
|
2
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
25
|
|
|
5
|
|
|
|
|
3829
|
|
120
|
3
|
|
33
|
|
|
24
|
$params{limit} ||= @docs; |
121
|
3
|
|
|
|
|
53
|
chdir($was); |
122
|
3
|
|
|
|
|
17
|
return grep { defined } @docs[0..$params{limit}-1]; |
|
5
|
|
|
|
|
55
|
|
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=head2 make_document |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
Turns a filename into a C, by parsing the file |
128
|
|
|
|
|
|
|
blosxom-style. |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=cut |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
sub make_document { |
133
|
5
|
|
|
5
|
1
|
13
|
my ($self, $file) = @_; |
134
|
5
|
50
|
|
|
|
17
|
return unless $file; |
135
|
5
|
50
|
|
|
|
191
|
open(my($in), '<:utf8', $file) or return; |
136
|
5
|
|
|
|
|
44
|
my $when = (stat $in)[9]; |
137
|
5
|
|
|
|
|
24
|
local $/ = "\n"; |
138
|
5
|
|
|
|
|
12
|
my $fileuid = (stat _)[4]; |
139
|
5
|
|
|
|
|
8
|
my $who; |
140
|
5
|
100
|
|
|
|
21
|
if (exists $UID_Cache{$fileuid}) { |
141
|
3
|
|
|
|
|
7
|
$who = $UID_Cache{$fileuid}; |
142
|
|
|
|
|
|
|
} else { |
143
|
2
|
|
|
|
|
1844
|
$who = $UID_Cache{$fileuid} = getpwuid($fileuid); |
144
|
|
|
|
|
|
|
} |
145
|
5
|
|
|
|
|
61
|
my $title = <$in>; |
146
|
5
|
|
|
|
|
16
|
chomp $title; |
147
|
5
|
|
|
|
|
17
|
local $/; |
148
|
5
|
|
|
|
|
55
|
my $content = <$in>; |
149
|
5
|
|
|
|
|
1065
|
close $in; |
150
|
5
|
|
|
|
|
36
|
my $id = $self->file_to_id($file); |
151
|
|
|
|
|
|
|
|
152
|
5
|
|
|
|
|
13
|
my $comments = []; |
153
|
5
|
100
|
|
|
|
76
|
$comments = [_read_comments($id, $id.".comments") ] |
154
|
|
|
|
|
|
|
if -e $id.".comments"; |
155
|
|
|
|
|
|
|
|
156
|
5
|
|
|
|
|
210
|
my $dir = dirname($file); |
157
|
5
|
|
|
|
|
17
|
$dir =~ s{^\./?}{}; |
158
|
5
|
|
50
|
|
|
32
|
my $category = $dir || "main"; |
159
|
5
|
|
|
|
|
44
|
return Bryar::Document->new( |
160
|
|
|
|
|
|
|
title => $title, |
161
|
|
|
|
|
|
|
content => $content, |
162
|
|
|
|
|
|
|
epoch => $when, |
163
|
|
|
|
|
|
|
author => $who, |
164
|
|
|
|
|
|
|
id => $id, |
165
|
|
|
|
|
|
|
category => $category, |
166
|
|
|
|
|
|
|
comments => $comments |
167
|
|
|
|
|
|
|
); |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub _read_comments { |
171
|
3
|
|
|
3
|
|
9
|
my ($id, $file) = @_; |
172
|
3
|
50
|
|
|
|
130
|
open(COMMENTS, '<:utf8', $file) or die $!; |
173
|
3
|
|
|
|
|
12
|
local $/; |
174
|
|
|
|
|
|
|
# Watch carefully |
175
|
3
|
|
|
|
|
62
|
my $stuff = ; |
176
|
3
|
|
|
|
|
8
|
my @rv; |
177
|
3
|
|
|
|
|
51
|
for (split /-----\n/, $stuff) { |
178
|
109
|
|
|
|
|
409
|
push @rv, |
179
|
|
|
|
|
|
|
Bryar::Comment->new( |
180
|
|
|
|
|
|
|
id => $id, |
181
|
28
|
|
|
|
|
123
|
map {/^(\w+): (.*)/; $1 => $2 } split /\n/, $_ |
|
109
|
|
|
|
|
365
|
|
182
|
|
|
|
|
|
|
) |
183
|
|
|
|
|
|
|
} |
184
|
3
|
|
|
|
|
23
|
return @rv; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head2 add_comment |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
Class->add_comment($bryar, |
190
|
|
|
|
|
|
|
document => $doc, |
191
|
|
|
|
|
|
|
author => $author, |
192
|
|
|
|
|
|
|
url => $url, |
193
|
|
|
|
|
|
|
content => $content ); |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
Records the given comment details. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=cut |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub add_comment { |
200
|
1
|
|
|
1
|
1
|
3
|
my ($self, $config) = (shift, shift); |
201
|
1
|
|
|
|
|
25
|
my %params = @_; |
202
|
|
|
|
|
|
|
|
203
|
1
|
|
|
|
|
9
|
s/\n/\r/g for values %params; |
204
|
|
|
|
|
|
|
|
205
|
1
|
|
|
|
|
5
|
my @links = ("$params{url} $params{content}" =~ m!(http://)!g); |
206
|
1
|
50
|
|
|
|
13
|
if(@links > 3) { # more than three links is definitely spam |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
207
|
0
|
|
|
|
|
0
|
$config->frontend->report_error('Comment failure', 'Attempt to spam the journal.'); |
208
|
|
|
|
|
|
|
} elsif(length($params{content}) < 1) { # real content always has, errm, content |
209
|
0
|
|
|
|
|
0
|
$config->frontend->report_error('Comment failure', 'Attempt to post with no content.'); |
210
|
|
|
|
|
|
|
} elsif(@links) { |
211
|
0
|
|
|
|
|
0
|
my($email, $author) = map { # kill funny chars to avoid remote |
212
|
0
|
|
|
|
|
0
|
my $foo = $_; # execution in open(). Yuck. |
213
|
0
|
|
|
|
|
0
|
$foo =~ s/[^\w @]/_/g; |
214
|
0
|
|
|
|
|
0
|
$foo; |
215
|
|
|
|
|
|
|
} @params{qw(email author)}; |
216
|
0
|
0
|
|
|
|
0
|
open(MAIL, "| mail -s \"$email $author maybe tried to spam the journal\" ".$config->email()) |
217
|
|
|
|
|
|
|
or $config->frontend->report_error('Comment failure', "Cannot send mail: $!"); |
218
|
0
|
|
|
|
|
0
|
print MAIL "$_: $params{$_}\n" for keys %params; |
219
|
0
|
|
|
|
|
0
|
print MAIL "\nEnvironment\n"; |
220
|
0
|
|
|
|
|
0
|
print MAIL "$_: $ENV{$_}\n" for keys %ENV; |
221
|
0
|
0
|
|
|
|
0
|
close MAIL |
222
|
|
|
|
|
|
|
or $config->frontend->report_error('Comment failure', "Cannot send mail: $!"); |
223
|
|
|
|
|
|
|
# FIXME: this is not an error |
224
|
0
|
|
|
|
|
0
|
$config->frontend->report_error("Your comment is being held for approval."); |
225
|
|
|
|
|
|
|
} else { |
226
|
1
|
|
|
|
|
7
|
my $file = $params{document}->id.".comments"; |
227
|
1
|
50
|
33
|
|
|
8
|
$params{url} = "http://".$params{url} |
228
|
|
|
|
|
|
|
if($params{url} && $params{url} !~ /^http:\/\//); |
229
|
|
|
|
|
|
|
# This probably fails with subblogs, but I don't use them. |
230
|
1
|
|
|
|
|
6
|
chdir $config->datadir."/"; |
231
|
1
|
50
|
|
|
|
47
|
open(OUT, ">>:utf8", $file) |
232
|
|
|
|
|
|
|
or $config->frontend->report_error("Cannot open $file", $!); |
233
|
1
|
|
|
|
|
45
|
delete $params{document}; |
234
|
1
|
|
|
|
|
16
|
print OUT "$_: $params{$_}\n" for keys %params; |
235
|
1
|
|
|
|
|
4
|
print OUT "-----\n"; |
236
|
|
|
|
|
|
|
# Looks a bit like blosxom, doesn't it? |
237
|
1
|
|
|
|
|
52
|
close OUT; |
238
|
|
|
|
|
|
|
# now send mail |
239
|
1
|
50
|
|
|
|
6
|
open(MAIL, '| mail -s "Someone commented in the journal" '.$config->email()) |
240
|
|
|
|
|
|
|
or $config->frontend->report_error('Comment failure', "Cannot send mail: $!"); |
241
|
1
|
|
|
|
|
83
|
print MAIL "$_: $params{$_}\n" for keys %params; |
242
|
1
|
|
|
|
|
11
|
print MAIL "\nEnvironment\n"; |
243
|
1
|
|
|
|
|
81
|
print MAIL "$_: $ENV{$_}\n" for keys %ENV; |
244
|
1
|
50
|
|
|
|
1253
|
close MAIL |
245
|
|
|
|
|
|
|
or $config->frontend->report_error('Comment failure', "Cannot send mail: $!"); |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=head1 LICENSE |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
This module is free software, and may be distributed under the same |
252
|
|
|
|
|
|
|
terms as Perl itself. |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=head1 AUTHOR |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Copyright (C) 2003, Simon Cozens C |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
some parts Copyright 2007 David Cantrell C |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=cut |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
1; |