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   1232 use strict;
  1         2  
  1         29  
3 1     1   5 use warnings;
  1         2  
  1         40  
4              
5 1     1   6 use RapidApp::Util qw(:all);
  1         2  
  1         518  
6 1     1   8 use Rapi::Blog::Util;
  1         1  
  1         20  
7 1     1   4 use List::Util;
  1         2  
  1         87  
8              
9 1     1   8 use Moo;
  1         1  
  1         10  
10             extends 'RapidApp::Template::AccessStore';
11 1     1   2358 use Types::Standard ':all';
  1         2  
  1         21  
12              
13 1     1   39353 use Rapi::Blog::Scaffold;
  1         2  
  1         22  
14 1     1   595 use Rapi::Blog::Template::Dispatcher;
  1         3  
  1         35  
15              
16 1     1   9 use Plack::App::File;
  1         2  
  1         22  
17 1     1   5 use Plack::Builder;
  1         1  
  1         89  
18 1     1   7 use Plack::Middleware::ConditionalGET;
  1         2  
  1         957  
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            
88             my $vars = {
89             %{ $self->$orig(@args) },
90             %{ $self->templateData($template) || {} },
91            
92             BlogCfg => $self->BlogCfg,
93             scaffold => $self->scaffold_cfg,
94             list_posts => sub { $self->Model->resultset('Post') ->list_posts(@_) },
95             list_tags => sub { $self->Model->resultset('Tag') ->list_tags(@_) },
96             list_categories => sub { $self->Model->resultset('Category') ->list_categories(@_) },
97             list_sections => sub { $self->Model->resultset('Section') ->list_sections(@_) },
98             list_users => sub { $self->Model->resultset('User') ->list_users(@_) },
99            
100             # TODO: consider mount_url
101             request_path => sub { $c ? join('','/',$c->req->path) : undef },
102            
103             User => sub { Rapi::Blog::Util->get_User },
104            
105             # Path to the 'Remote' controller
106             remote_action_path => sub { $c ? join('',$c->mount_url,'/remote') : undef },
107            
108             add_post_path => sub {
109             my $ns = $c->module_root_namespace;
110             if(my $mode = shift) {
111             $mode = lc($mode);
112             # Note: 'direct' is not useful un this context since add relies on opening new tab
113             die "add_post_path(): bad argument '$mode' -- must be undef or 'navable'"
114             unless ($mode eq 'navable');
115             return join('',$c->mount_url,'/rapidapp/module/',$mode,'/',$ns,'/main/db/db_post/add')
116             }
117             else {
118             return join('',$c->mount_url,'/',$ns,'/#!/',$ns,'/main/db/db_post/add')
119             }
120             },
121            
122             resolve_section_id => sub {
123             if(my $id = shift) {
124             my $Section = $self->Model->resultset('Section')
125             ->search_rs({ 'me.id' => $id })
126             ->first;
127             return $Section ? $Section->name : $id
128             }
129             },
130            
131             # Expose this here so its available to non-priv templates:
132             mount_url => sub { $c->mount_url },
133            
134             local_info => sub {
135             my $new = shift;
136            
137             my $uri = $c->req->uri or return undef;
138             my $session = $c->session or return undef;
139             my $err = $session->{local_info}{$uri->path};
140            
141             if(defined $new) {
142             if(!$new || lc($new) eq 'clear') {
143             exists $session->{local_info}{$uri->path} and delete $session->{local_info}{$uri->path}
144             }
145             else {
146             $session->{local_info}{$uri->path} = $new
147             }
148             }
149              
150             $err
151             }
152            
153            
154             };
155            
156             #if (my $Scaffold = $self->DispatchRule_for($template)->Scaffold) {
157             # $vars->{scaffold} = $Scaffold->config;
158             #}
159            
160             return $vars
161            
162             };
163              
164             around '_get_admin_template_vars' => sub {
165             my ($orig,$self,@args) = @_;
166              
167             return {
168             %{ $self->$orig(@args) },
169            
170             ensure_logged_out => sub {
171             if (Rapi::Blog::Util->get_User) {
172             RapidApp->active_request_context->logout
173             }
174             ''
175             }
176            
177            
178             };
179              
180             };
181              
182              
183             # -----------------
184             # Store class API:
185              
186              
187 1     1   8 use DateTime;
  1         2  
  1         27  
188 1     1   926 use Date::Parse;
  1         4409  
  1         112  
189 1     1   8 use Path::Class qw/file dir/;
  1         2  
  1         972  
