|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package BusyBird::Main::PSGI::View;  | 
| 
2
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
79672
 | 
 use v5.8.0;  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
293
 | 
    | 
| 
3
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
32
 | 
 use strict;  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
221
 | 
    | 
| 
4
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
123
 | 
 use warnings;  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
228
 | 
    | 
| 
5
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
37
 | 
 use BusyBird::Util qw(set_param split_with_entities);  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
385
 | 
    | 
| 
6
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
139
 | 
 use Carp;  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
415
 | 
    | 
| 
7
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
37
 | 
 use Try::Tiny;  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
425
 | 
    | 
| 
8
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
39
 | 
 use Scalar::Util qw(weaken);  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
365
 | 
    | 
| 
9
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
41
 | 
 use JSON qw(to_json);  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
57
 | 
    | 
| 
10
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
5478
 | 
 use Text::Xslate qw(html_builder html_escape);  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
71852
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
685
 | 
    | 
| 
11
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
74
 | 
 use File::Spec;  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
145
 | 
    | 
| 
12
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
42
 | 
 use Encode ();  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
105
 | 
    | 
| 
13
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
3935
 | 
 use JavaScript::Value::Escape ();  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4280
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
188
 | 
    | 
| 
14
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
42
 | 
 use DateTime::TimeZone;  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
162
 | 
    | 
| 
15
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
29
 | 
 use BusyBird::DateTime::Format;  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
136
 | 
    | 
| 
16
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
28
 | 
 use BusyBird::Log qw(bblog);  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
346
 | 
    | 
| 
17
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
38
 | 
 use BusyBird::SafeData qw(safed);  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
353
 | 
    | 
| 
18
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
3552
 | 
 use Cache::Memory::Simple;  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4817
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
194
 | 
    | 
| 
19
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
3614
 | 
 use Plack::Util ();  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
65631
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
226
 | 
    | 
| 
20
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
72
 | 
 use Tie::IxHash;  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
