source: main/trunk/greenstone2/perllib/cpan/Mojolicious/Renderer.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: 12.2 KB
Line 
1package Mojolicious::Renderer;
2use Mojo::Base -base;
3
4use Mojo::Cache;
5use Mojo::File 'path';
6use Mojo::JSON 'encode_json';
7use Mojo::Home;
8use Mojo::Loader 'data_section';
9use Mojo::Util qw(decamelize encode md5_sum monkey_patch);
10
11has cache => sub { Mojo::Cache->new };
12has classes => sub { ['main'] };
13has default_format => 'html';
14has 'default_handler';
15has encoding => 'UTF-8';
16has [qw(handlers helpers)] => sub { {} };
17has paths => sub { [] };
18
19# Bundled templates
20my $TEMPLATES = Mojo::Home->new->mojo_lib_dir->child('Mojolicious', 'resources',
21 'templates');
22
23sub DESTROY { Mojo::Util::_teardown($_) for @{shift->{namespaces}} }
24
25sub accepts {
26 my ($self, $c) = (shift, shift);
27
28 # List representations
29 my $req = $c->req;
30 my $fmt = $req->param('format') || $c->stash->{format};
31 my @exts = $fmt ? ($fmt) : ();
32 push @exts, @{$c->app->types->detect($req->headers->accept)};
33 return \@exts unless @_;
34
35 # Find best representation
36 for my $ext (@exts) { $ext eq $_ and return $ext for @_ }
37 return @exts ? undef : shift;
38}
39
40sub add_handler { $_[0]->handlers->{$_[1]} = $_[2] and return $_[0] }
41
42sub add_helper {
43 my ($self, $name, $cb) = @_;
44 $self->helpers->{$name} = $cb;
45 delete $self->{proxy};
46 return $self;
47}
48
49sub get_data_template {
50 my ($self, $options) = @_;
51 return undef unless my $template = $self->template_name($options);
52 return data_section $self->{index}{$template}, $template;
53}
54
55sub get_helper {
56 my ($self, $name) = @_;
57
58 if (my $h = $self->{proxy}{$name} || $self->helpers->{$name}) { return $h }
59
60 my $found;
61 my $class = 'Mojolicious::Renderer::Helpers::' . md5_sum "$name:$self";
62 my $re = length $name ? qr/^(\Q$name\E\.([^.]+))/ : qr/^(([^.]+))/;
63 for my $key (keys %{$self->helpers}) {
64 $key =~ $re ? ($found, my $method) = (1, $2) : next;
65 my $sub = $self->get_helper($1);
66 monkey_patch $class, $method => sub { ${shift()}->$sub(@_) };
67 }
68
69 $found ? push @{$self->{namespaces}}, $class : return undef;
70 return $self->{proxy}{$name} = sub { bless \(my $dummy = shift), $class };
71}
72
73sub render {
74 my ($self, $c, $args) = @_;
75
76 my $stash = $c->stash;
77 my $options = {
78 encoding => $self->encoding,
79 handler => $stash->{handler},
80 template => delete $stash->{template},
81 variant => $stash->{variant}
82 };
83 my $inline = $options->{inline} = delete $stash->{inline};
84 $options->{handler} //= $self->default_handler if defined $inline;
85 $options->{format} = $stash->{format} || $self->default_format;
86
87 # Data
88 return delete $stash->{data}, $options->{format} if defined $stash->{data};
89
90 # Text
91 return _maybe($options->{encoding}, delete $stash->{text}), $options->{format}
92 if defined $stash->{text};
93
94 # JSON
95 return encode_json(delete $stash->{json}), 'json' if exists $stash->{json};
96
97 # Template or templateless handler
98 $options->{template} //= $self->template_for($c);
99 return () unless $self->_render_template($c, \my $output, $options);
100
101 # Inheritance
102 my $content = $stash->{'mojo.content'} ||= {};
103 local $content->{content} = $output =~ /\S/ ? $output : undef
104 if $stash->{extends} || $stash->{layout};
105 while ((my $next = _next($stash)) && !defined $inline) {
106 @$options{qw(handler template)} = ($stash->{handler}, $next);
107 $options->{format} = $stash->{format} || $self->default_format;
108 if ($self->_render_template($c, \my $tmp, $options)) { $output = $tmp }
109 $content->{content} //= $output if $output =~ /\S/;
110 }
111
112 return $output if $args->{'mojo.string'};
113 return _maybe($options->{encoding}, $output), $options->{format};
114}
115
116sub template_for {
117 my ($self, $c) = @_;
118
119 # Normal default template
120 my $stash = $c->stash;
121 my ($controller, $action) = @$stash{qw(controller action)};
122 return join '/', split('-', decamelize $controller), $action
123 if $controller && $action;
124
125 # Try the route name if we don't have controller and action
126 return undef unless my $route = $c->match->endpoint;
127 return $route->name;
128}
129
130sub template_handler {
131 my ($self, $options) = @_;
132 return undef unless my $file = $self->template_name($options);
133 return $self->default_handler unless my $handlers = $self->{templates}{$file};
134 return $handlers->[0];
135}
136
137sub template_name {
138 my ($self, $options) = @_;
139
140 return undef unless defined(my $template = $options->{template});
141 return undef unless my $format = $options->{format};
142 $template .= ".$format";
143
144 $self->warmup unless $self->{templates};
145
146 # Variants
147 my $handler = $options->{handler};
148 if (defined(my $variant = $options->{variant})) {
149 $variant = "$template+$variant";
150 my $handlers = $self->{templates}{$variant} // [];
151 $template = $variant
152 if @$handlers && !defined $handler || grep { $_ eq $handler } @$handlers;
153 }
154
155 return defined $handler ? "$template.$handler" : $template;
156}
157
158sub template_path {
159 my ($self, $options) = @_;
160 return undef unless my $name = $self->template_name($options);
161 my @parts = split '/', $name;
162 -r and return $_
163 for map { path($_, @parts)->to_string } @{$self->paths}, $TEMPLATES;
164 return undef;
165}
166
167sub warmup {
168 my $self = shift;
169
170 my ($index, $templates) = @$self{qw(index templates)} = ({}, {});
171
172 # Handlers for templates
173 for my $path (@{$self->paths}, $TEMPLATES) {
174 s/\.(\w+)$// and push @{$templates->{$_}}, $1
175 for path($path)->list_tree->map(sub { join '/', @{$_->to_rel($path)} })
176 ->each;
177 }
178
179 # Handlers and classes for DATA templates
180 for my $class (reverse @{$self->classes}) {
181 $index->{$_} = $class for my @keys = sort keys %{data_section $class};
182 s/\.(\w+)$// and unshift @{$templates->{$_}}, $1 for reverse @keys;
183 }
184}
185
186sub _maybe { $_[0] ? encode @_ : $_[1] }
187
188sub _next {
189 my $stash = shift;
190 return delete $stash->{extends} if $stash->{extends};
191 return undef unless my $layout = delete $stash->{layout};
192 return join '/', 'layouts', $layout;
193}
194
195sub _render_template {
196 my ($self, $c, $output, $options) = @_;
197
198 my $handler = $options->{handler} ||= $self->template_handler($options);
199 return undef unless $handler;
200 $c->app->log->error(qq{No handler for "$handler" available}) and return undef
201 unless my $renderer = $self->handlers->{$handler};
202
203 $renderer->($self, $c, $output, $options);
204 return 1 if defined $$output;
205}
206
2071;
208
209=encoding utf8
210
211=head1 NAME
212
213Mojolicious::Renderer - Generate dynamic content
214
215=head1 SYNOPSIS
216
217 use Mojolicious::Renderer;
218
219 my $renderer = Mojolicious::Renderer->new;
220 push @{$renderer->classes}, 'MyApp::Controller::Foo';
221 push @{$renderer->paths}, '/home/sri/templates';
222
223=head1 DESCRIPTION
224
225L<Mojolicious::Renderer> is the standard L<Mojolicious> renderer.
226
227See L<Mojolicious::Guides::Rendering> for more.
228
229=head1 ATTRIBUTES
230
231L<Mojolicious::Renderer> implements the following attributes.
232
233=head2 cache
234
235 my $cache = $renderer->cache;
236 $renderer = $renderer->cache(Mojo::Cache->new);
237
238Renderer cache, defaults to a L<Mojo::Cache> object.
239
240=head2 classes
241
242 my $classes = $renderer->classes;
243 $renderer = $renderer->classes(['main']);
244
245Classes to use for finding templates in C<DATA> sections with L<Mojo::Loader>,
246first one has the highest precedence, defaults to C<main>. Only files with
247exactly two extensions will be used, like C<index.html.ep>. Note that for
248templates to be detected, these classes need to have already been loaded and
249added before L</"warmup"> is called, which usually happens automatically during
250application startup.
251
252 # Add another class with templates in DATA section
253 push @{$renderer->classes}, 'Mojolicious::Plugin::Fun';
254
255 # Add another class with templates in DATA section and higher precedence
256 unshift @{$renderer->classes}, 'Mojolicious::Plugin::MoreFun';
257
258=head2 default_format
259
260 my $default = $renderer->default_format;
261 $renderer = $renderer->default_format('html');
262
263The default format to render if C<format> is not set in the stash, defaults to
264C<html>. Note that changing the default away from C<html> is not recommended, as
265it has the potential to break, for example, plugins with bundled templates.
266
267=head2 default_handler
268
269 my $default = $renderer->default_handler;
270 $renderer = $renderer->default_handler('ep');
271
272The default template handler to use for rendering in cases where auto-detection
273doesn't work, like for C<inline> templates.
274
275=head2 encoding
276
277 my $encoding = $renderer->encoding;
278 $renderer = $renderer->encoding('koi8-r');
279
280Will encode generated content if set, defaults to C<UTF-8>. Note that many
281renderers such as L<Mojolicious::Plugin::EPRenderer> also use this value to
282determine if template files should be decoded before processing.
283
284=head2 handlers
285
286 my $handlers = $renderer->handlers;
287 $renderer = $renderer->handlers({epl => sub {...}});
288
289Registered handlers.
290
291=head2 helpers
292
293 my $helpers = $renderer->helpers;
294 $renderer = $renderer->helpers({url_for => sub {...}});
295
296Registered helpers.
297
298=head2 paths
299
300 my $paths = $renderer->paths;
301 $renderer = $renderer->paths(['/home/sri/templates']);
302
303Directories to look for templates in, first one has the highest precedence.
304
305 # Add another "templates" directory
306 push @{$renderer->paths}, '/home/sri/templates';
307
308 # Add another "templates" directory with higher precedence
309 unshift @{$renderer->paths}, '/home/sri/themes/blue/templates';
310
311=head1 METHODS
312
313L<Mojolicious::Renderer> inherits all methods from L<Mojo::Base> and implements
314the following new ones.
315
316=head2 accepts
317
318 my $all = $renderer->accepts(Mojolicious::Controller->new);
319 my $best = $renderer->accepts(Mojolicious::Controller->new, 'html', 'json');
320
321Select best possible representation for L<Mojolicious::Controller> object from
322C<format> C<GET>/C<POST> parameter, C<format> stash value, or C<Accept> request
323header, defaults to returning the first extension if no preference could be
324detected.
325
326=head2 add_handler
327
328 $renderer = $renderer->add_handler(epl => sub {...});
329
330Register a handler.
331
332 $renderer->add_handler(foo => sub {
333 my ($renderer, $c, $output, $options) = @_;
334 ...
335 $$output = 'Hello World!';
336 });
337
338=head2 add_helper
339
340 $renderer = $renderer->add_helper(url_for => sub {...});
341
342Register a helper.
343
344 $renderer->add_helper(foo => sub {
345 my ($c, @args) = @_;
346 ...
347 });
348
349=head2 get_data_template
350
351 my $template = $renderer->get_data_template({
352 template => 'foo/bar',
353 format => 'html',
354 handler => 'epl'
355 });
356
357Return a C<DATA> section template from L</"classes"> for an options hash
358reference with C<template>, C<format>, C<variant> and C<handler> values, or
359C<undef> if no template could be found, usually used by handlers.
360
361=head2 get_helper
362
363 my $helper = $renderer->get_helper('url_for');
364
365Get a helper by full name, generate a helper dynamically for a prefix, or return
366C<undef> if no helper or prefix could be found. Generated helpers return a
367proxy object containing the current controller object and on which nested
368helpers can be called.
369
370=head2 render
371
372 my ($output, $format) = $renderer->render(Mojolicious::Controller->new, {
373 template => 'foo/bar',
374 foo => 'bar'
375 });
376
377Render output through one of the renderers. See
378L<Mojolicious::Controller/"render"> for a more user-friendly interface.
379
380=head2 template_for
381
382 my $name = $renderer->template_for(Mojolicious::Controller->new);
383
384Return default template name for L<Mojolicious::Controller> object, or C<undef>
385if no name could be generated.
386
387=head2 template_handler
388
389 my $handler = $renderer->template_handler({
390 template => 'foo/bar',
391 format => 'html'
392 });
393
394Return handler for an options hash reference with C<template>, C<format> and
395C<variant> values, or C<undef> if no handler could be found.
396
397=head2 template_name
398
399 my $template = $renderer->template_name({
400 template => 'foo/bar',
401 format => 'html',
402 handler => 'epl'
403 });
404
405Return a template name for an options hash reference with C<template>,
406C<format>, C<variant> and C<handler> values, or C<undef> if no template could be
407found, usually used by handlers.
408
409=head2 template_path
410
411 my $path = $renderer->template_path({
412 template => 'foo/bar',
413 format => 'html',
414 handler => 'epl'
415 });
416
417Return the full template path for an options hash reference with C<template>,
418C<format>, C<variant> and C<handler> values, or C<undef> if the file does not
419exist in L</"paths">, usually used by handlers.
420
421=head2 warmup
422
423 $renderer->warmup;
424
425Prepare templates from L</"classes"> for future use.
426
427=head1 SEE ALSO
428
429L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
430
431=cut
Note: See TracBrowser for help on using the repository browser.