File Coverage

blib/lib/Yukki/Web/View.pm
Criterion Covered Total %
statement 38 101 37.6
branch 0 16 0.0
condition 0 18 0.0
subroutine 13 22 59.0
pod 6 6 100.0
total 57 163 34.9


line stmt bran cond sub pod time code
1             package Yukki::Web::View;
2             $Yukki::Web::View::VERSION = '0.99_01'; # TRIAL
3              
4 4     4   41 $Yukki::Web::View::VERSION = '0.9901';use v5.24;
  4         14  
5 4     4   17 use utf8;
  4         8  
  4         44  
6 4     4   100 use Moo;
  4         9  
  4         18  
7              
8 4     4   2833 use File::Slurp qw( read_file );
  4         20602  
  4         233  
9 4     4   1717 use Type::Params qw( validate );
  4         93307  
  4         37  
10 4     4   865 use Scalar::Util qw( blessed reftype );
  4         8  
  4         182  
11 4     4   1589 use Spreadsheet::Engine;
  4         93928  
  4         128  
12 4     4   2653 use Template::Pure;
  4         184215  
  4         124  
13 4     4   2006 use Text::MultiMarkdown;
  4         119505  
  4         190  
14 4     4   407 use Try::Tiny;
  4         994  
  4         192  
15 4     4   424 use Type::Utils;
  4         3643  
  4         48  
16 4     4   6064 use Types::Standard qw( Dict Str ArrayRef HashRef slurpy );
  4         9  
  4         45  
17              
18 4     4   5005 use namespace::clean;
  4         7568  
  4         35  