27031
 | 
    | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub new {  | 
| 
23
 | 
53
 | 
 
 | 
 
 | 
  
53
  
 | 
  
1
  
 | 
367
 | 
     my ($class, %args) = @_;  | 
| 
24
 | 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
277
 | 
     my $self = bless {  | 
| 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         main_obj => undef,  | 
| 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         renderer => undef,  | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }, $class;  | 
| 
28
 | 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
313
 | 
     $self->set_param(\%args, "main_obj", undef, 1);  | 
| 
29
 | 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
214
 | 
     $self->set_param(\%args, "script_name", undef, 1);  | 
| 
30
 | 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
281
 | 
     my $sharedir = $self->{main_obj}->get_config('sharedir_path');  | 
| 
31
 | 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
271
 | 
     $sharedir =~ s{/+$}{};  | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->{renderer} = Text::Xslate->new(  | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         path => [ File::Spec->catdir($sharedir, 'www', 'templates') ],  | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         cache_dir => File::Spec->tmpdir,  | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         syntax => 'Kolon',  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         function => $self->template_functions(),  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         warn_handler => sub {  | 
| 
38
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
             bblog("warn", @_);  | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         ## we don't use die_handler because (1) it is called for every  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         ## death even when it's in eval() scope, (2) exceptions are  | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         ## caught by Xslate and passed to warn_handler anyway.  | 
| 
43
 | 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1855
 | 
     );  | 
| 
44
 | 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16322
 | 
     return $self;  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub response_notfound {  | 
| 
48
 | 
15
 | 
 
 | 
 
 | 
  
15
  
 | 
  
1
  
 | 
391
 | 
     my ($self, $message) = @_;  | 
| 
49
 | 
15
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
67
 | 
     $message ||= 'Not Found';  | 
| 
50
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
187
 | 
     return ['404',  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ['Content-Type' => 'text/plain',  | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
              'Content-Length' => length($message)],  | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             [$message]];  | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub response_error_html {  | 
| 
57
 | 
9
 | 
 
 | 
 
 | 
  
9
  
 | 
  
1
  
 | 
3345
 | 
     my ($self, $http_code, $message) = @_;  | 
| 
58
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
55
 | 
     return $self->_response_template(  | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         template => 'error.tx', args => {error => $message},  | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         code => $http_code  | 
| 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub response_json {  | 
| 
66
 | 
147
 | 
 
 | 
 
 | 
  
147
  
 | 
  
1
  
 | 
293
 | 
     my ($self, $res_code, $response_object) = @_;  | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $message = try {  | 
| 
68
 | 
147
 | 
  
 50
  
 | 
 
 | 
  
147
  
 | 
 
 | 
4143
 | 
         die "response must not be undef" if not defined $response_object;  | 
| 
69
 | 
147
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
1525
 | 
         if($res_code eq '200' && ref($response_object) eq "HASH" && !exists($response_object->{error})) {  | 
| 
 
 | 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
70
 | 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
218
 | 
             $response_object->{error} = undef;  | 
| 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
72
 | 
147
 | 
 
 | 
 
 | 
 
 | 
 
 | 
814
 | 
         to_json($response_object, {ascii => 1})  | 
| 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }catch {  | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         undef  | 
| 
75
 | 
147
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
1073
 | 
     };  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
    | 
| 
76
 | 
147
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
7300
 | 
     if(defined($message)) {  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return [  | 
| 
78
 | 
146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1118
 | 
             $res_code, ['Content-Type' => 'application/json; charset=utf-8'],  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             [$message]  | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         ];  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }else {  | 
| 
82
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
         return $self->response_json(500, {error => "error while encoding to JSON."});  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _response_template {  | 
| 
87
 | 
36
 | 
 
 | 
 
 | 
  
36
  
 | 
 
 | 
141
 | 
     my ($self, %args) = @_;  | 
| 
88
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
100
 | 
     my $template_name = delete $args{template};  | 
| 
89
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
119
 | 
     croak 'template parameter is mandatory' if not defined $template_name;  | 
| 
90
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
72
 | 
     my $args = delete $args{args};  | 
| 
91
 | 
36
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
191
 | 
     my $code = delete $args{code} || 200;  | 
| 
92
 | 
36
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
185
 | 
     my $headers = delete $args{headers} || [];  | 
| 
93
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
187
 | 
     if(!Plack::Util::header_exists($headers, 'Content-Type')) {  | 
| 
94
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
973
 | 
         push(@$headers, 'Content-Type', 'text/html; charset=utf8');  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
96
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2229
 | 
     my $ret = Encode::encode('utf8', $self->{renderer}->render($template_name, $args));  | 
| 
97
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4374
 | 
     return [$code, $headers, [$ret]];  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $REGEXP_URL_CHAR = qr{[A-Za-z0-9~\/._!\?\&=\-%#\+:\;,\@\']};  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $REGEXP_HTTP_URL = qr{https?:\/\/$REGEXP_URL_CHAR+};  | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $REGEXP_ABSOLUTE_PATH = qr{/$REGEXP_URL_CHAR*};  | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my %URL_ATTRIBUTES = map { $_ => 1 } qw(src href);  | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _is_valid_link_url {  | 
| 
107
 | 
107
 | 
 
 | 
 
 | 
  
107
  
 | 
 
 | 
135
 | 
     my ($url) = @_;  | 
| 
108
 | 
107
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
219
 | 
     $url = "" if not defined $url;  | 
| 
109
 | 
107
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5137
 | 
     return ($url =~ /^(?:$REGEXP_HTTP_URL|$REGEXP_ABSOLUTE_PATH)$/);  | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _html_attributes_string {  | 
| 
113
 | 
108
 | 
 
 | 
 
 | 
  
108
  
 | 
 
 | 
244
 | 
     my ($mandatory_attrs_ref, @attr) = @_;  | 
| 
114
 | 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
303
 | 
     for(my $i = 0 ; $i < $#attr ; $i += 2) {  | 
| 
115
 | 
224
 | 
 
 | 
 
 | 
 
 | 
 
 | 
607
 | 
         $attr[$i] = lc($attr[$i]);  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
117
 | 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
754
 | 
     tie(my %attr, 'Tie::IxHash', @attr);  | 
| 
118
 | 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3570
 | 
     foreach my $attr_key (@$mandatory_attrs_ref) {  | 
| 
119
 | 
108
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
417
 | 
         croak "$attr_key attribute is mandatory" if not defined $attr{$attr_key};  | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
121
 | 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
516
 | 
     my @attr_strings = ();  | 
| 
122
 | 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
174
 | 
     foreach my $attr_key (keys %attr) {  | 
| 
123
 | 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
962
 | 
         my $attr_value = $attr{$attr_key};  | 
| 
124
 | 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
458
 | 
         my $value_str;  | 
| 
125
 | 
73
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
215
 | 
         if($URL_ATTRIBUTES{$attr_key}) {  | 
| 
126
 | 
55
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
97
 | 
             croak "$attr_key attribute is invalid as a URL" if not _is_valid_link_url($attr_value);  | 
| 
127
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
33
 | 
             $value_str = $attr_value;  | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }else {  | 
| 
129
 | 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
47
 | 
             $value_str = html_escape($attr_value);  | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
131
 | 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
302
 | 
         push(@attr_strings, html_escape($attr_key) . qq{="$value_str"});  | 
| 
132
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
133
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
140
 | 
     return join(" ", @attr_strings);  | 
| 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
136
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _html_link {  | 
| 
137
 | 
73
 | 
 
 | 
 
 | 
  
73
  
 | 
 
 | 
4206
 | 
     my ($text, @attr) = @_;  | 
| 
138
 | 
73
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
174
 | 
     $text = "" if not defined $text;  | 
| 
139
 | 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
248
 | 
     my $escaped_text = html_escape($text);  | 
| 
140
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return try {  | 
| 
141
 | 
73
 | 
 
 | 
 
 | 
  
73
  
 | 
 
 | 
2162
 | 
         my $attr_str = _html_attributes_string(['href'], @attr);  | 
| 
142
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
100
 | 
         return qq{$escaped_text};  | 
| 
143
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }catch {  | 
| 
144
 | 
54
 | 
 
 | 
 
 | 
  
54
  
 | 
 
 | 
5849
 | 
         return $escaped_text;  | 
| 
145
 | 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
496
 | 
     };  | 
| 
146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
147
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
148
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _html_link_status_text {  | 
| 
149
 | 
13
 | 
 
 | 
 
 | 
  
13
  
 | 
 
 | 
19
 | 
     my ($text, $url) = @_;  | 
| 
150
 | 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
     return _html_link($text, href => $url, target => "_blank");  | 
| 
151
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
152
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
153
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _make_path {  | 
| 
154
 | 
488
 | 
 
 | 
 
 | 
  
488
  
 | 
 
 | 
498
 | 
     my ($script_name, $given_path) = @_;  | 
| 
155
 | 
488
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
973
 | 
     if(substr($given_path, 0, 1) eq "/") {  | 
| 
156
 | 
486
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2841
 | 
         return "$script_name$given_path";  | 
| 
157
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }else {  | 
| 
158
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
         return $given_path;  | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub template_functions {  | 
| 
163
 | 
54
 | 
 
 | 
 
 | 
  
54
  
 | 
  
1
  
 | 
230
 | 
     my $script_name = $_[0]->{script_name};  | 
| 
164
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return {  | 
| 
165
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         js => \&JavaScript::Value::Escape::js,  | 
| 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         link => html_builder(\&_html_link),  | 
| 
167
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         image => html_builder {  | 
| 
168
 | 
35
 | 
 
 | 
 
 | 
  
35
  
 | 
 
 | 
14697
 | 
             my (@attr) = @_;  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             return try {  | 
| 
170
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1422
 | 
                 my $attr_str = _html_attributes_string(['src'], @attr);  | 
| 
171
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
                 return qq{ }  | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }catch {  | 
| 
173
 | 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4554
 | 
                 return "";  | 
| 
174
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
380
 | 
             };  | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         bb_level => sub {  | 
| 
177
 | 
54
 | 
 
 | 
 
 | 
  
54
  
 | 
 
 | 
21342
 | 
             my $level = shift;  | 
| 
178
 | 
54
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
163
 | 
             $level = 0 if not defined $level;  | 
| 
179
 | 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
257
 | 
             return $level;  | 
| 
180
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
181
 | 
322
 | 
 
 | 
 
 | 
  
322
  
 | 
 
 | 
28332
 | 
         path => sub { _make_path($script_name, $_[0]) },  | 
| 
182
 | 
55
 | 
 
 | 
 
 | 
  
55
  
 | 
 
 | 
1624
 | 
         script_name => sub { $script_name },  | 
| 
183
 | 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
409
 | 
     };  | 
| 
184
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
185
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _render_text_segment {  | 
| 
187
 | 
24
 | 
 
 | 
 
 | 
  
24
  
 | 
 
 | 
37
 | 
     my ($self, $timeline_name, $segment, $status) = @_;  | 
| 
188
 | 
24
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
106
 | 
     return undef if !defined($segment->{entity}) || !defined($segment->{type});  | 
| 
189
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
     my $url_builder = $self->{main_obj}->get_timeline_config($timeline_name, "$segment->{type}_entity_url_builder");  | 
| 
190
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
     my $text_builder = $self->{main_obj}->get_timeline_config($timeline_name, "$segment->{type}_entity_text_builder");  | 
| 
191
 | 
10
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
42
 | 
     return undef if !defined($url_builder) || !defined($text_builder);  | 
| 
192
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
23
 | 
     my $url_str = $url_builder->($segment->{text}, $segment->{entity}, $status);  | 
| 
193
 | 
10
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
22
 | 
     return undef if !_is_valid_link_url($url_str);  | 
| 
194
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
31
 | 
     my $text_str = $text_builder->($segment->{text}, $segment->{entity}, $status);  | 
| 
195
 | 
10
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
26
 | 
     $text_str = "" if not defined $text_str;  | 
| 
196
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
     return _html_link_status_text($text_str, $url_str);  | 
| 
197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
199
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub template_functions_for_timeline {  | 
| 
200
 | 
27
 | 
 
 | 
 
 | 
  
27
  
 | 
  
1
  
 | 
50
 | 
     my ($self, $timeline_name) = @_;  | 
| 
201
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
94
 | 
     weaken $self;  ## in case the functions are kept by $self  | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return {  | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         bb_timestamp => sub {  | 
| 
204
 | 
28
 | 
 
 | 
 
 | 
  
28
  
 | 
 
 | 
1709
 | 
             my ($timestamp_string) = @_;  | 
| 
205
 | 
28
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
144
 | 
             return "" if !$timestamp_string;  | 
| 
206
 | 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
81
 | 
             my $timezone = $self->_get_timezone($self->{main_obj}->get_timeline_config($timeline_name, "time_zone"));  | 
| 
207
 | 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3122
 | 
             my $dt = BusyBird::DateTime::Format->parse_datetime($timestamp_string);  | 
| 
208
 | 
12
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
14483
 | 
             return "" if !defined($dt);  | 
| 
209
 | 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
59
 | 
             $dt->set_time_zone($timezone);  | 
| 
210
 | 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
234
 | 
             $dt->set_locale($self->{main_obj}->get_timeline_config($timeline_name, "time_locale"));  | 
| 
211
 | 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1549
 | 
             return $dt->strftime($self->{main_obj}->get_timeline_config($timeline_name, "time_format"));  | 
| 
212
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
213
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         bb_status_permalink => sub {  | 
| 
214
 | 
31
 | 
 
 | 
 
 | 
  
31
  
 | 
 
 | 
2150
 | 
             my ($status) = @_;  | 
| 
215
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
129
 | 
             my $builder = $self->{main_obj}->get_timeline_config($timeline_name, "status_permalink_builder");  | 
| 
216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $url = try {  | 
| 
217
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1014
 | 
                 $builder->($status);  | 
| 
218
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }catch {  | 
| 
219
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 my ($e) = @_;  | 
| 
220
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 bblog("error", "Error in status_permalink_builder: $e");  | 
| 
221
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 undef;  | 
| 
222
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
260
 | 
             };  | 
| 
223
 | 
31
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
483
 | 
             return (_is_valid_link_url($url) ? $url : "");  | 
| 
224
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         bb_text => html_builder {  | 
| 
226
 | 
31
 | 
 
 | 
 
 | 
  
31
  
 | 
 
 | 
2461
 | 
             my $status = shift;  | 
| 
227
 | 
31
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
149
 | 
             return "" if not defined $status->{text};  | 
| 
228
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
31
 | 
             my $segments_ref = split_with_entities($status->{text}, $status->{entities});  | 
| 
229
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
             my $result_text = "";  | 
| 
230
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
             foreach my $segment (@$segments_ref) {  | 
| 
231
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 my $rendered_text = try {  | 
| 
232
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
714
 | 
                     $self->_render_text_segment($timeline_name, $segment, $status)  | 
| 
233
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 }catch {  | 
| 
234
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                     my ($e) = @_;  | 
| 
235
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                     bblog("error", "Error while rendering text: $e");  | 
| 
236
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                     undef;  | 
| 
237
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
158
 | 
                 };  | 
| 
238
 | 
24
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
461
 | 
                 $result_text .= defined($rendered_text) ? $rendered_text : _escape_and_linkify_status_text($segment->{text});  | 
| 
239
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
240
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
36
 | 
             return $result_text;  | 
| 
241
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
242
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         bb_attached_image_urls => sub {  | 
| 
243
 | 
31
 | 
 
 | 
 
 | 
  
31
  
 | 
 
 | 
2492
 | 
             my ($status) = @_;  | 
| 
244
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
128
 | 
             my $urls_builder = $self->{main_obj}->get_timeline_config($timeline_name, "attached_image_urls_builder");  | 
| 
245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my @image_urls = try {  | 
| 
246
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
972
 | 
                 $urls_builder->($status);  | 
| 
247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }catch {  | 
| 
248
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 my ($e) = @_;  | 
| 
249
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 bblog("error", "Error in attached_image_urls_builder: $e");  | 
| 
250
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 ();  | 
| 
251
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
254
 | 
             };  | 
| 
252
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2003
 | 
             return [grep { _is_valid_link_url($_) } @image_urls ];  | 
| 
 
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
    | 
| 
253
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         },  | 
| 
254
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
394
 | 
     };  | 
| 
255
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
256
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
257
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
258
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $timezone_cache = Cache::Memory::Simple->new();  | 
| 
259
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $CACHE_EXPIRATION_TIME = 3600 * 24;  | 
| 
260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $CACHE_SIZE_LIMIT = 100;  | 
| 
261
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     sub _get_timezone {  | 
| 
262
 | 
12
 | 
 
 | 
 
 | 
  
12
  
 | 
 
 | 
21
 | 
         my ($self, $timezone_string) = @_;  | 
| 
263
 | 
12
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
60
 | 
         if($timezone_cache->count > $CACHE_SIZE_LIMIT) {  | 
| 
264
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $timezone_cache->purge();  | 
| 
265
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             if($timezone_cache->count > $CACHE_SIZE_LIMIT) {  | 
| 
266
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 $timezone_cache->delete_all();  | 
| 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
268
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
269
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return $timezone_cache->get_or_set($timezone_string, sub {  | 
| 
270
 | 
3
 | 
 
 | 
 
 | 
  
3
  
 | 
 
 | 
68
 | 
             return DateTime::TimeZone->new(name => $timezone_string),  | 
| 
271
 | 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
178
 | 
         }, $CACHE_EXPIRATION_TIME);  | 
| 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
273
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
274
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _escape_and_linkify_status_text {  | 
| 
276
 | 
14
 | 
 
 | 
 
 | 
  
14
  
 | 
 
 | 
20
 | 
     my ($text) = @_;  | 
| 
277
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
     my $result_text = "";  | 
| 
278
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
     my $remaining_index = 0;  | 
| 
279
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
174
 | 
     while($text =~ m/\G(.*?)($REGEXP_HTTP_URL)/sg) {  | 
| 
280
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
         my ($other_text, $url) = ($1, $2);  | 
| 
281
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
         $result_text .= html_escape($other_text);  | 
| 
282
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
         $result_text .= _html_link_status_text($url, $url);  | 
| 
283
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
58
 | 
         $remaining_index = pos($text);  | 
| 
284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
285
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
146
 | 
     $result_text .= html_escape(substr($text, $remaining_index));  | 
| 
286
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
49
 | 
     return $result_text;  | 
| 
287
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
288
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
289
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _format_status_html_destructive {  | 
| 
290
 | 
25
 | 
 
 | 
 
 | 
  
25
  
 | 
 
 | 
49
 | 
     my ($self, $status, $timeline_name) = @_;  | 
| 
291
 | 
25
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
76
 | 
     $timeline_name = "" if not defined $timeline_name;  | 
| 
292
 | 
25
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
110
 | 
     if(ref($status->{retweeted_status}) eq "HASH" && (!defined($status->{busybird}) || ref($status->{busybird}) eq 'HASH')) {  | 
| 
 
 | 
 
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
293
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
         my $retweet = $status->{retweeted_status};  | 
| 
294
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
         $status->{busybird}{retweeted_by_user} = $status->{user};  | 
| 
295
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
         foreach my $key (qw(text created_at user entities)) {  | 
| 
296
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
             $status->{$key} = $retweet->{$key};  | 
| 
297
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
298
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
299
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
88
 | 
     return $self->{renderer}->render(  | 
| 
300
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         "status.tx",  | 
| 
301
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         {ss => safed($status),  | 
| 
302
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
135
 | 
          %{$self->template_functions_for_timeline($timeline_name)}}  | 
| 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
304
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
305
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
306
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my %RESPONSE_FORMATTER_FOR_TL_GET_STATUSES = (  | 
| 
307
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     html => sub {  | 
| 
308
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my ($self, $timeline_name, $code, $response_object) = @_;  | 
| 
309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if($code == 200) {  | 
| 
310
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $result = "";  | 
| 
311
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             foreach my $status (@{$response_object->{statuses}}) {  | 
| 
312
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $result .= $self->_format_status_html_destructive($status, $timeline_name);  | 
| 
313
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $result = Encode::encode('utf8', $result);  | 
| 
315
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             return [200, ['Content-Type', 'text/html; charset=utf8'], [$result]];  | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }else {  | 
| 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             return $self->response_error_html($code, $response_object->{error});  | 
| 
318
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
319
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     },  | 
| 
320
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     json => sub {  | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my ($self, $timeline_name, $code, $response_object) = @_;  | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return $self->response_json($code, $response_object);  | 
| 
323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     },  | 
| 
324
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     json_only_statuses => sub {  | 
| 
325
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my ($self, $timeline_name, $code, $response_object) = @_;  | 
| 
326
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $res_array = defined($response_object->{error}) ? [] : $response_object->{statuses};  | 
| 
327
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $res_msg = try {  | 
| 
328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             to_json($res_array, {ascii => 1});  | 
| 
329
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }catch {  | 
| 
330
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             "[]"  | 
| 
331
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         };  | 
| 
332
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return [  | 
| 
333
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $code, ['Content-Type' => 'application/json; charset=utf-8'],  | 
| 
334
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             [$res_msg]  | 
| 
335
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         ];  | 
| 
336
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     },  | 
| 
337
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
338
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
339
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub response_statuses {  | 
| 
340
 | 
87
 | 
 
 | 
 
 | 
  
87
  
 | 
  
1
  
 | 
24959
 | 
     my ($self, %args) = @_;  | 
| 
341
 | 
87
 | 
  
 50
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
377
 | 
     if(!defined($args{statuses}) && !defined($args{error})) {  | 
| 
342
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         croak "stautses or error parameter is mandatory";  | 
| 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
344
 | 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
222
 | 
     foreach my $param_key (qw(http_code format)) {  | 
| 
345
 | 
174
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
525
 | 
         croak "$param_key parameter is mandatory" if not defined($args{$param_key});  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
347
 | 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
312
 | 
     my $formatter = $RESPONSE_FORMATTER_FOR_TL_GET_STATUSES{lc($args{format})};  | 
| 
348
 | 
87
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
229
 | 
     if(!defined($formatter)) {  | 
| 
349
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
         $formatter = $RESPONSE_FORMATTER_FOR_TL_GET_STATUSES{html};  | 
| 
350
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
         delete $args{statuses};  | 
| 
351
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
         $args{error} = "Unknown format: $args{format}";  | 
| 
352
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
         $args{http_code} = 400;  | 
| 
353
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
354
 | 
87
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
758
 | 
     return $formatter->($self, $args{timeline_name}, $args{http_code},  | 
| 
355
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                         defined($args{error}) ? {error => $args{error}}  | 
| 
356
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                                               : {error => undef, statuses => $args{statuses}});  | 
| 
357
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
358
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
359
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my %TIMELINE_CONFIG_FILTER_FOR = (  | 
| 
360
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     timeline_web_notifications => sub { defined($_[0]) ? "$_[0]" : ""},  | 
| 
361
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     acked_statuses_load_count => sub { $_[0] =~ /^\d+$/ ? $_[0] : undef },  | 
| 
362
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     default_level_threshold => sub { $_[0] =~ /^\d+$/ ? $_[0] : undef},  | 
| 
363
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
364
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
365
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _create_timeline_config_json {  | 
| 
366
 | 
13
 | 
 
 | 
 
 | 
  
13
  
 | 
 
 | 
24
 | 
     my ($self, $timeline_name) = @_;  | 
| 
367
 | 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
27
 | 
     my %config = ();  | 
| 
368
 | 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
48
 | 
     foreach my $key (keys %TIMELINE_CONFIG_FILTER_FOR) {  | 
| 
369
 | 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
57
 | 
         my $config_filter = $TIMELINE_CONFIG_FILTER_FOR{$key};  | 
| 
370
 | 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
104
 | 
         my $orig_value = $self->{main_obj}->get_timeline_config($timeline_name, $key);  | 
| 
371
 | 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
97
 | 
         $config{$key} = $config_filter->($orig_value);  | 
| 
372
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
373
 | 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
75
 | 
     return to_json(\%config);  | 
| 
374
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
375
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
376
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub response_timeline {  | 
| 
377
 | 
21
 | 
 
 | 
 
 | 
  
21
  
 | 
  
1
  
 | 
767
 | 
     my ($self, $timeline_name) = @_;  | 
| 
378
 | 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
124
 | 
     my $timeline = $self->{main_obj}->get_timeline($timeline_name);  | 
| 
379
 | 
21
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
323
 | 
     return $self->response_notfound("Cannot find $timeline_name") if not defined($timeline);  | 
| 
380
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
| 
381
 | 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
58
 | 
     return $self->_response_template(  | 
| 
382
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         template => "timeline.tx",  | 
| 
383
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         args => {  | 
| 
384
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             timeline_name => $timeline_name,  | 
| 
385
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             timeline_config_json => $self->_create_timeline_config_json($timeline_name),  | 
| 
386
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             post_button_url => $self->{main_obj}->get_timeline_config($timeline_name, "post_button_url"),  | 
| 
387
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             attached_image_max_height => $self->{main_obj}->get_timeline_config($timeline_name, "attached_image_max_height"),  | 
| 
388
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             attached_image_show_default_bool =>  | 
| 
389
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ($self->{main_obj}->get_timeline_config($timeline_name, "attached_image_show_default") eq "visible")  | 
| 
390
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
391
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
393
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
394
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub response_timeline_list {  | 
| 
395
 | 
14
 | 
 
 | 
 
 | 
  
14
  
 | 
  
1
  
 | 
314451
 | 
     my ($self, %args) = @_;  | 
| 
396
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
52
 | 
     foreach my $key (qw(timeline_unacked_counts total_page_num cur_page)) {  | 
| 
397
 | 
42
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
169
 | 
         croak "$key parameter is mandatory" if not defined $args{$key};  | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
399
 | 
14
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
72
 | 
     croak "timeline_unacked_counts must be an array-ref" if ref($args{timeline_unacked_counts}) ne "ARRAY";  | 
| 
400
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
| 
401
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
111
 | 
     my %input_args = (last_page => $args{total_page_num} - 1);  | 
| 
402
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
30
 | 
     foreach my $input_key (qw(cur_page)) {  | 
| 
403
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
49
 | 
         $input_args{$input_key} = $args{$input_key};  | 
| 
404
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
405
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
| 
406
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
639
 | 
     $input_args{timeline_unacked_counts_json} = [map {  | 
| 
407
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
38
 | 
         +{name => $_->{name}, counts_json => to_json($_->{counts})}  | 
| 
408
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
     } @{$args{timeline_unacked_counts}}];  | 
| 
409
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
| 
410
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
390
 | 
     my $pager_entry_max = $self->{main_obj}->get_config('timeline_list_pager_entry_max');  | 
| 
411
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
59
 | 
     my $left_margin = int($pager_entry_max / 2);  | 
| 
412
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
30
 | 
     my $right_margin = $pager_entry_max - $left_margin;  | 
| 
413
 | 
14
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
106
 | 
     $input_args{page_list} =  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           $args{total_page_num} <= $pager_entry_max ? [0 .. ($args{total_page_num} - 1)]  | 
| 
415
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         : $args{cur_page} <= $left_margin           ? [0 .. ($pager_entry_max - 1)]  | 
| 
416
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         : $args{cur_page} >= ($args{total_page_num} - $right_margin)  | 
| 
417
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                                                     ? [($args{total_page_num} - $pager_entry_max) .. ($args{total_page_num} - 1)]  | 
| 
418
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                                                     : [($args{cur_page} - $left_margin) .. ($args{cur_page} + $right_margin - 1)];  | 
| 
419
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $input_args{page_path} = sub {  | 
| 
420
 | 
166
 | 
 
 | 
 
 | 
  
166
  
 | 
 
 | 
216
 | 
         my ($page) = @_;  | 
| 
421
 | 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
326
 | 
         return _make_path($self->{script_name}, "/?page=$page");  | 
| 
422
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
95
 | 
     };  | 
| 
423
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
71
 | 
     return $self->_response_template(  | 
| 
424
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         template => "timeline_list.tx",  | 
| 
425
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         args => \%input_args,  | 
| 
426
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
427
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
428
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
429
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
430
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
431
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 __END__  |