root/lang/perl/mobirc/trunk/lib/App/Mobirc/HTTPD/Controller.pm @ 12371

Revision 12371, 8.5 kB (checked in by tokuhirom, 5 years ago)

use Template::Declare for readable code.

Line 
1package App::Mobirc::HTTPD::Controller;
2use strict;
3use warnings;
4
5use Carp;
6use CGI;
7use URI;
8use Encode;
9use Template;
10use Path::Class;
11use URI::Escape;
12use HTTP::Response;
13use HTML::Entities;
14use Scalar::Util qw/blessed/;
15use List::Util qw/first/;
16use Template::Provider::Encoding;
17use Encode::JP::Mobile 0.24;
18
19use App::Mobirc;
20use App::Mobirc::Util;
21use App::Mobirc::HTTPD::View;
22
23sub call {
24    my ($class, $method, @args) = @_;
25    DEBUG "CALL METHOD $method with @args";
26    $class->$method(@args);
27}
28
29# this module contains MVC's C.
30
31sub dispatch_index {
32    my ($class, $c) = @_;
33
34    my $channels = [
35        reverse
36          map {
37              $_->[0];
38          }
39          sort {
40              $a->[1] <=> $b->[1] ||
41              $a->[2] <=> $b->[2]
42          }
43          map {
44              my $unl  = $_->unread_lines ? 1 : 0;
45              my $buf  = $_->message_log || [];
46              my $last =
47                (grep {
48                    $_->{class} eq "public" ||
49                    $_->{class} eq "notice"
50                } @{ $buf })[-1] || {};
51              my $time = ($last->{time} || 0);
52              [$_, $unl, $time];
53          }
54          $c->{global_context}->channels
55    ];
56
57    my $keyword_recent_num = $c->{global_context}->get_channel(U '*keyword*')->unread_lines;
58
59    return render(
60        $c,
61        'index' => {
62            exists_recent_entries => (
63                grep( $_->unread_lines, $c->{global_context}->channels )
64                ? true
65                : false
66            ),
67            keyword_recent_num => $keyword_recent_num,
68            channels => $channels,
69        }
70    );
71}
72
73# recent messages on every channel
74sub dispatch_recent {
75    my ($class, $c) = @_;
76
77    my @target_channels;
78    my $log_counter = 0;
79    my $has_next_page = false;
80
81    my @unread_channels =
82      grep { $_->unread_lines }
83      $c->{global_context}->channels;
84
85    DEBUG "SCALAR " . scalar @unread_channels;
86
87    for my $channel (@unread_channels) {
88        push @target_channels, $channel;
89        $log_counter += scalar @{ $channel->recent_log };
90
91        if ($log_counter >= $c->{config}->{httpd}->{recent_log_per_page}) {
92            $has_next_page = true; # FIXME: BUGGY
93            last;
94        }
95    }
96
97    my $out = render(
98        $c,
99        'recent' => {
100            target_channels => \@target_channels,
101            has_next_page   => $has_next_page,
102        },
103    );
104
105    # reset counter.
106    for my $channel ( @target_channels ) {
107        $channel->clear_unread;
108    }
109
110    return $out;
111}
112
113sub dispatch_clear_all_unread {
114    my ($class, $c) = @_;
115
116    for my $channel ($c->{global_context}->channels) {
117        $channel->clear_unread;
118    }
119
120    my $response = HTTP::Response->new(302);
121    my $root = $c->{config}->{httpd}->{root};
122
123    # SHOULD USE http://example.com/ INSTEAD OF http://example.com:portnumber/
124    # because au phone returns '400 Bad Request' when redrirect to http://example.com:portnumber/
125    $response->push_header(
126        'Location' => (
127                'http://'
128              . ($c->{config}->{httpd}->{host} || $c->{req}->header('Host'))
129              . $root
130        )
131    );
132
133    return $response;
134}
135
136# topic on every channel
137sub dispatch_topics {
138    my ($class, $c) = @_;
139
140    return render(
141        $c,
142        'topics' => {
143            channels => [$c->{global_context}->channels],
144        },
145    );
146}
147
148sub post_dispatch_show_channel {
149    my ( $class, $c, $recent_mode, $channel) = @_;
150
151    $channel = decode('utf8', $channel); # maybe $channel is not flagged utf8.
152
153    my $r       = CGI->new( $c->{req}->content );
154    my $message = $r->param('msg');
155    $message = decode( $c->{mobile_agent}->encoding, $message );
156
157    DEBUG "POST MESSAGE $message";
158
159    $c->{global_context}->get_channel($channel)->post_command($message);
160
161    my $irc_incode = $c->{irc_incode};
162
163    my $response = HTTP::Response->new(302);
164    my $root = $c->{config}->{httpd}->{root};
165    $root =~ s!/$!!;
166    my $path = $c->{req}->uri;
167    $path =~ s/#/%23/;
168
169    # SHOULD USE http://example.com/ INSTEAD OF http://example.com:portnumber/
170    # because au phone returns '400 Bad Request' when redrirect to http://example.com:portnumber/
171    $response->push_header(
172        'Location' => (
173                'http://'
174              . ($c->{config}->{httpd}->{host} || $c->{req}->header('Host'))
175              . $root
176              . $path
177              . '?time='
178              . time
179        )
180    );
181    return $response;
182}
183
184sub dispatch_keyword {
185    my ($class, $c, $recent_mode) = @_;
186
187    my $channel = $c->{global_context}->get_channel(U '*keyword*');
188
189    my $out = render(
190        $c,
191        'keyword' => {
192            rows => ($recent_mode ? $channel->recent_log : $channel->message_log),
193        },
194    );
195
196    $channel->clear_unread;
197
198    return $out;
199}
200
201sub dispatch_show_channel {
202    my ($class, $c, $recent_mode, $channel_name, $render) = @_;
203
204    DEBUG "show channel page: $channel_name";
205    $channel_name = decode('utf8', $channel_name); # maybe $channel_name is not flagged utf8.
206
207    my $channel = $c->{global_context}->get_channel($channel_name);
208
209    my $out = render(
210        $c,
211        'show_channel' => {
212            channel     => $channel,
213            recent_mode => $recent_mode,
214            render_ajax    => $render,
215            msg         => decode(
216                'utf8', +{ URI->new( $c->{req}->uri )->query_form }->{msg}
217            ),
218            channel_page_option => [
219                map { $_->( $channel, $c ) } @{
220                    $c->{global_context}->get_hook_codes('channel_page_option')
221                  }
222            ],
223          }
224    );
225
226    $channel->clear_unread;
227
228    return $out;
229}
230
231sub render {
232    my ( $c, $name, $args ) = @_;
233
234    croak "invalid args : $args" unless ref $args eq 'HASH';
235
236    DEBUG "rendering template";
237
238    # set default vars
239    $args = {
240        docroot              => $c->{config}->{httpd}->{root},
241        render_line          => sub { render_line( $c, @_ ) },
242        user_agent           => $c->{user_agent},
243        mobile_agent         => $c->{mobile_agent},
244        title                => $c->{config}->{httpd}->{title},
245        version              => $App::Mobirc::VERSION,
246        now                  => time(),
247
248        %$args,
249    };
250
251    my $tmpl_dir = $c->{mobile_agent}->is_non_mobile ? 'pc' : 'mobile';
252    DEBUG "tmpl_dir: $tmpl_dir";
253
254    my $tt = Template->new(
255        LOAD_TEMPLATES => [
256            Template::Provider::Encoding->new(
257                ABSOLUTE => 1,
258                INCLUDE_PATH => dir( $c->{config}->{global}->{assets_dir}, 'tmpl', $tmpl_dir, )->stringify,
259            )
260        ],
261    );
262    $tt->process("$name.html", $args, \my $out)
263        or die $tt->error;
264
265    DEBUG "rendering done";
266
267    $out = _html_filter($c, $out);
268    my $content = encode( $c->{mobile_agent}->encoding, $out);
269
270    # change content type for docomo
271    # FIXME: hmm... should be in the plugin?
272    my $content_type = $c->{config}->{httpd}->{content_type};
273    $content_type= 'application/xhtml+xml' if $c->{mobile_agent}->is_docomo;
274    unless ( $content_type ) {
275        if ( $c->{mobile_agent}->can_display_utf8 ) {
276            $content_type = 'text/html; charset=UTF-8';
277        } else {
278            $content_type = 'text/html; charset=Shift_JIS';
279        }
280    }
281
282    my $response = HTTP::Response->new(200);
283    $response->push_header( 'Content-type' => encode('utf8', $content_type) );
284    $response->push_header('Content-Length' => length($content) );
285
286    $response->content( $content );
287
288    for my $code (@{$c->{global_context}->get_hook_codes('response_filter')}) {
289        $code->($c, $response);
290    }
291
292    return $response;
293}
294
295sub dispatch_static {
296    my ($class, $c, $file_name, $content_type) = @_;
297
298    my $file = file($c->{config}->{global}->{assets_dir},'static', $file_name);
299    my $content = $file->slurp;
300
301    my $response = HTTP::Response->new(200);
302    $response->push_header( 'Content-type' => $content_type );
303    $response->push_header('Content-Length' => length($content) );
304
305    $response->content( $content );
306
307    return $response;
308}
309
310sub _html_filter {
311    my $c = shift;
312    my $content = shift;
313
314    for my $code (@{$c->{global_context}->get_hook_codes('html_filter')}) {
315        $content = $code->($c, $content);
316    }
317
318    $content;
319}
320
321sub render_line {
322    my $c   = shift;
323    my $message = shift;
324
325    return "" unless $message;
326    croak "must be object: $message" unless ref $message eq 'App::Mobirc::Model::Message';
327
328    my $out = App::Mobirc::HTTPD::View->show('irc_message', $message, $c->{irc_nick});
329    $out =~ s/^ //smg;
330    $out =~ s/\n//g;
331    $out;
332}
333
3341;
Note: See TracBrowser for help on using the browser.