File Coverage

blib/lib/Yukki/Web/View.pm
Criterion Covered Total %
statement 35 98 35.7
branch 0 16 0.0
condition 0 18 0.0
subroutine 12 21 57.1
pod 6 6 100.0
total 53 159 33.3


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