File Coverage

blib/lib/RapidApp/Module/ExtComponent.pm
Criterion Covered Total %
statement 58 89 65.1
branch 14 28 50.0
condition 3 9 33.3
subroutine 15 26 57.6
pod 0 19 0.0
total 90 171 52.6


line stmt bran cond sub pod time code
1             package RapidApp::Module::ExtComponent;
2              
3 5     5   2864 use strict;
  5         11  
  5         206  
4 5     5   68 use Moose;
  5         11  
  5         27  
5              
6             extends 'RapidApp::Module';
7              
8 5     5   30312 use RapidApp::Module::Meta::Trait::ExtProp;
  5         11  
  5         163  
9              
10 5     5   31 use RapidApp::Util qw(:all);
  5         9  
  5         8989  
11              
12             # New: ability to programatically set the Ext panel header/footer
13             has 'header_template', is => 'ro', isa => 'Maybe[Str]', default => sub{undef};
14             has 'get_header_html', is => 'ro', isa => 'CodeRef', default => sub {
15             sub {
16             my $o = shift;
17             return $o->header_template ? $o->c->template_render(
18             $o->header_template,
19             { c => $o->c, self => $o }
20             ) : undef;
21             }
22             };
23              
24             has 'footer_template', is => 'ro', isa => 'Maybe[Str]', default => sub{undef};
25             has 'get_footer_html', is => 'ro', isa => 'CodeRef', default => sub {
26             sub {
27             my $o = shift;
28             return $o->footer_template ? $o->c->template_render(
29             $o->footer_template,
30             { c => $o->c, self => $o }
31             ) : undef;
32             }
33             };
34              
35             # See below
36             has '_build_plugins', is => 'ro', isa => 'ArrayRef', default => sub {[]};
37              
38             # New: informational only property to be added to the ext config and checked
39             # by the role_checker function below
40             has 'require_role', is => 'ro', isa => 'Maybe[Str]', default => sub{undef};
41              
42             # Generic function that can be supplied to call to test if the current request/context
43             # matches a given 'role' to globally deny access to this component.
44             # This is for a simple permission API. Note that the 'role' concept is generic to support
45             # any implementation, such as Catalyst Roles, or anything else. Will receive the context
46             # object ($c) as the first argument and the 'require_role' value as the second.
47             # By default, this function is set to a passthrough to a global config param or
48             # just the standard 'check_any_user_role' function
49             has 'role_checker', is => 'ro', isa => 'Maybe[CodeRef]', default => sub {
50             my $self = shift;
51             my $code = try{$self->app->config->{'Model::RapidApp'}{role_checker}} || sub {
52             my $c = shift; #<-- expects CONTEXT object
53             # This default checker function always returns true if the catalyst
54             # context object lacks the check_any_user_role method (which happens
55             # when the Authorization plugin isn't loaded). This doesn't stop
56             # the developer from supplying a custom role_checker instead which
57             # could perform tests based on other criteria, even if there are no
58             # users configured (i.e. it could be based on ip address, header, etc)
59             return $c && $c->can('check_any_user_role') ? $c->check_any_user_role(@_) : 1;
60             }
61             };
62              
63             sub BUILD {
64 214     214 0 2344 my $self = shift;
65            
66             # Add the Ext.ux.RapidApp.Plugin.EventHandlers plugin to all AppCmp
67             # based objects:
68             #$self->add_plugin({ ptype => 'rappeventhandlers' });
69            
70            
71 214 50       448 if(scalar(@{$self->plugins}) > 0) {
  214         5914  
72             # New: Save the plugins set at BUILD time for later to force them to always
73             # be applied to the content.
74 0         0 @{$self->_build_plugins} = @{$self->plugins};
  0         0  
  0         0  
75 0         0 $self->add_ONREQUEST_calls_early('_appcmp_enforce_build_plugins');
76             }
77            
78             $self->apply_extconfig(
79 214 100       6035 require_role => $self->require_role
80             ) if ($self->require_role);
81              
82 214         814 foreach my $attr ($self->meta->get_all_attributes) {
83 21100 100       2578086 $attr->_apply_ext_value($self) if ($attr->does('ExtProp'));
84             }
85              
86             }
87              
88             sub _appcmp_enforce_build_plugins {
89 0     0   0 my $self = shift;
90 0         0 my %curPlg = map {$_=>1} @{$self->plugins};
  0         0  
  0         0  
91 0   0     0 $curPlg{$_} or $self->add_plugin($_) for (@{$self->_build_plugins});
  0         0  
92             }
93              
94              
95             sub test_permission {
96 57     57 0 107 my $self = shift;
97 57         183 my $c = RapidApp->active_request_context;
98             return (
99 57 50 33     1095 $c and
100             $self->role_checker and
101             $self->require_role and
102             # FIXME: only perform the test for valid sessions. This logic already
103             # happens (i.e. data is already protected from invalid sessions) and
104             # will need to be updated to consider this new feature
105             $c->can('session') and $c->session and #<-- had to add this because 'session_is_valid' below
106             # was not returning false (some kind of init issue?)
107             # TODO: investigate further...
108             $c->can('session_is_valid') and $c->session_is_valid and
109             ! $self->role_checker->($c,$self->require_role)
110             ) ? 0 : 1;
111             }
112              
113              
114             sub allowed_content {
115 0     0 0 0 my $self = shift;
116 0 0       0 return $self->test_permission ? $self->content : ();
117             }
118              
119             sub enforce_permission {
120 57     57 0 114 my $self = shift;
121 57 50       209 $self->test_permission and return 1;
122 0         0 die usererr "Permission denied";
123             }
124              
125             sub content {
126 46     46 0 95 my $self = shift;
127              
128 46         173 $self->enforce_permission;
129            
130             # ---
131             # optionally apply extconfig parameters stored in the stash. This was added to support
132             # dynamic dispatch functionality such as a 'RequestMapper' Catalyst controller that might
133             # load saved searches by id or name, and might need to apply extra app/module params
134 46     46   831 my $apply_extconfig = try{$self->c->stash->{apply_extconfig}};
  46         1278  
135 46 50       2185 $self->apply_extconfig( %$apply_extconfig ) if (ref($apply_extconfig) eq 'HASH');
136             # ---
137              
138 46         165 my $cnf = $self->get_complete_extconfig;
139            
140 46         1229 my $header_html = $self->get_header_html->($self);
141             $cnf->{headerCfg} = {
142 46 50       120 tag => 'div',
143             cls => 'panel-borders',
144             html => $header_html
145             } if ($header_html);
146            
147 46         1210 my $footer_html = $self->get_footer_html->($self);
148             $cnf->{footerCfg} = {
149 46 50       143 tag => 'div',
150             cls => 'panel-borders',
151             html => $footer_html
152             } if ($footer_html);
153            
154 46         962 return $cnf;
155             }
156              
157              
158             sub get_complete_extconfig {
159 46     46 0 82 my $self = shift;
160 46         165 $self->apply_all_extconfig_attrs;
161 46         7220 $self->call_rapidapp_handlers($self->all_ONCONTENT_calls);
162 46         1265 return $self->extconfig;
163             }
164              
165             sub apply_all_extconfig_attrs {
166 46     46 0 78 my $self = shift;
167 46         207 foreach my $attr ($self->meta->get_all_attributes) {
168             next unless (
169 3856 100 66     485907 $attr->does('RapidApp::Role::AppCmpConfigParam') and
170             $attr->has_value($self)
171             );
172            
173 138         49496 $self->apply_extconfig( $attr->name => $attr->get_value($self) );
174             }
175             }
176              
177              
178             has 'ONCONTENT_calls' => (
179             traits => [ 'Array' ],
180             is => 'ro',
181             isa => 'ArrayRef[RapidApp::Handler]',
182             default => sub { [] },
183             handles => {
184             all_ONCONTENT_calls => 'elements',
185             add_ONCONTENT_calls => 'push',
186             has_no_ONCONTENT_calls => 'is_empty',
187             }
188             );
189             around 'add_ONCONTENT_calls' => __PACKAGE__->add_ONREQUEST_calls_modifier;
190              
191              
192              
193              
194             has 'extconfig' => (
195             traits => [
196             'Hash',
197             'RapidApp::Role::PerRequestBuildDefReset'
198             ],
199             is => 'ro',
200             isa => 'HashRef',
201             default => sub { {} },
202             handles => {
203             apply_extconfig => 'set',
204             get_extconfig_param => 'get',
205             has_extconfig_param => 'exists',
206             has_no_extconfig => 'is_empty',
207             num_extconfig_params => 'count',
208             delete_extconfig_param => 'delete'
209             },
210             );
211             # 'config' is being renamed to 'extconfig'
212             # These mappings are for backward compatability and will be removed
213             # at some point. Once they are removed, it will force an API change
214             # that can be made at any time, since 'extconfig' is already active
215 0     0 0 0 sub config { (shift)->extconfig(@_) }
216 458     458 0 15470 sub apply_config { (shift)->apply_extconfig(@_) }
217 0     0 0 0 sub get_config_param { (shift)->get_extconfig_param(@_) }
218 0     0 0 0 sub has_config_param { (shift)->has_extconfig_param(@_) }
219 0     0 0 0 sub has_no_config { (shift)->has_no_extconfig(@_) }
220 0     0 0 0 sub num_config_params { (shift)->num_extconfig_params(@_) }
221 0     0 0 0 sub delete_config_param { (shift)->delete_extconfig_param(@_) }
222 0     0 0 0 sub apply_all_config_attrs { (shift)->apply_all_extconfig_attrs(@_) }
223              
224              
225              
226              
227             has 'plugins' => (
228             traits => [
229             'Array',
230             'RapidApp::Role::AppCmpConfigParam',
231             'RapidApp::Role::PerRequestBuildDefReset',
232             ],
233             is => 'ro',
234             isa => 'ArrayRef',
235             default => sub { [] },
236             handles => {
237             add_plugin => 'push',
238             has_no_plugins => 'is_empty',
239             plugin_list => 'uniq'
240             }
241             );
242              
243              
244             # -- disabled and replaced by "listener_callbacks"
245             # event_handlers do basically the same thing as "listeners" except it is
246             # setup as a list instead of a hash, allowing multiple handlers to be
247             # defined for the same event. Items should be added as ArrayRefs, with
248             # the first element defining a string representing the event name, and the
249             # remaining elements representing 1 or more handlers (RapidApp::JSONFunc
250             # objects). Unlike "listeners" which is a built-in config param associated
251             # with all Ext.Observable objects, "event_handlers" is a custom param that
252             # is processed in the Ext.ux.RapidApp.Plugin.EventHandlers plugin which is
253             # also setup in AppCmp
254             #has 'event_handlers' => (
255             # traits => [
256             # 'Array',
257             # 'RapidApp::Role::AppCmpConfigParam',
258             # 'RapidApp::Role::PerRequestBuildDefReset',
259             # ],
260             # is => 'ro',
261             # isa => 'ArrayRef[ArrayRef]',
262             # default => sub { [] },
263             # handles => {
264             # add_event_handlers => 'push',
265             # has_no_event_handlers => 'is_empty',
266             # }
267             #);
268              
269              
270              
271             has 'listeners' => (
272             traits => [
273             'Hash',
274             'RapidApp::Role::AppCmpConfigParam',
275             'RapidApp::Role::PerRequestBuildDefReset'
276             ],
277             is => 'ro',
278             isa => 'HashRef',
279             default => sub { {} },
280             handles => {
281             apply_listeners => 'set',
282             get_listener => 'get',
283             has_no_listeners => 'is_empty',
284             num_listeners => 'count',
285             delete_listeners => 'delete'
286             },
287             );
288              
289              
290              
291             has 'listener_callbacks' => (
292             traits => [
293             'Hash',
294             'RapidApp::Role::AppCmpConfigParam',
295             'RapidApp::Role::PerRequestBuildDefReset'
296             ],
297             is => 'ro',
298             isa => 'HashRef[ArrayRef[RapidApp::JSONFunc]]',
299             default => sub { {} },
300             handles => {
301             apply_listener_callbacks => 'set',
302             get_listener_callbacks => 'get',
303             has_listener_callbacks => 'exists'
304             },
305             );
306             sub add_listener_callbacks {
307 65     65 0 210 my ($self,$event,@funcs) = @_;
308            
309 65         135 my $list = [];
310 65 50       2544 $list = $self->get_listener_callbacks($event) if ($self->has_listener_callbacks($event));
311            
312 65         179 foreach my $func (@funcs) {
313            
314 65 50       196 if (ref($func)) {
315 65         175 push @$list, $func;
316             }
317             else {
318             # Auto convert strings into RapidApp::JSONFunc objects:
319 0         0 push @$list, RapidApp::JSONFunc->new( raw => 1, func => $func );
320             }
321             }
322            
323 65         2342 return $self->apply_listener_callbacks( $event => $list );
324             }
325              
326              
327              
328             sub add_listener {
329 65     65 0 143 my $self = shift;
330 65         124 my $event = shift;
331            
332 65         313 $self->add_listener_callbacks($event,@_);
333            
334 65         1837 my $handler = RapidApp::JSONFunc->new( raw => 1, func =>
335             'function(arg1) {' .
336            
337             'var cmp = this;' .
338            
339             'if(arg1.listener_callbacks && !cmp.listener_callbacks) {' .
340             'cmp = arg1;' .
341             '}' .
342            
343             'var args = arguments;' .
344             'if(cmp.listener_callbacks) {' .
345             'var list = this.listener_callbacks["' . $event . '"];' .
346             'if(Ext.isArray(list)) {' .
347             'Ext.each(list,function(fn) {' .
348             'fn.apply(this,args);' .
349             '},this);' .
350             '}' .
351             '}' .
352             '}'
353             );
354            
355 65         2317 return $self->apply_listeners( $event => $handler );
356             }
357              
358              
359             sub is_printview {
360 0     0 0   my $self = shift;
361 0 0         my $header = $self->c->req->header('X-RapidApp-View') or return 0;
362 0 0         return 1 if ($header eq 'print');
363 0           return 0;
364             }
365              
366             # Available to derived classes. Can be added to toolbar buttons, etc
367             sub print_view_button {
368 0     0 0   my $self = shift;
369            
370 0           my $params = $self->c->req->params;
371 0           delete $params->{_dc};
372            
373 0           my $cnf = {
374             url => $self->suburl('printview'),
375             params => $params
376             };
377            
378 0           my $json = $self->json->encode($cnf);
379            
380             return {
381 0           xtype => 'button',
382             text => 'Print View',
383             iconCls => 'ra-icon-printer',
384             handler => jsfunc 'Ext.ux.RapidApp.winLoadUrlGET.createCallback(' . $json . ')'
385             };
386             }
387              
388              
389              
390             #### --------------------- ####
391              
392              
393 5     5   40 no Moose;
  5         8  
  5         31  
394             #__PACKAGE__->meta->make_immutable;
395              
396              
397              
398             1;