source: main/trunk/greenstone2/perllib/cpan/Mojolicious/Plugin/DefaultHelpers.pm@ 32205

Last change on this file since 32205 was 32205, checked in by ak19, 6 years ago

First set of commits to do with implementing the new 'paged_html' output option of PDFPlugin that uses using xpdftools' new pdftohtml. So far tested only on Linux (64 bit), but things work there so I'm optimistically committing the changes since they work. 2. Committing the pre-built Linux binaries of XPDFtools for both 32 and 64 bit built by the XPDF group. 2. To use the correct bitness variant of xpdftools, setup.bash now exports the BITNESS env var, consulted by gsConvert.pl. 3. All the perl code changes to do with using xpdf tools' pdftohtml to generate paged_html and feed it in the desired form into GS(3): gsConvert.pl, PDFPlugin.pm and its parent ConvertBinaryPFile.pm have been modified to make it all work. xpdftools' pdftohtml generates a folder containing an html file and a screenshot for each page in a PDF (as well as an index.html linking to each page's html). However, we want a single html file that contains each individual 'page' html's content in a div, and need to do some further HTML style, attribute and structure modifications to massage the xpdftool output to what we want for GS. In order to parse and manipulate the HTML 'DOM' to do this, we're using the Mojo::DOM package that Dr Bainbridge found and which he's compiled up. Mojo::DOM is therefore also committed in this revision. Some further changes and some display fixes are required, but need to check with the others about that.

File size: 15.5 KB
Line 
1package Mojolicious::Plugin::DefaultHelpers;
2use Mojo::Base 'Mojolicious::Plugin';
3
4use Mojo::Asset::File;
5use Mojo::ByteStream;
6use Mojo::Collection;
7use Mojo::Exception;
8use Mojo::IOLoop;
9use Mojo::Util qw(deprecated dumper hmac_sha1_sum steady_time);
10use Time::HiRes qw(gettimeofday tv_interval);
11use Scalar::Util 'blessed';
12
13sub register {
14 my ($self, $app) = @_;
15
16 # Controller alias helpers
17 for my $name (qw(app flash param stash session url_for validation)) {
18 $app->helper($name => sub { shift->$name(@_) });
19 }
20
21 # Stash key shortcuts (should not generate log messages)
22 for my $name (qw(extends layout title)) {
23 $app->helper($name => sub { shift->stash($name, @_) });
24 }
25
26 $app->helper(accepts => sub { $_[0]->app->renderer->accepts(@_) });
27 $app->helper(b => sub { shift; Mojo::ByteStream->new(@_) });
28 $app->helper(c => sub { shift; Mojo::Collection->new(@_) });
29 $app->helper(config => sub { shift->app->config(@_) });
30
31 $app->helper(content => sub { _content(0, 0, @_) });
32 $app->helper(content_for => sub { _content(1, 0, @_) });
33 $app->helper(content_with => sub { _content(0, 1, @_) });
34
35 # DEPRECATED!
36 $app->helper(
37 delay => sub {
38 deprecated 'delay helper is DEPRECATED';
39 my $c = shift;
40 my $tx = $c->render_later->tx;
41 Mojo::IOLoop->delay(@_)
42 ->catch(sub { $c->helpers->reply->exception(pop) and undef $tx })->wait;
43 }
44 );
45
46 $app->helper($_ => $self->can("_$_"))
47 for qw(csrf_token current_route inactivity_timeout is_fresh url_with);
48
49 $app->helper(dumper => sub { shift; dumper @_ });
50 $app->helper(include => sub { shift->render_to_string(@_) });
51
52 $app->helper("reply.$_" => $self->can("_$_")) for qw(asset file static);
53
54 $app->helper('reply.exception' => sub { _development('exception', @_) });
55 $app->helper('reply.not_found' => sub { _development('not_found', @_) });
56
57 $app->helper('timing.begin' => \&_timing_begin);
58 $app->helper('timing.elapsed' => \&_timing_elapsed);
59 $app->helper('timing.rps' => \&_timing_rps);
60 $app->helper('timing.server_timing' => \&_timing_server_timing);
61
62 $app->helper(ua => sub { shift->app->ua });
63}
64
65sub _asset {
66 my $c = shift;
67 $c->app->static->serve_asset($c, @_);
68 $c->rendered;
69}
70
71sub _block { ref $_[0] eq 'CODE' ? $_[0]() : $_[0] }
72
73sub _content {
74 my ($append, $replace, $c, $name, $content) = @_;
75 $name ||= 'content';
76
77 my $hash = $c->stash->{'mojo.content'} ||= {};
78 if (defined $content) {
79 if ($append) { $hash->{$name} .= _block($content) }
80 if ($replace) { $hash->{$name} = _block($content) }
81 else { $hash->{$name} //= _block($content) }
82 }
83
84 return Mojo::ByteStream->new($hash->{$name} // '');
85}
86
87sub _csrf_token {
88 my $c = shift;
89 return $c->session->{csrf_token}
90 ||= hmac_sha1_sum($$ . steady_time . rand, $c->app->secrets->[0]);
91}
92
93sub _current_route {
94 return '' unless my $route = shift->match->endpoint;
95 return @_ ? $route->name eq shift : $route->name;
96}
97
98sub _development {
99 my ($page, $c, $e) = @_;
100
101 my $app = $c->app;
102 $app->log->error($e = _exception($e) ? $e : Mojo::Exception->new($e)->inspect)
103 if $page eq 'exception';
104
105 # Filtered stash snapshot
106 my $stash = $c->stash;
107 %{$stash->{snapshot} = {}} = map { $_ => $stash->{$_} }
108 grep { !/^mojo\./ and defined $stash->{$_} } keys %$stash;
109 $stash->{exception} = $page eq 'exception' ? $e : undef;
110
111 # Render with fallbacks
112 my $mode = $app->mode;
113 my $options = {
114 format => $stash->{format} || $app->renderer->default_format,
115 handler => undef,
116 status => $page eq 'exception' ? 500 : 404,
117 template => "$page.$mode"
118 };
119 my $bundled = 'mojo/' . ($mode eq 'development' ? 'debug' : $page);
120 return $c if _fallbacks($c, $options, $page, $bundled);
121 _fallbacks($c, {%$options, format => 'html'}, $page, $bundled);
122 return $c;
123}
124
125sub _exception { blessed $_[0] && $_[0]->isa('Mojo::Exception') }
126
127sub _fallbacks {
128 my ($c, $options, $template, $bundled) = @_;
129
130 # Mode specific template
131 return 1 if $c->render_maybe(%$options);
132
133 # Normal template
134 return 1 if $c->render_maybe(%$options, template => $template);
135
136 # Inline template
137 my $stash = $c->stash;
138 return undef unless $options->{format} eq 'html';
139 delete @$stash{qw(extends layout)};
140 return $c->render_maybe($bundled, %$options, handler => 'ep');
141}
142
143sub _file { _asset(shift, Mojo::Asset::File->new(path => shift)) }
144
145sub _inactivity_timeout {
146 my ($c, $timeout) = @_;
147 my $stream = Mojo::IOLoop->stream($c->tx->connection // '');
148 $stream->timeout($timeout) if $stream;
149 return $c;
150}
151
152sub _is_fresh {
153 my ($c, %options) = @_;
154 return $c->app->static->is_fresh($c, \%options);
155}
156
157sub _static {
158 my ($c, $file) = @_;
159 return !!$c->rendered if $c->app->static->serve($c, $file);
160 $c->app->log->debug(qq{Static file "$file" not found});
161 return !$c->helpers->reply->not_found;
162}
163
164sub _timing_begin { shift->stash->{'mojo.timing'}{shift()} = [gettimeofday] }
165
166sub _timing_elapsed {
167 my ($c, $name) = @_;
168 return undef unless my $started = $c->stash->{'mojo.timing'}{$name};
169 return tv_interval($started, [gettimeofday()]);
170}
171
172sub _timing_rps { $_[1] == 0 ? undef : sprintf '%.3f', 1 / $_[1] }
173
174sub _timing_server_timing {
175 my ($c, $metric, $desc, $dur) = @_;
176 my $value = $metric;
177 $value .= qq{;desc="$desc"} if defined $desc;
178 $value .= ";dur=$dur" if defined $dur;
179 $c->res->headers->append('Server-Timing' => $value);
180}
181
182sub _url_with {
183 my $c = shift;
184 return $c->url_for(@_)->query($c->req->url->query->clone);
185}
186
1871;
188
189=encoding utf8
190
191=head1 NAME
192
193Mojolicious::Plugin::DefaultHelpers - Default helpers plugin
194
195=head1 SYNOPSIS
196
197 # Mojolicious
198 $app->plugin('DefaultHelpers');
199
200 # Mojolicious::Lite
201 plugin 'DefaultHelpers';
202
203=head1 DESCRIPTION
204
205L<Mojolicious::Plugin::DefaultHelpers> is a collection of helpers for
206L<Mojolicious>.
207
208This is a core plugin, that means it is always enabled and its code a good
209example for learning to build new plugins, you're welcome to fork it.
210
211See L<Mojolicious::Plugins/"PLUGINS"> for a list of plugins that are available
212by default.
213
214=head1 HELPERS
215
216L<Mojolicious::Plugin::DefaultHelpers> implements the following helpers.
217
218=head2 accepts
219
220 my $formats = $c->accepts;
221 my $format = $c->accepts('html', 'json', 'txt');
222
223Select best possible representation for resource from C<format> C<GET>/C<POST>
224parameter, C<format> stash value or C<Accept> request header with
225L<Mojolicious::Renderer/"accepts">, defaults to returning the first extension if
226no preference could be detected.
227
228 # Check if JSON is acceptable
229 $c->render(json => {hello => 'world'}) if $c->accepts('json');
230
231 # Check if JSON was specifically requested
232 $c->render(json => {hello => 'world'}) if $c->accepts('', 'json');
233
234 # Unsupported representation
235 $c->render(data => '', status => 204)
236 unless my $format = $c->accepts('html', 'json');
237
238 # Detected representations to select from
239 my @formats = @{$c->accepts};
240
241=head2 app
242
243 %= app->secrets->[0]
244
245Alias for L<Mojolicious::Controller/"app">.
246
247=head2 b
248
249 %= b('Joel is a slug')->slugify
250
251Turn string into a L<Mojo::ByteStream> object.
252
253=head2 c
254
255 %= c('a', 'b', 'c')->shuffle->join
256
257Turn list into a L<Mojo::Collection> object.
258
259=head2 config
260
261 %= config 'something'
262
263Alias for L<Mojolicious/"config">.
264
265=head2 content
266
267 %= content foo => begin
268 test
269 % end
270 %= content bar => 'Hello World!'
271 %= content 'foo'
272 %= content 'bar'
273 %= content
274
275Store partial rendered content in a named buffer and retrieve it later,
276defaults to retrieving the named buffer C<content>, which is used by the
277renderer for the C<layout> and C<extends> features. New content will be ignored
278if the named buffer is already in use.
279
280=head2 content_for
281
282 % content_for foo => begin
283 test
284 % end
285 %= content_for 'foo'
286
287Same as L</"content">, but appends content to named buffers if they are already
288in use.
289
290 % content_for message => begin
291 Hello
292 % end
293 % content_for message => begin
294 world!
295 % end
296 %= content 'message'
297
298=head2 content_with
299
300 % content_with foo => begin
301 test
302 % end
303 %= content_with 'foo'
304
305Same as L</"content">, but replaces content of named buffers if they are
306already in use.
307
308 % content message => begin
309 world!
310 % end
311 % content_with message => begin
312 Hello <%= content 'message' %>
313 % end
314 %= content 'message'
315
316=head2 csrf_token
317
318 %= csrf_token
319
320Get CSRF token from L</"session">, and generate one if none exists.
321
322=head2 current_route
323
324 % if (current_route 'login') {
325 Welcome to Mojolicious!
326 % }
327 %= current_route
328
329Check or get name of current route.
330
331=head2 dumper
332
333 %= dumper {some => 'data'}
334
335Dump a Perl data structure with L<Mojo::Util/"dumper">, very useful for
336debugging.
337
338=head2 extends
339
340 % extends 'blue';
341 % extends 'blue', title => 'Blue!';
342
343Set C<extends> stash value, all additional key/value pairs get merged into the
344L</"stash">.
345
346=head2 flash
347
348 %= flash 'foo'
349
350Alias for L<Mojolicious::Controller/"flash">.
351
352=head2 inactivity_timeout
353
354 $c = $c->inactivity_timeout(3600);
355
356Use L<Mojo::IOLoop/"stream"> to find the current connection and increase
357timeout if possible.
358
359 # Longer version
360 Mojo::IOLoop->stream($c->tx->connection)->timeout(3600);
361
362=head2 include
363
364 %= include 'menubar'
365 %= include 'menubar', format => 'txt'
366
367Alias for L<Mojolicious::Controller/"render_to_string">.
368
369=head2 is_fresh
370
371 my $bool = $c->is_fresh;
372 my $bool = $c->is_fresh(etag => 'abc');
373 my $bool = $c->is_fresh(last_modified => $epoch);
374
375Check freshness of request by comparing the C<If-None-Match> and
376C<If-Modified-Since> request headers to the C<ETag> and C<Last-Modified>
377response headers with L<Mojolicious::Static/"is_fresh">.
378
379 # Add ETag/Last-Modified headers and check freshness before rendering
380 $c->is_fresh(etag => 'abc', last_modified => 1424985708)
381 ? $c->rendered(304)
382 : $c->render(text => 'I ♥ Mojolicious!');
383
384=head2 layout
385
386 % layout 'green';
387 % layout 'green', title => 'Green!';
388
389Set C<layout> stash value, all additional key/value pairs get merged into the
390L</"stash">.
391
392=head2 param
393
394 %= param 'foo'
395
396Alias for L<Mojolicious::Controller/"param">.
397
398=head2 reply->asset
399
400 $c->reply->asset(Mojo::Asset::File->new);
401
402Reply with a L<Mojo::Asset::File> or L<Mojo::Asset::Memory> object using
403L<Mojolicious::Static/"serve_asset">, and perform content negotiation with
404C<Range>, C<If-Modified-Since> and C<If-None-Match> headers.
405
406 # Serve asset with custom modification time
407 my $asset = Mojo::Asset::Memory->new;
408 $asset->add_chunk('Hello World!')->mtime(784111777);
409 $c->res->headers->content_type('text/plain');
410 $c->reply->asset($asset);
411
412 # Serve static file if it exists
413 if (my $asset = $c->app->static->file('images/logo.png')) {
414 $c->res->headers->content_type('image/png');
415 $c->reply->asset($asset);
416 }
417
418=head2 reply->exception
419
420 $c = $c->reply->exception('Oops!');
421 $c = $c->reply->exception(Mojo::Exception->new);
422
423Render the exception template C<exception.$mode.$format.*> or
424C<exception.$format.*> and set the response status code to C<500>. Also sets
425the stash values C<exception> to a L<Mojo::Exception> object and C<snapshot> to
426a copy of the L</"stash"> for use in the templates.
427
428=head2 reply->file
429
430 $c->reply->file('/etc/passwd');
431
432Reply with a static file from an absolute path anywhere on the file system using
433L<Mojolicious/"static">.
434
435 # Longer version
436 $c->reply->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
437
438 # Serve file from an absolute path with a custom content type
439 $c->res->headers->content_type('application/myapp');
440 $c->reply->file('/home/sri/foo.txt');
441
442 # Serve file from a secret application directory
443 $c->reply->file($c->app->home->child('secret', 'file.txt'));
444
445=head2 reply->not_found
446
447 $c = $c->reply->not_found;
448
449Render the not found template C<not_found.$mode.$format.*> or
450C<not_found.$format.*> and set the response status code to C<404>. Also sets
451the stash value C<snapshot> to a copy of the L</"stash"> for use in the
452templates.
453
454=head2 reply->static
455
456 my $bool = $c->reply->static('images/logo.png');
457 my $bool = $c->reply->static('../lib/MyApp.pm');
458
459Reply with a static file using L<Mojolicious/"static">, usually from the
460C<public> directories or C<DATA> sections of your application. Note that this
461helper uses a relative path, but does not protect from traversing to parent
462directories.
463
464 # Serve file from a relative path with a custom content type
465 $c->res->headers->content_type('application/myapp');
466 $c->reply->static('foo.txt');
467
468=head2 session
469
470 %= session 'foo'
471
472Alias for L<Mojolicious::Controller/"session">.
473
474=head2 stash
475
476 %= stash 'foo'
477 % stash foo => 'bar';
478
479Alias for L<Mojolicious::Controller/"stash">.
480
481 %= stash('name') // 'Somebody'
482
483=head2 timing->begin
484
485 $c->timing->begin('foo');
486
487Create named timestamp for L<"timing-E<gt>elapsed">. Note that this helper is
488EXPERIMENTAL and might change without warning!
489
490=head2 timing->elapsed
491
492 my $elapsed = $c->timing->elapsed('foo');
493
494Return fractional amount of time in seconds since named timstamp has been
495created with L</"timing-E<gt>begin"> or C<undef> if no such timestamp exists.
496Note that this helper is EXPERIMENTAL and might change without warning!
497
498 # Log timing information
499 $c->timing->begin('database_stuff');
500 ...
501 my $elapsed = $c->timing->elapsed('database_stuff');
502 $c->app->log->debug("Database stuff took $elapsed seconds");
503
504=head2 timing->rps
505
506 my $rps = $c->timing->rps('0.001');
507
508Return fractional number of requests that could be performed in one second if
509every singe one took the given amount of time in seconds or C<undef> if the
510number is too low. Note that this helper is EXPERIMENTAL and might change
511without warning!
512
513 # Log more timing information
514 $c->timing->begin('web_stuff');
515 ...
516 my $elapsed = $c->timing->elapsed('web_stuff');
517 my $rps = $c->timing->rps($elapsed);
518 $c->app->log->debug("Web stuff took $elapsed seconds ($rps per second)");
519
520=head2 timing->server_timing
521
522 $c->timing->server_timing('metric');
523 $c->timing->server_timing('metric', 'Some Description');
524 $c->timing->server_timing('metric', 'Some Description', '0.001');
525
526Create C<Server-Timing> header with optional description and duration. Note that
527this helper is EXPERIMENTAL and might change without warning!
528
529 # "Server-Timing: miss"
530 $c->timing->server_timing('miss');
531
532 # "Server-Timing: dc;desc=atl"
533 $c->timing->server_timing('dc', 'atl');
534
535 # "Server-Timing: db;desc=Database;dur=0.0001"
536 $c->timing->begin('database_stuff');
537 ...
538 my $elapsed = $c->timing->elapsed('database_stuff');
539 $c->timing->server_timing('db', 'Database', $elapsed);
540
541 # "Server-Timing: miss, dc;desc=atl"
542 $c->timing->server_timing('miss');
543 $c->timing->server_timing('dc', 'atl');
544
545=head2 title
546
547 %= title
548 % title 'Welcome!';
549 % title 'Welcome!', foo => 'bar';
550
551Get or set C<title> stash value, all additional key/value pairs get merged into
552the L</"stash">.
553
554=head2 ua
555
556 %= ua->get('mojolicious.org')->result->dom->at('title')->text
557
558Alias for L<Mojolicious/"ua">.
559
560=head2 url_for
561
562 %= url_for 'named', controller => 'bar', action => 'baz'
563
564Alias for L<Mojolicious::Controller/"url_for">.
565
566 %= url_for('/index.html')->query(foo => 'bar')
567
568=head2 url_with
569
570 %= url_with 'named', controller => 'bar', action => 'baz'
571
572Does the same as L</"url_for">, but inherits query parameters from the current
573request.
574
575 %= url_with->query([page => 2])
576
577=head2 validation
578
579 %= validation->param('foo')
580
581Alias for L<Mojolicious::Controller/"validation">.
582
583=head1 METHODS
584
585L<Mojolicious::Plugin::DefaultHelpers> inherits all methods from
586L<Mojolicious::Plugin> and implements the following new ones.
587
588=head2 register
589
590 $plugin->register(Mojolicious->new);
591
592Register helpers in L<Mojolicious> application.
593
594=head1 SEE ALSO
595
596L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
597
598=cut
Note: See TracBrowser for help on using the repository browser.