19              
20             # ABSTRACT: base class for Yukki::Web views
21              
22              
23             has app => (
24             is => 'ro',
25             isa => class_type('Yukki::Web'),
26             required => 1,
27             weak_ref => 1,
28             handles => 'Yukki::Role::App',
29             );
30              
31              
32             has markdown => (
33             is => 'ro',
34             isa => class_type('Text::MultiMarkdown'),
35             required => 1,
36             lazy => 1,
37             builder => '_build_markdown',
38             handles => {
39             'format_markdown' => 'markdown',
40             },
41             );
42              
43             sub _build_markdown {
44 0     0     Text::MultiMarkdown->new(
45             markdown_in_html_blocks => 1,
46             heading_ids => 0,
47             );
48             }
49              
50              
51             has messages_template => (
52             is => 'ro',
53             isa => class_type('Template::Pure'),
54             lazy => 1,
55             builder => '_build_messages_template',
56             );
57              
58             sub _build_messages_template {
59 0     0     my $self = shift;
60 0           return $self->prepare_template(
61             template => 'messages.html',
62             directives => [
63             '.error' => {
64             'error<-errors' => [
65             '.' => 'error',
66             ],
67             },
68             '.warning' => {
69             'warning<-warnings' => [
70             '.' => 'warning',
71             ],
72             },
73             '.info' => {
74             'one_info<-info' => [
75             '.' => 'one_info',
76             ],
77             },
78             ],
79             );
80             }
81              
82             has _page_templates => (
83             is => 'ro',
84             isa => HashRef,
85             required => 1,
86             default => sub { +{} },
87             );
88              
89              
90             has links_template => (
91             is => 'ro',
92             isa => class_type('Template::Pure'),
93             lazy => 1,
94             builder => '_build_links_template',
95             );
96              
97             sub _build_links_template {
98 0     0     my $self = shift;
99 0           $self->prepare_template(
100             template => 'links.html',
101             directives => [
102             '.links' => {
103             'link<-links' => [
104             'a' => 'link.label',
105             'a@href' => 'link.href',
106             ],
107             },
108             ],
109             );
110             }
111              
112              
113             sub page_template {
114 0     0 1   my ($self, $which) = @_;
115              
116             return $self->_page_templates->{ $which }
117 0 0         if $self->_page_templates->{ $which };
118              
119 0   0       my $view = $which // 'default';
120 0   0       my $view_args = $self->app->settings->page_views->{ $view }
121             // { template => 'shell.html' };
122 0   0       $view_args->{directives} //= [];
123              
124             my %menu_vars = map {
125 0           my $menu_name = $_;
126 0           "#nav-$menu_name .navigation" => {
127             "menu_item<-$menu_name" => [
128             'a' => 'menu_item.label',
129             'a@href' => 'menu_item.href',
130             ],
131             },
132 0           } @{ $self->app->settings->menu_names };
  0            
133              
134             return $self->_page_templates->{ $which } = $self->prepare_template(
135             template => $view_args->{template},
136             directives => [
137             $view_args->{directives}->@*,
138 0           'head script.local' => {
139             'script<-scripts' => [
140             '@src' => 'script',
141             ],
142             },
143             'head link.local' => {
144             'link<-links' => [
145             '@href' => 'link',
146             ],
147             },
148             '#messages' => 'messages | encoded_string',
149             'title' => 'main_title',
150             '.masthead-title' => 'title',
151             %menu_vars,
152             '#breadcrumb li' => {
153             'crumb<-breadcrumb' => [
154             'a' => 'crumb.label',
155             'a@href' => 'crumb.href',
156             ],
157             },
158             '#content' => 'content | encoded_string',
159             ],
160             );
161             }
162              
163              
164             sub prepare_template {
165 0     0 1   my ($self, $opt)
166             = validate(\@_, class_type(__PACKAGE__),
167             slurpy Dict[
168             template => Str,
169             directives => ArrayRef,
170             ]);
171 0           my ($template, $directives) = @{$opt}{qw( template directives )};
  0            
172              
173 0           my $template_content = read_file(
174             $self->app->locate_dir('template_path', $template)
175             );
176              
177 0           return Template::Pure->new(
178             template => $template_content,
179             directives => $directives,
180             );
181             }
182              
183              
184             sub render_page {
185 0     0 1   my ($self, $opt)
186             = validate(\@_, class_type(__PACKAGE__),
187             slurpy Dict[
188             template => class_type('Template::Pure'),
189             context => class_type('Yukki::Web::Context'),
190             vars => HashRef,
191             ]);
192 0           my ($template, $ctx, $vars) = @{$opt}{qw( template context vars )};
  0            
193 0   0       $vars //= {};
194              
195 0 0         my $messages = $self->render(
    0          
    0          
196             template => $self->messages_template,
197             context => $ctx,
198             vars => {
199             errors => $ctx->has_errors ? [ $ctx->list_errors ] : undef,
200             warnings => $ctx->has_warnings ? [ $ctx->list_warnings ] : undef,
201             info => $ctx->has_info ? [ $ctx->list_info ] : undef,
202             },
203             );
204              
205 0           my ($main_title, $title);
206 0 0         if ($ctx->response->has_page_title) {
207 0           $title = $ctx->response->page_title;
208 0           $main_title = $ctx->response->page_title . ' - Yukki';
209             }
210             else {
211 0           $title = $main_title = 'Yukki';
212             }
213              
214             my %menu_vars = map {
215 0           $_ => $self->available_menu_items($ctx, $_)
216 0           } @{ $self->app->settings->menu_names };
  0            
217              
218 0           my @scripts = $self->app->settings->all_scripts;
219 0           my @styles = $self->app->settings->all_styles;
220              
221 0   0       my $view = $ctx->request->parameters->{view} // 'default';
222              
223 0   0       $vars->{'head script.local'} //= [];
224 0   0       $vars->{'head link.local'} //= [];
225              
226             return $self->render(
227             template => $self->page_template($view),
228             context => $ctx,
229             vars => {
230             $vars->%*,
231             scripts => [
232 0           map { $ctx->rebase_url($_) }
233             @scripts,
234             $vars->{'head script.local'}->@*,
235             ],
236             links => [
237 0           map { $ctx->rebase_url($_) }
238             @styles,
239             $vars->{'head link.local'}->@*,
240             ],
241             'messages' => $messages,
242             'main_title' => $main_title,
243             'title' => $title,
244             %menu_vars,
245             'breadcrumb' => $ctx->response->has_breadcrumb ? [
246             map {
247 0 0         +{
248             %$_,
249 0           href => $ctx->rebase_url($_->{href}),
250             }
251             } $ctx->response->breadcrumb_links
252             ] : undef,
253             'content' => $self->render(
254             template => $template,
255             context => $ctx,
256             vars => $vars,
257             ),
258             },
259             );
260             }
261              
262              
263             sub available_menu_items {
264 0     0 1   my ($self, $ctx, $name) = @_;
265              
266             my @items = map {
267             +{
268             %$_,
269 0           href => $ctx->rebase_url($_->{href}),
270             },
271             } grep {
272 0           my $url = $_->{href}; $url =~ s{\?.*$}{};
  0            
  0            
273              
274 0           my $match = $self->app->router->match($url);
275 0 0         return unless $match;
276 0           my $access_level_needed = $match->access_level;
277             $self->check_access(
278             user => $ctx->session->{user},
279 0   0       repository => $match->mapping->{repository} // '-',
280             needs => $access_level_needed,
281             );
282             } $ctx->response->navigation_menu($name);
283              
284 0 0         return @items ? \@items : undef;
285             }
286              
287              
288             sub render_links {
289 0     0 1   my ($self, $opt)
290             = validate(\@_, class_type(__PACKAGE__),
291             slurpy Dict[
292             context => class_type('Yukki::Web::Context'),
293             links => ArrayRef[HashRef],
294             ]);
295 0           my ($ctx, $links) = @{$opt}{qw( context links )};
  0            
296              
297             return $self->render(
298             template => $self->links_template,
299             context => $ctx,
300             vars => {
301             links => [ map {
302 0           +{
303             label => $_->{label},
304 0           href => $ctx->rebase_url($_->{href}),
305             }
306             } @$links ],
307             },
308             );
309             }
310              
311              
312             sub render {
313 0     0 1   my ($self, $opt)
314             = validate(\@_, class_type(__PACKAGE__),
315             slurpy Dict[
316             template => class_type('Template::Pure'),
317             context => class_type('Yukki::Web::Context'),
318             vars => HashRef,
319             ]);
320 0           my ($template, $ctx, $vars) = @{$opt}{qw( template context vars )};
  0            
321 0   0       $vars //= {};
322              
323 0           my %vars = (
324             %$vars,
325             ctx => $ctx,
326             view => $self,
327             );
328              
329 0           return $template->render($vars);
330             }
331              
332             1;
333              
334             __END__
335              
336             =pod
337              
338             =encoding UTF-8
339              
340             =head1 NAME
341              
342             Yukki::Web::View - base class for Yukki::Web views
343              
344             =head1 VERSION
345              
346             version 0.99_01
347              
348             =head1 DESCRIPTION
349              
350             This is the base class for all L<Yukki::Web> views.
351              
352             =head1 ATTRIBUTES
353              
354             =head2 app
355              
356             This is the L<Yukki::Web> singleton.
357              
358             =head2 markdown
359              
360             This is the L<Text::MultiMarkdown> object for rendering L</yukkitext>. Do not
361             use.
362              
363             Provides a C<format_markdown> method delegated to C<markdown>. Do not use.
364              
365             =head2 messages_template
366              
367             This is the template used to render info, warning, and error messages to the page.
368              
369             =head2 links_template
370              
371             This is the template object used to render links.
372              
373             =head1 METHODS
374              
375             =head2 page_template
376              
377             my $template = $self->page_template('default');
378              
379             Returns the template used to render pages for the given style name.
380              
381             =head2 prepare_template
382              
383             my $template = $self->prepare_template({
384             template => 'foo.html',
385             directives => { ... },
386             });
387              
388             This prepares a template for later rendering.
389              
390             The C<template> is the name of the template file to use.
391              
392             The C<directives> are the L<Template::Pure> directives to apply data given at render time to modify the template to create the output.
393              
394             =head2 render_page
395              
396             my $document = $self->render_page({
397             template => 'foo.html',
398             context => $ctx,
399             vars => { ... },
400             });
401              
402             This renders the given template and places it into the content section of the
403             F<shell.html> template.
404              
405             The C<context> is used to render parts of the shell template.
406              
407             The C<vars> are processed against the given template with L<Template::Pure>.
408              
409             =head2 available_menu_items
410              
411             my @items = $self->available_menu_items($ctx, 'menu_name');
412              
413             Retrieves the navigation menu from the L<Yukki::Web::Response> and purges any links that the current user does not have access to.
414              
415             =head2 render_links
416              
417             my $document = $self->render_links($ctx, \@navigation_links);
418              
419             This renders a set of links using the F<links.html> template.
420              
421             =head2 render
422              
423             my $document = $self->render({
424             template => $template,
425             vars => { ... },
426             });
427              
428             This renders the given L<Template::Pure>. The C<vars> are
429             used as the ones passed to the C<process> method.
430              
431             =head1 AUTHOR
432              
433             Andrew Sterling Hanenkamp <hanenkamp@cpan.org>
434              
435             =head1 COPYRIGHT AND LICENSE
436              
437             This software is copyright (c) 2017 by Qubling Software LLC.
438              
439             This is free software; you can redistribute it and/or modify it under
440             the same terms as the Perl 5 programming language system itself.
441              
442             =cut