190              
191             has 'get_Model', is => 'ro', isa => Maybe[CodeRef], default => sub {undef};
192              
193             has 'Model', is => 'ro', lazy => 1, default => sub {
194             my $self = shift;
195             die "Must supply 'Model' or 'get_Model'" unless $self->get_Model;
196             $self->get_Model->()
197             }, isa => Object;
198              
199              
200 0     0 0   sub internal_post_path { (shift)->scaffold_cfg->internal_post_path }
201 0     0 0   sub view_wrappers { (shift)->scaffold_cfg->view_wrappers }
202 0     0 0   sub default_view_path { (shift)->scaffold_cfg->default_view_path }
203 0     0 0   sub preview_path { (shift)->scaffold_cfg->preview_path }
204              
205              
206             #has 'internal_post_path', is => 'ro', isa => Str, required => 1;
207             ##has 'view_wrappers', is => 'ro', isa => ArrayRef[HashRef], default => sub {[]};
208             #has 'default_view_path', is => 'ro', isa => Maybe[Str], default => sub {undef};
209             ##has 'preview_path', is => 'ro', isa => Maybe[Str], default => sub {undef};
210              
211              
212             sub get_uid {
213 0     0 0   my $self = shift;
214            
215 0 0         if(my $c = RapidApp->active_request_context) {
216 0 0         return $c->user->id if ($c->can('user'));
217             }
218            
219 0           return 0;
220             }
221              
222             sub cur_ts {
223 0     0 0   my $self = shift;
224 0           my $dt = DateTime->now( time_zone => 'local' );
225 0           join(' ',$dt->ymd('-'),$dt->hms(':'));
226             }
227              
228             sub owns_tpl {
229 0     0 0   my ($self, $template) = @_;
230 0           $self->Dispatcher_for($template)->claimed
231             }
232              
233             sub template_exists {
234 0     0 0   my ($self, $template) = @_;
235 0           $self->Dispatcher_for($template)->exists
236             }
237              
238              
239             sub template_mtime {
240 0     0 0   my ($self, $template) = @_;
241 0   0       $self->_cache_slots->{$template}{template_mtime} //= $self->_template_mtime($template)
242             }
243              
244             sub _template_mtime {
245 0     0     my ($self, $template) = @_;
246            
247 0           $self->Dispatcher_for($template)->mtime
248             }
249              
250             sub template_content {
251 0     0 0   my ($self, $template) = @_;
252 0   0       $self->_cache_slots->{$template}{template_content} //= $self->_template_content($template)
253             }
254              
255             sub _template_content {
256 0     0     my ($self, $template) = @_;
257            
258 0           $self->Dispatcher_for($template)->content
259             }
260              
261              
262             sub create_template {
263 0     0 0   my ($self, $template, $content) = @_;
264            
265 0 0         my $name = $self->Post_name_for($template) or return undef;
266            
267 0           my $create = {
268             name => $name,
269             body => $content,
270             published => 1
271             };
272            
273 0 0         $self->Model->resultset('Post')->create($create) ? 1 : 0;
274             }
275              
276              
277             sub update_template {
278 0     0 0   my ($self, $template, $content) = @_;
279 0 0         my $name = $self->Post_name_for($template) or return undef;
280            
281 0 0         my $Row = $self->Model->resultset('Post')
282             ->search_rs({ 'me.name' => $name })
283             ->first or die 'Not found!';
284            
285 0 0         $Row->update({ body => $content }) ? 1 : 0;
286             }
287              
288              
289             sub delete_template {
290 0     0 0   my ($self, $template) = @_;
291 0 0         my $name = $self->Post_name_for($template) or return undef;
292            
293 0 0         my $Row = $self->Model->resultset('Post')
294             ->search_rs({ 'me.name' => $name })
295             ->first or die 'Not found!';
296            
297 0 0         $Row->delete ? 1 : 0;
298             }
299              
300             around 'get_template_format' => sub {
301             my ($orig, $self, @args) = @_;
302             my $template = join('/',@args);
303            
304             # By rule all local tempaltes (Post rows) are markdown
305             return $self->Post_name_for($template)
306             ? 'markdown'
307             : $self->$orig(@args)
308             };
309              
310             sub list_templates {
311 0     0 0   my $self = shift;
312 0           [ map { join('',$self->internal_post_path,$_) } $self->Model->resultset('Post')->get_column('name')->all ]
  0            
313             }
314              
315             around 'template_post_processor_class' => sub {
316             my ($orig,$self,@args) = @_;
317             my $template = join('/',@args);
318            
319             # By rule, never use a post processor with a wrapper view:
320             return undef if $self->Dispatcher_for($template)->find_parent_type('ViewWrapper');
321            
322             # Render markdown with our MarkdownElement post-processor if the next template
323             # (i.e. which is including us) is one of our wrapper/views. This will defer
324             # rendering of markdown to the client-side with the marked.js library
325             if($self->process_Context && $self->get_template_format($template) eq 'markdown') {
326             if(my $next_template = $self->process_Context->next_template) {
327             if($self->Dispatcher_for($next_template)->is_type('ViewWrapper')) {
328             return 'Rapi::Blog::Template::Postprocessor::MarkdownElement'
329             }
330             }
331             }
332              
333             return $self->$orig(@args)
334             };
335              
336              
337             sub template_psgi_response {
338 0     0 0   my ($self, $template, $c) = @_;
339 0           $self->Dispatcher_for($template)->maybe_psgi_response
340             }
341              
342              
343             1;