| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package BusyBird::Main::PSGI; | 
| 2 | 5 |  |  | 5 |  | 5783 | use strict; | 
|  | 5 |  |  |  |  | 10 |  | 
|  | 5 |  |  |  |  | 179 |  | 
| 3 | 5 |  |  | 5 |  | 23 | use warnings; | 
|  | 5 |  |  |  |  | 7 |  | 
|  | 5 |  |  |  |  | 159 |  | 
| 4 | 5 |  |  | 5 |  | 21 | use BusyBird::Util qw(set_param future_of); | 
|  | 5 |  |  |  |  | 8 |  | 
|  | 5 |  |  |  |  | 249 |  | 
| 5 | 5 |  |  | 5 |  | 2389 | use BusyBird::Main::PSGI::View; | 
|  | 5 |  |  |  |  | 13 |  | 
|  | 5 |  |  |  |  | 177 |  | 
| 6 | 5 |  |  | 5 |  | 2612 | use Router::Simple; | 
|  | 5 |  |  |  |  | 14230 |  | 
|  | 5 |  |  |  |  | 142 |  | 
| 7 | 5 |  |  | 5 |  | 2418 | use Plack::Request; | 
|  | 5 |  |  |  |  | 185551 |  | 
|  | 5 |  |  |  |  | 209 |  | 
| 8 | 5 |  |  | 5 |  | 2415 | use Plack::Builder (); | 
|  | 5 |  |  |  |  | 26621 |  | 
|  | 5 |  |  |  |  | 124 |  | 
| 9 | 5 |  |  | 5 |  | 2450 | use Plack::App::File; | 
|  | 5 |  |  |  |  | 31904 |  | 
|  | 5 |  |  |  |  | 146 |  | 
| 10 | 5 |  |  | 5 |  | 32 | use Try::Tiny; | 
|  | 5 |  |  |  |  | 9 |  | 
|  | 5 |  |  |  |  | 323 |  | 
| 11 | 5 |  |  | 5 |  | 25 | use JSON qw(decode_json); | 
|  | 5 |  |  |  |  | 10 |  | 
|  | 5 |  |  |  |  | 41 |  | 
| 12 | 5 |  |  | 5 |  | 616 | use Scalar::Util qw(looks_like_number); | 
|  | 5 |  |  |  |  | 8 |  | 
|  | 5 |  |  |  |  | 219 |  | 
| 13 | 5 |  |  | 5 |  | 22 | use List::Util qw(min); | 
|  | 5 |  |  |  |  | 8 |  | 
|  | 5 |  |  |  |  | 272 |  | 
| 14 | 5 |  |  | 5 |  | 23 | use Carp; | 
|  | 5 |  |  |  |  | 8 |  | 
|  | 5 |  |  |  |  | 203 |  | 
| 15 | 5 |  |  | 5 |  | 26 | use Exporter qw(import); | 
|  | 5 |  |  |  |  | 6 |  | 
|  | 5 |  |  |  |  | 138 |  | 
| 16 | 5 |  |  | 5 |  | 21 | use URI::Escape qw(uri_unescape); | 
|  | 5 |  |  |  |  | 11 |  | 
|  | 5 |  |  |  |  | 215 |  | 
| 17 | 5 |  |  | 5 |  | 24 | use Encode qw(decode_utf8); | 
|  | 5 |  |  |  |  | 7 |  | 
|  | 5 |  |  |  |  | 362 |  | 
| 18 | 5 |  |  | 5 |  | 31 | use Future::Q; | 
|  | 5 |  |  |  |  | 7 |  | 
|  | 5 |  |  |  |  | 129 |  | 
| 19 | 5 |  |  | 5 |  | 22 | use POSIX qw(ceil); | 
|  | 5 |  |  |  |  | 10 |  | 
|  | 5 |  |  |  |  | 70 |  | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | our @EXPORT_OK = qw(create_psgi_app); | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | sub create_psgi_app { | 
| 25 | 45 |  |  | 45 | 1 | 1228 | my ($main_obj) = @_; | 
| 26 | 45 |  |  |  |  | 286 | my $self = __PACKAGE__->_new(main_obj => $main_obj); | 
| 27 | 45 |  |  |  |  | 155 | return $self->_to_app; | 
| 28 |  |  |  |  |  |  | } | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | sub _new { | 
| 31 | 45 |  |  | 45 |  | 110 | my ($class, %params) = @_; | 
| 32 | 45 |  |  |  |  | 312 | my $self = bless { | 
| 33 |  |  |  |  |  |  | router => Router::Simple->new, | 
| 34 |  |  |  |  |  |  | view => undef, ## lazy build | 
| 35 |  |  |  |  |  |  | }, $class; | 
| 36 | 45 |  |  |  |  | 463 | $self->set_param(\%params, "main_obj", undef, 1); | 
| 37 | 45 |  |  |  |  | 135 | $self->_build_routes(); | 
| 38 | 45 |  |  |  |  | 1905 | return $self; | 
| 39 |  |  |  |  |  |  | } | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | sub _to_app { | 
| 42 | 45 |  |  | 45 |  | 69 | my $self = shift; | 
| 43 | 45 |  |  |  |  | 168 | my $sharedir = $self->{main_obj}->get_config("sharedir_path"); | 
| 44 | 45 |  |  |  |  | 212 | $sharedir =~ s{/+$}{}; | 
| 45 |  |  |  |  |  |  | return Plack::Builder::builder { | 
| 46 | 45 |  |  | 45 |  | 1717 | Plack::Builder::enable 'ContentLength'; | 
| 47 | 45 |  |  |  |  | 6159 | Plack::Builder::mount '/static' => Plack::App::File->new( | 
| 48 |  |  |  |  |  |  | root => File::Spec->catdir($sharedir, 'www', 'static') | 
| 49 |  |  |  |  |  |  | )->to_app; | 
| 50 | 45 |  |  |  |  | 2673 | Plack::Builder::mount '/' => $self->_my_app; | 
| 51 | 45 |  |  |  |  | 379 | }; | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | sub _my_app { | 
| 55 | 45 |  |  | 45 |  | 69 | my ($self) = @_; | 
| 56 |  |  |  |  |  |  | return sub { | 
| 57 | 185 |  |  | 185 |  | 826612 | my ($env) = @_; | 
| 58 | 185 |  | 66 |  |  | 1046 | $self->{view} ||= BusyBird::Main::PSGI::View->new(main_obj => $self->{main_obj}, script_name => $env->{SCRIPT_NAME}); | 
| 59 | 185 | 100 |  |  |  | 831 | if(my $dest = $self->{router}->match($env)) { | 
| 60 | 179 |  |  |  |  | 14454 | my $req = Plack::Request->new($env); | 
| 61 | 179 |  |  |  |  | 1420 | my $code = $dest->{code}; | 
| 62 | 179 |  |  |  |  | 311 | my $method = $dest->{method}; | 
| 63 | 179 | 50 |  |  |  | 855 | return defined($code) ? $code->($self, $req, $dest) : $self->$method($req, $dest); | 
| 64 |  |  |  |  |  |  | }else { | 
| 65 | 6 |  |  |  |  | 509 | return $self->{view}->response_notfound(); | 
| 66 |  |  |  |  |  |  | } | 
| 67 | 45 |  |  |  |  | 214 | }; | 
| 68 |  |  |  |  |  |  | } | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | sub _build_routes { | 
| 71 | 45 |  |  | 45 |  | 65 | my ($self) = @_; | 
| 72 | 45 |  |  |  |  | 196 | my $tl_mapper = $self->{router}->submapper( | 
| 73 |  |  |  |  |  |  | '/timelines/{timeline}', {} | 
| 74 |  |  |  |  |  |  | ); | 
| 75 | 45 |  |  |  |  | 1291 | $tl_mapper->connect('/statuses.{format}', | 
| 76 |  |  |  |  |  |  | {method => '_handle_tl_get_statuses'}, {method => 'GET'}); | 
| 77 | 45 |  |  |  |  | 5475 | $tl_mapper->connect('/statuses.json', | 
| 78 |  |  |  |  |  |  | {method => '_handle_tl_post_statuses'}, {method => 'POST'}); | 
| 79 | 45 |  |  |  |  | 3485 | $tl_mapper->connect('/ack.json', | 
| 80 |  |  |  |  |  |  | {method => '_handle_tl_ack'}, {method => 'POST'}); | 
| 81 | 45 |  |  |  |  | 3218 | $tl_mapper->connect('/updates/unacked_counts.json', | 
| 82 |  |  |  |  |  |  | {method => '_handle_tl_get_unacked_counts'}, {method => 'GET'}); | 
| 83 | 45 |  |  |  |  | 3424 | $tl_mapper->connect($_, {method => '_handle_tl_index'}) foreach "", qw(/ /index.html /index.htm); | 
| 84 | 45 |  |  |  |  | 10618 | $self->{router}->connect('/updates/unacked_counts.json', | 
| 85 |  |  |  |  |  |  | {method => '_handle_get_unacked_counts'}, {method => 'GET'}); | 
| 86 | 45 |  |  |  |  | 1892 | foreach my $path ("/", "/index.html") { | 
| 87 | 90 |  |  |  |  | 1996 | $self->{router}->connect($path, {method => '_handle_get_timeline_list'}, {method => 'GET'}); | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | sub _get_timeline_name { | 
| 92 | 152 |  |  | 152 |  | 211 | my ($dest) = @_; | 
| 93 | 152 |  |  |  |  | 256 | my $name = $dest->{timeline}; | 
| 94 | 152 | 50 |  |  |  | 383 | $name = "" if not defined($name); | 
| 95 | 152 |  |  |  |  | 394 | $name =~ s/\+/ /g; | 
| 96 | 152 |  |  |  |  | 465 | return decode_utf8(uri_unescape($name)); | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | sub _get_timeline { | 
| 100 | 135 |  |  | 135 |  | 204 | my ($self, $dest) = @_; | 
| 101 | 135 |  |  |  |  | 308 | my $name = _get_timeline_name($dest); | 
| 102 | 135 |  |  |  |  | 4162 | my $timeline = $self->{main_obj}->get_timeline($name); | 
| 103 | 135 | 100 |  |  |  | 1358 | if(!defined($timeline)) { | 
| 104 | 4 |  |  |  |  | 37 | die qq{No timeline named $name}; | 
| 105 |  |  |  |  |  |  | } | 
| 106 | 131 |  |  |  |  | 253 | return $timeline; | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | sub _handle_tl_get_statuses { | 
| 110 | 70 |  |  | 70 |  | 118 | my ($self, $req, $dest) = @_; | 
| 111 |  |  |  |  |  |  | return sub { | 
| 112 | 70 |  |  | 70 |  | 3196 | my $responder = shift; | 
| 113 | 70 |  |  |  |  | 98 | my $timeline; | 
| 114 | 70 |  |  |  |  | 220 | my $only_statuses = !!($req->query_parameters->{only_statuses}); | 
| 115 | 70 | 100 | 100 |  |  | 5952 | my $format = !defined($dest->{format}) ? "" | 
|  |  | 50 |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | : (lc($dest->{format}) eq "json" && $only_statuses) ? "json_only_statuses" | 
| 117 |  |  |  |  |  |  | : $dest->{format}; | 
| 118 |  |  |  |  |  |  | Future::Q->try(sub { | 
| 119 | 70 |  |  |  |  | 2510 | $timeline = $self->_get_timeline($dest); | 
| 120 | 69 |  | 100 |  |  | 160 | my $count = $req->query_parameters->{count} || 20; | 
| 121 | 69 | 50 | 33 |  |  | 881 | if(!looks_like_number($count) || int($count) != $count) { | 
| 122 | 0 |  |  |  |  | 0 | die "count parameter must be an integer\n"; | 
| 123 |  |  |  |  |  |  | } | 
| 124 | 69 |  | 100 |  |  | 153 | my $ack_state = $req->query_parameters->{ack_state} || 'any'; | 
| 125 | 69 |  |  |  |  | 480 | my $max_id = decode_utf8($req->query_parameters->{max_id}); | 
| 126 | 69 |  |  |  |  | 816 | return future_of($timeline, "get_statuses", | 
| 127 |  |  |  |  |  |  | count => $count, ack_state => $ack_state, max_id => $max_id); | 
| 128 |  |  |  |  |  |  | })->then(sub { | 
| 129 | 65 |  |  |  |  | 10799 | my $statuses = shift; | 
| 130 | 65 |  |  |  |  | 345 | $responder->($self->{view}->response_statuses( | 
| 131 |  |  |  |  |  |  | statuses => $statuses, http_code => 200, format => $format, | 
| 132 |  |  |  |  |  |  | timeline_name => $timeline->name | 
| 133 |  |  |  |  |  |  | )); | 
| 134 |  |  |  |  |  |  | })->catch(sub { | 
| 135 | 5 |  |  |  |  | 1588 | my ($error, $is_normal_error) = @_; | 
| 136 | 5 | 100 |  |  |  | 45 | $responder->($self->{view}->response_statuses( | 
|  |  | 100 |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | error => "$error", http_code => ($is_normal_error ? 500 : 400), format => $format, | 
| 138 |  |  |  |  |  |  | ($timeline ? (timeline_name => $timeline->name) : ()) | 
| 139 |  |  |  |  |  |  | )); | 
| 140 | 70 |  |  |  |  | 606 | }); | 
| 141 | 70 |  |  |  |  | 667 | }; | 
| 142 |  |  |  |  |  |  | } | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | sub _handle_tl_post_statuses { | 
| 145 | 31 |  |  | 31 |  | 71 | my ($self, $req, $dest) = @_; | 
| 146 |  |  |  |  |  |  | return sub { | 
| 147 | 31 |  |  | 31 |  | 1646 | my $responder = shift; | 
| 148 |  |  |  |  |  |  | Future::Q->try(sub { | 
| 149 | 31 |  |  |  |  | 1251 | my $timeline = $self->_get_timeline($dest); | 
| 150 | 30 |  |  |  |  | 119 | my $posted_obj = decode_json($req->content); | 
| 151 | 29 | 100 |  |  |  | 36261 | if(ref($posted_obj) ne 'ARRAY') { | 
| 152 | 5 |  |  |  |  | 12 | $posted_obj = [$posted_obj]; | 
| 153 |  |  |  |  |  |  | } | 
| 154 | 29 |  |  |  |  | 133 | return future_of($timeline, "add_statuses", statuses => $posted_obj); | 
| 155 |  |  |  |  |  |  | })->then(sub { | 
| 156 | 27 |  |  |  |  | 5434 | my $added_num = shift; | 
| 157 | 27 |  |  |  |  | 227 | $responder->($self->{view}->response_json(200, {count => $added_num + 0})); | 
| 158 |  |  |  |  |  |  | })->catch(sub { | 
| 159 | 4 |  |  |  |  | 1174 | my ($e, $is_normal_error) = @_; | 
| 160 | 4 | 100 |  |  |  | 25 | $responder->($self->{view}->response_json(($is_normal_error ? 500 : 400), {error => "$e"})); | 
| 161 | 31 |  |  |  |  | 304 | }); | 
| 162 | 31 |  |  |  |  | 323 | }; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | sub _handle_tl_ack { | 
| 166 | 28 |  |  | 28 |  | 58 | my ($self, $req, $dest) = @_; | 
| 167 |  |  |  |  |  |  | return sub { | 
| 168 | 28 |  |  | 28 |  | 1287 | my $responder = shift; | 
| 169 |  |  |  |  |  |  | Future::Q->try(sub { | 
| 170 | 28 |  |  |  |  | 964 | my $timeline = $self->_get_timeline($dest); | 
| 171 | 27 |  |  |  |  | 39 | my $max_id = undef; | 
| 172 | 27 |  |  |  |  | 42 | my $ids = undef; | 
| 173 | 27 | 100 |  |  |  | 114 | if($req->content) { | 
| 174 | 23 |  |  |  |  | 18806 | my $body_obj = decode_json($req->content); | 
| 175 | 23 | 50 |  |  |  | 541 | if(ref($body_obj) ne 'HASH') { | 
| 176 | 0 |  |  |  |  | 0 | die "Response body must be an object.\n"; | 
| 177 |  |  |  |  |  |  | } | 
| 178 | 23 |  |  |  |  | 54 | $max_id = $body_obj->{max_id}; | 
| 179 | 23 |  |  |  |  | 63 | $ids = $body_obj->{ids}; | 
| 180 |  |  |  |  |  |  | } | 
| 181 | 27 |  |  |  |  | 416 | return future_of($timeline, "ack_statuses", max_id => $max_id, ids => $ids); | 
| 182 |  |  |  |  |  |  | })->then(sub { | 
| 183 | 25 |  |  |  |  | 3580 | my $acked_num = shift; | 
| 184 | 25 |  |  |  |  | 182 | $responder->($self->{view}->response_json(200, {count => $acked_num + 0})); | 
| 185 |  |  |  |  |  |  | })->catch(sub { | 
| 186 | 3 |  |  |  |  | 784 | my ($e, $is_normal_error) = @_; | 
| 187 | 3 | 100 |  |  |  | 21 | $responder->($self->{view}->response_json(($is_normal_error ? 500 : 400), {error => "$e"})); | 
| 188 | 28 |  |  |  |  | 217 | }); | 
| 189 | 28 |  |  |  |  | 242 | }; | 
| 190 |  |  |  |  |  |  | } | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | sub _handle_tl_get_unacked_counts { | 
| 193 | 6 |  |  | 6 |  | 9 | my ($self, $req, $dest) = @_; | 
| 194 |  |  |  |  |  |  | return sub { | 
| 195 | 6 |  |  | 6 |  | 224 | my $responder = shift; | 
| 196 |  |  |  |  |  |  | Future::Q->try(sub { | 
| 197 | 6 |  |  |  |  | 345 | my $timeline = $self->_get_timeline($dest); | 
| 198 | 5 |  |  |  |  | 16 | my $query_params = $req->query_parameters; | 
| 199 | 5 |  |  |  |  | 340 | my %assumed = (); | 
| 200 | 5 | 100 |  |  |  | 17 | if(defined $query_params->{total}) { | 
| 201 | 3 |  |  |  |  | 9 | $assumed{total} = delete $query_params->{total}; | 
| 202 |  |  |  |  |  |  | } | 
| 203 | 5 |  |  |  |  | 16 | foreach my $query_key (keys %$query_params) { | 
| 204 | 5 | 50 |  |  |  | 15 | next if !looks_like_number($query_key); | 
| 205 | 5 | 50 |  |  |  | 16 | next if int($query_key) != $query_key; | 
| 206 | 5 |  |  |  |  | 9 | $assumed{$query_key} = $query_params->{$query_key}; | 
| 207 |  |  |  |  |  |  | } | 
| 208 | 5 |  |  |  |  | 19 | my $ret_future = Future::Q->new; | 
| 209 |  |  |  |  |  |  | $timeline->watch_unacked_counts(assumed => \%assumed, callback => sub { | 
| 210 | 5 |  |  |  |  | 7 | my ($error, $w, $unacked_counts) = @_; | 
| 211 | 5 |  |  |  |  | 22 | $w->cancel(); ## immediately cancel the watcher to prevent multiple callbacks | 
| 212 | 5 | 50 |  |  |  | 53 | if($error) { | 
| 213 | 0 |  |  |  |  | 0 | $ret_future->reject($error, 1); | 
| 214 |  |  |  |  |  |  | }else { | 
| 215 | 5 |  |  |  |  | 15 | $ret_future->fulfill($unacked_counts); | 
| 216 |  |  |  |  |  |  | } | 
| 217 | 5 |  |  |  |  | 82 | }); | 
| 218 | 5 |  |  |  |  | 27 | return $ret_future; | 
| 219 |  |  |  |  |  |  | })->then(sub { | 
| 220 | 5 |  |  |  |  | 1137 | my ($unacked_counts) = @_; | 
| 221 | 5 |  |  |  |  | 25 | $responder->($self->{view}->response_json(200, {unacked_counts => $unacked_counts})); | 
| 222 |  |  |  |  |  |  | })->catch(sub { | 
| 223 | 1 |  |  |  |  | 260 | my ($e, $is_normal_error) = @_; | 
| 224 | 1 | 50 |  |  |  | 7 | $responder->($self->{view}->response_json(($is_normal_error ? 500 : 400), {error => "$e"})); | 
| 225 | 6 |  |  |  |  | 42 | }); | 
| 226 | 6 |  |  |  |  | 44 | }; | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | sub _handle_get_unacked_counts { | 
| 230 | 17 |  |  | 17 |  | 30 | my ($self, $req, $dest) = @_; | 
| 231 |  |  |  |  |  |  | return sub { | 
| 232 | 17 |  |  | 17 |  | 850 | my $responder = shift; | 
| 233 |  |  |  |  |  |  | Future::Q->try(sub { | 
| 234 | 17 |  |  |  |  | 629 | my $query_params = $req->query_parameters; | 
| 235 | 17 |  |  |  |  | 1852 | my $level = $query_params->{level}; | 
| 236 | 17 | 100 | 33 |  |  | 104 | if(not defined($level)) { | 
|  |  | 50 | 66 |  |  |  |  | 
| 237 | 3 |  |  |  |  | 3 | $level = "total"; | 
| 238 |  |  |  |  |  |  | }elsif($level ne 'total' && (!looks_like_number($level) || int($level) != $level)) { | 
| 239 | 0 |  |  |  |  | 0 | die "level parameter must be an integer\n"; | 
| 240 |  |  |  |  |  |  | } | 
| 241 | 17 |  |  |  |  | 32 | my %assumed = (); | 
| 242 | 17 |  |  |  |  | 48 | foreach my $query_key (keys %$query_params) { | 
| 243 | 62 | 100 |  |  |  | 607 | next if substr($query_key, 0, 3) ne 'tl_'; | 
| 244 | 46 |  |  |  |  | 113 | $assumed{decode_utf8(substr($query_key, 3))} = $query_params->{$query_key}; | 
| 245 |  |  |  |  |  |  | } | 
| 246 | 17 |  |  |  |  | 182 | my $ret_future = Future::Q->new; | 
| 247 |  |  |  |  |  |  | $self->{main_obj}->watch_unacked_counts( | 
| 248 |  |  |  |  |  |  | level => $level, assumed => \%assumed, callback => sub { | 
| 249 | 15 |  |  |  |  | 22 | my ($error, $w, $tl_unacked_counts) = @_; | 
| 250 | 15 |  |  |  |  | 45 | $w->cancel(); ## immediately cancel the watcher to prevent multiple callbacks | 
| 251 | 15 | 50 |  |  |  | 203 | if($error) { | 
| 252 | 0 |  |  |  |  | 0 | $ret_future->reject($error, 1); | 
| 253 |  |  |  |  |  |  | }else { | 
| 254 | 15 |  |  |  |  | 43 | $ret_future->fulfill($tl_unacked_counts); | 
| 255 |  |  |  |  |  |  | } | 
| 256 |  |  |  |  |  |  | } | 
| 257 | 17 |  |  |  |  | 314 | ); | 
| 258 | 15 |  |  |  |  | 74 | return $ret_future; | 
| 259 |  |  |  |  |  |  | })->then(sub { | 
| 260 | 15 |  |  |  |  | 1655 | my ($tl_unacked_counts) = @_; | 
| 261 | 15 |  |  |  |  | 74 | $responder->($self->{view}->response_json(200, {unacked_counts => $tl_unacked_counts})); | 
| 262 |  |  |  |  |  |  | })->catch(sub { | 
| 263 | 2 |  |  |  |  | 790 | my ($e, $is_normal_error) = @_; | 
| 264 | 2 | 50 |  |  |  | 13 | $responder->($self->{view}->response_json(($is_normal_error ? 500 : 400), {error => "$e"})); | 
| 265 | 17 |  |  |  |  | 114 | }); | 
| 266 | 17 |  |  |  |  | 152 | }; | 
| 267 |  |  |  |  |  |  | } | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | sub _handle_tl_index { | 
| 270 | 17 |  |  | 17 |  | 21 | my ($self, $req, $dest) = @_; | 
| 271 | 17 |  |  |  |  | 57 | return $self->{view}->response_timeline(_get_timeline_name($dest)); | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | sub _handle_get_timeline_list { | 
| 275 | 10 |  |  | 10 |  | 18 | my ($self, $req, $dest) = @_; | 
| 276 |  |  |  |  |  |  | return sub { | 
| 277 | 10 |  |  | 10 |  | 521 | my $responder = shift; | 
| 278 |  |  |  |  |  |  | Future::Q->try(sub { | 
| 279 | 10 |  |  |  |  | 382 | my $num_per_page = $self->{main_obj}->get_config('timeline_list_per_page'); | 
| 280 | 112 |  |  |  |  | 896 | my @timelines = grep { | 
| 281 | 10 |  |  |  |  | 39 | !$self->{main_obj}->get_timeline_config($_->name, "hidden") | 
| 282 |  |  |  |  |  |  | } $self->{main_obj}->get_all_timelines(); | 
| 283 | 10 | 50 |  |  |  | 59 | if(@timelines == 0) { | 
| 284 | 0 |  |  |  |  | 0 | die "No visible timeline. Probably you must configure config.psgi to create a timeline."; | 
| 285 |  |  |  |  |  |  | } | 
| 286 | 10 |  |  |  |  | 56 | my $page_num = ceil(scalar(@timelines) / $num_per_page); | 
| 287 | 10 |  |  |  |  | 19 | my $cur_page = 0; | 
| 288 | 10 |  |  |  |  | 40 | my $query = $req->query_parameters; | 
| 289 | 10 | 100 |  |  |  | 651 | if(defined $query->{page}) { | 
| 290 | 7 | 100 | 100 |  |  | 73 | if(!looks_like_number($query->{page}) || $query->{page} < 0 || $query->{page} >= $page_num) { | 
|  |  |  | 100 |  |  |  |  | 
| 291 | 4 |  |  |  |  | 26 | die "Invalid page parameter\n"; | 
| 292 |  |  |  |  |  |  | } | 
| 293 | 3 |  |  |  |  | 5 | $cur_page = $query->{page}; | 
| 294 |  |  |  |  |  |  | } | 
| 295 | 6 |  |  |  |  | 61 | my @target_timelines = @timelines[($cur_page * $num_per_page) .. min(($cur_page+1) * $num_per_page - 1, $#timelines)]; | 
| 296 | 24 |  |  |  |  | 451 | return Future::Q->needs_all(map { future_of($_, "get_unacked_counts") } @target_timelines)->then(sub { | 
| 297 | 6 |  |  |  |  | 1579 | my (@unacked_counts_list) = @_; | 
| 298 | 24 |  |  |  |  | 49 | my @timeline_unacked_counts = map { | 
| 299 | 6 |  |  |  |  | 22 | +{ name => $target_timelines[$_]->name, counts => $unacked_counts_list[$_] } | 
| 300 |  |  |  |  |  |  | } 0 .. $#target_timelines; | 
| 301 | 6 |  |  |  |  | 41 | $responder->( $self->{view}->response_timeline_list( | 
| 302 |  |  |  |  |  |  | timeline_unacked_counts => \@timeline_unacked_counts, | 
| 303 |  |  |  |  |  |  | total_page_num => $page_num, | 
| 304 |  |  |  |  |  |  | cur_page => $cur_page | 
| 305 |  |  |  |  |  |  | ) ); | 
| 306 | 6 |  |  |  |  | 11 | }); | 
| 307 |  |  |  |  |  |  | })->catch(sub { | 
| 308 | 4 |  |  |  |  | 577 | my ($error, $is_normal_error) = @_; | 
| 309 | 4 | 50 |  |  |  | 20 | $responder->($self->{view}->response_error_html( | 
| 310 |  |  |  |  |  |  | ($is_normal_error ? 500 : 400), $error | 
| 311 |  |  |  |  |  |  | )); | 
| 312 | 10 |  |  |  |  | 102 | }); | 
| 313 | 10 |  |  |  |  | 88 | }; | 
| 314 |  |  |  |  |  |  | } | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | 1; | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | __END__ |