File Coverage

blib/lib/Rapi/Blog/Template/AccessStore.pm
Criterion Covered Total %
statement 45 98 45.9
branch 0 28 0.0
condition 0 11 0.0
subroutine 15 37 40.5
pod 0 19 0.0
total 60 193 31.0


line stmt bran cond sub pod time code
1             package Rapi::Blog::Template::AccessStore;
2 1     1   1224 use strict;
  1         2  
  1         27  
3 1     1   5 use warnings;
  1         2  
  1         28  
4              
5 1     1   5 use RapidApp::Util qw(:all);
  1         2  
  1         500  
6 1     1   7 use Rapi::Blog::Util;
  1         2  
  1         19  
7 1     1   4 use List::Util;
  1         2  
  1         53  
8              
9 1     1   6 use Moo;
  1         2  
  1         8  
10             extends 'RapidApp::Template::AccessStore';
11 1     1   2388 use Types::Standard ':all';
  1         2  
  1         9  
12              
13 1     1   39798 use Rapi::Blog::Scaffold;
  1         2  
  1         22  
14 1     1   530 use Rapi::Blog::Template::Dispatcher;
  1         3  
  1         36  
15              
16 1     1   8 use Plack::App::File;
  1         2  
  1         21  
17 1     1   5 use Plack::Builder;
  1         2  
  1         97  
18 1     1   6 use Plack::Middleware::ConditionalGET;
  1         3  
  1         1165  
19              
20 0   0 0     sub _cache_slots { (shift)->local_cache->{template_row_slot} //= {} }
21              
22             has 'BlogCfg', is => 'ro', required => 1, isa => HashRef;
23             has 'ScaffoldSet', is => 'ro', required => 1, isa => InstanceOf['Rapi::Blog::Scaffold::Set'];
24             has 'scaffold_cfg', is => 'ro', required => 1, isa => InstanceOf['Rapi::Blog::Scaffold::Config'];
25              
26             sub Dispatcher_for {
27 0     0 0   my ($self,@args) = @_;
28 0           my $path = join('/',@args);
29 0   0       $self->_cache_slots->{$path}{Dispatcher} ||= Rapi::Blog::Template::Dispatcher->new(
30             path => $path, AccessStore => $self, ctx => RapidApp->active_request_context
31             )->resolve
32             }
33              
34             sub PostDispatcher_for {
35 0     0 0   my $self = shift;
36 0 0         my $Dispatcher = $self->Dispatcher_for(@_) or return undef;
37 0 0         $Dispatcher->type eq 'Post' ? $Dispatcher : undef
38             }
39              
40             sub Post_name_for {
41 0     0 0   my $self = shift;
42 0 0         my $Dispatcher = $self->PostDispatcher_for(@_) or return undef;
43 0           $Dispatcher->name
44             }
45              
46              
47             #has 'scaffold_dir', is => 'ro', isa => InstanceOf['Path::Class::Dir'], required => 1;
48             #has 'scaffold_cnf', is => 'ro', isa => HashRef, required => 1;
49             #has 'static_paths', is => 'ro', isa => ArrayRef[Str], default => sub {[]};
50             #has 'private_paths', is => 'ro', isa => ArrayRef[Str], default => sub {[]};
51             #has 'default_ext', is => 'ro', isa => Maybe[Str], default => sub {undef};
52              
53             around 'template_external_tpl' => sub {
54             my ($orig,$self,@args) = @_;
55             my $template = join('/',@args);
56            
57             $self->Dispatcher_for($template)->claimed ? 1 : $self->$orig(@args)
58             };
59              
60              
61             # Deny post templates access to to privileged attributes such as the catalyst context object).
62             # This could be expanded later on to allow only certain posts to be able to access these attributes.
63             around 'template_admin_tpl' => sub {
64             my ($orig,$self,@args) = @_;
65             my $template = join('/',@args);
66             $self->Dispatcher_for($template)->restrict ? 0 : $self->$orig(@args)
67             };
68              
69              
70              
71              
72             sub templateData {
73 0     0 0   my ($self, $template) = @_;
74 0 0         die 'template name argument missing!' unless ($template);
75            
76 0           $self->Dispatcher_for($template)->template_vars
77             }
78              
79             # -----------------
80             # Access class API:
81              
82             around '_get_default_template_vars' => sub {
83             my ($orig,$self,@args) = @_;
84             my $c = RapidApp->active_request_context;
85            
86             my $template = join('/',@args);
87             my $recaptcha_config = $self->BlogCfg->{recaptcha_config};
88            
89             my $vars = {
90             %{ $self->$orig(@args) },
91             %{ $self->templateData($template) || {} },
92            
93             BlogCfg => $self->BlogCfg,
94             scaffold => $self->scaffold_cfg,
95             list_posts => sub { $self->Model->resultset('Post') ->list_posts(@_) },
96             get_posts => sub { $self->Model->resultset('Post') ->get_posts(@_) },
97             list_tags => sub { $self->Model->resultset('Tag') ->list_tags(@_) },
98             list_categories => sub { $self->Model->resultset('Category') ->list_categories(@_) },
99             list_sections => sub { $self->Model->resultset('Section') ->list_sections(@_) },
100             list_users => sub { $self->Model->resultset('User') ->list_users(@_) },
101            
102             # TODO: consider mount_url
103             request_path => sub { $c ? join('','/',$c->req->path) : undef },
104            
105             User => sub { Rapi::Blog::Util->get_User },
106            
107             # Path to the 'Remote' controller
108             remote_action_path => sub { $c ? join('',$c->mount_url,'/remote') : undef },
109            
110             add_post_path => sub {
111             my $ns = $c->module_root_namespace;
112             if(my $mode = shift) {
113             $mode = lc($mode);
114             # Note: 'direct' is not useful un this context since add relies on opening new tab
115             die "add_post_path(): bad argument '$mode' -- must be undef or 'navable'"
116             unless ($mode eq 'navable');
117             return join('',$c->mount_url,'/rapidapp/module/',$mode,'/',$ns,'/main/db/db_post/add')
118             }
119             else {
120             return join('',$c->mount_url,'/',$ns,'/#!/',$ns,'/main/db/db_post/add')
121             }
122             },
123            
124             resolve_section_id => sub {
125             if(my $id = shift) {
126             my $Section = $self->Model->resultset('Section')
127             ->search_rs({ 'me.id' => $id })
128             ->first;
129             return $Section ? $Section->name : $id
130             }
131             },
132            
133             # Expose this here so its available to non-priv templates:
134             mount_url => sub { $c->mount_url },
135            
136             accessed_site => sub {
137             $c && $c->req or return undef;
138             my $uri = $c->req->uri or return undef;
139             my $host = $c->req->env->{HTTP_HOST} || $uri->host_port;
140             my $proto = $c->req->env->{HTTP_X_FORWARDED_PROTO} || $uri->scheme || 'http';
141             join('',$proto,'://',$host)
142             },
143            
144             local_info => sub {
145             my $new = shift;
146            
147             my $uri = $c->req->uri or return undef;
148             my $session = $c->session or return undef;
149             my $err = $session->{local_info}{$uri->path};
150            
151             if(defined $new) {
152             if(!$new || lc($new) eq 'clear') {
153             exists $session->{local_info}{$uri->path} and delete $session->{local_info}{$uri->path}
154             }
155             else {
156             $session->{local_info}{$uri->path} = $new
157             }
158             }
159              
160             $err
161             },
162            
163             recaptcha_script_tag => sub {
164             $recaptcha_config
165             ? '<script src="https://www.google.com/recaptcha/api.js" async defer></script>'
166             : ''
167             },
168            
169             recaptcha_form_item => sub {
170             $recaptcha_config
171             ? join('',
172             '<div class="g-recaptcha" data-sitekey="',
173             $recaptcha_config->{public_key},
174             '"></div>'
175             ) : ''
176             },
177            
178             ## All of these work the same:
179             # [% ppRender.TextMarkdown(content) %]
180             # [% ppRender('TextMarkdown',content) %]
181             # [% ppRender('Rapi::Blog::Template::Postprocessor::TextMarkdown',content) %]
182             # [% SET pRen = ppRender('TextMarkdown') %]
183             # [% pRen(content) %]
184             ppRender => sub {
185             if (scalar(@_) == 0) {
186             return Rapi::Blog::Util::ppRender->new
187             }
188             elsif(scalar(@_) == 1) {
189             my $pRen = Rapi::Blog::Util::ppRender->new( _post_processor => (shift) );
190             return sub { $pRen->_call_process(@_) }
191             }
192             else {
193             return Rapi::Blog::Util::ppRender->new->_call_process(@_)
194             }
195             }
196             };
197              
198             #if (my $Scaffold = $self->DispatchRule_for($template)->Scaffold) {
199             # $vars->{scaffold} = $Scaffold->config;
200             #}
201            
202             return $vars
203            
204             };
205              
206             around '_get_admin_template_vars' => sub {
207             my ($orig,$self,@args) = @_;
208              
209             return {
210             %{ $self->$orig(@args) },
211            
212             ensure_logged_out => sub {
213             if (Rapi::Blog::Util->get_User) {
214             RapidApp->active_request_context->logout
215             }
216             ''
217             }
218            
219            
220             };
221              
222             };
223              
224              
225             # -----------------
226             # Store class API:
227              
228              
229 1     1   8 use DateTime;
  1         2  
  1         26  
230 1     1   546 use Date::Parse;
  1         4421  
  1         115  
231 1     1   8 use Path::Class qw/file dir/;
  1         1  
  1         864  
232              
233             has 'get_Model', is => 'ro', isa => Maybe[CodeRef], default => sub {undef};
234              
235             has 'Model', is => 'ro', lazy => 1, default => sub {
236             my $self = shift;
237             die "Must supply 'Model' or 'get_Model'" unless $self->get_Model;
238             $self->get_Model->()
239             }, isa => Object;
240              
241              
242 0     0 0   sub internal_post_path { (shift)->scaffold_cfg->internal_post_path }
243 0     0 0   sub view_wrappers { (shift)->scaffold_cfg->view_wrappers }
244 0     0 0   sub default_view_path { (shift)->scaffold_cfg->default_view_path }
245 0     0 0   sub preview_path { (shift)->scaffold_cfg->preview_path }
246              
247              
248             #has 'internal_post_path', is => 'ro', isa => Str, required => 1;
249             ##has 'view_wrappers', is => 'ro', isa => ArrayRef[HashRef], default => sub {[]};
250             #has 'default_view_path', is => 'ro', isa => Maybe[Str], default => sub {undef};
251             ##has 'preview_path', is => 'ro', isa => Maybe[Str], default => sub {undef};
252              
253              
254             sub get_uid {
255 0     0 0   my $self = shift;
256            
257 0 0         if(my $c = RapidApp->active_request_context) {
258 0 0         return $c->user->id if ($c->can('user'));
259             }
260            
261 0           return 0;
262             }
263              
264             sub cur_ts {
265 0     0 0   my $self = shift;
266 0           my $dt = DateTime->now( time_zone => 'local' );
267 0           join(' ',$dt->ymd('-'),$dt->hms(':'));
268             }
269              
270             sub owns_tpl {
271 0     0 0   my ($self, $template) = @_;
272 0           $self->Dispatcher_for($template)->claimed
273             }
274              
275             sub template_exists {
276 0     0 0   my ($self, $template) = @_;
277 0           $self->Dispatcher_for($template)->exists
278             }
279              
280              
281             sub template_mtime {
282 0     0 0   my ($self, $template) = @_;
283 0   0       $self->_cache_slots->{$template}{template_mtime} //= $self->_template_mtime($template)
284             }
285              
286             sub _template_mtime {
287 0     0     my ($self, $template) = @_;
288            
289 0           $self->Dispatcher_for($template)->mtime
290             }
291              
292             sub template_content {
293 0     0 0   my ($self, $template) = @_;
294 0   0       $self->_cache_slots->{$template}{template_content} //= $self->_template_content($template)
295             }
296              
297             sub _template_content {
298 0     0     my ($self, $template) = @_;
299            
300 0           $self->Dispatcher_for($template)->content
301             }
302              
303              
304             sub create_template {
305 0     0 0   my ($self, $template, $content) = @_;
306            
307 0 0         my $name = $self->Post_name_for($template) or return undef;
308            
309 0           my $create = {
310             name => $name,
311             body => $content,
312             published => 1
313             };
314            
315 0 0         $self->Model->resultset('Post')->create($create) ? 1 : 0;
316             }
317              
318              
319             sub update_template {
320 0     0 0   my ($self, $template, $content) = @_;
321 0 0         my $name = $self->Post_name_for($template) or return undef;
322            
323 0 0         my $Row = $self->Model->resultset('Post')
324             ->search_rs({ 'me.name' => $name })
325             ->first or die 'Not found!';
326            
327 0 0         $Row->update({ body => $content }) ? 1 : 0;
328             }
329              
330              
331             sub delete_template {
332 0     0 0   my ($self, $template) = @_;
333 0 0         my $name = $self->Post_name_for($template) or return undef;
334            
335 0 0         my $Row = $self->Model->resultset('Post')
336             ->search_rs({ 'me.name' => $name })
337             ->first or die 'Not found!';
338            
339 0 0         $Row->delete ? 1 : 0;
340             }
341              
342             around 'get_template_format' => sub {
343             my ($orig, $self, @args) = @_;
344             my $template = join('/',@args);
345            
346             # By rule all local tempaltes (Post rows) are markdown
347             return $self->Post_name_for($template)
348             ? 'markdown'
349             : $self->$orig(@args)
350             };
351              
352             sub list_templates {
353 0     0 0   my $self = shift;
354 0           [ map { join('',$self->internal_post_path,$_) } $self->Model->resultset('Post')->get_column('name')->all ]
  0            
355             }
356              
357             around 'template_post_processor_class' => sub {
358             my ($orig,$self,@args) = @_;
359             my $template = join('/',@args);
360            
361             # By rule, never use a post processor with a wrapper view:
362             return undef if $self->Dispatcher_for($template)->find_parent_type('ViewWrapper');
363            
364             # Render markdown with our MarkdownElement post-processor if the next template
365             # (i.e. which is including us) is one of our wrapper/views. This will defer
366             # rendering of markdown to the client-side with the marked.js library
367             if($self->process_Context && $self->get_template_format($template) eq 'markdown') {
368             if(my $next_template = $self->process_Context->next_template) {
369             if($self->Dispatcher_for($next_template)->is_type('ViewWrapper')) {
370             return 'Rapi::Blog::Template::Postprocessor::MarkdownElement'
371             }
372             }
373             }
374              
375             return $self->$orig(@args)
376             };
377              
378              
379             sub template_psgi_response {
380 0     0 0   my ($self, $template, $c) = @_;
381 0           $self->Dispatcher_for($template)->maybe_psgi_response
382             }
383              
384              
385             1;