File Coverage

blib/lib/RapidApp/Module/ExtComponent.pm
Criterion Covered Total %
statement 58 89 65.1
branch 16 30 53.3
condition 5 12 41.6
subroutine 15 26 57.6
pod 0 19 0.0
total 94 176 53.4


line stmt bran cond sub pod time code
1             package RapidApp::Module::ExtComponent;
2              
3 5     5   2613 use strict;
  5         11  
  5         209  
4 5     5   25 use Moose;
  5         9  
  5         25  
5              
6             extends 'RapidApp::Module';
7              
8 5     5   30248 use RapidApp::Module::Meta::Trait::ExtProp;
  5         14  
  5         157  
9              
10 5     5   29 use RapidApp::Util qw(:all);
  5         10  
  5         9177  
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 2170 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       419 if(scalar(@{$self->plugins}) > 0) {
  214         5848  
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       5913 require_role => $self->require_role
80             ) if ($self->require_role);
81              
82 214         831 foreach my $attr ($self->meta->get_all_attributes) {
83 21100 100       2580205 $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 160     160 0 232 my $self = shift;
97 160         373 my $c = RapidApp->active_request_context;
98             (
99 160 100 33     3395 $c and
    50 66        
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 : $self->parent_module && $self->parent_module->can('test_permission')
111             ? $self->parent_module->test_permission : 1
112             }
113              
114              
115             sub allowed_content {
116 0     0 0 0 my $self = shift;
117 0 0       0 return $self->test_permission ? $self->content : ();
118             }
119              
120             sub enforce_permission {
121 57     57 0 100 my $self = shift;
122 57 50       181 $self->test_permission and return 1;
123 0         0 die usererr "Permission denied";
124             }
125              
126             sub content {
127 46     46 0 103 my $self = shift;
128              
129 46         172 $self->enforce_permission;
130            
131             # ---
132             # optionally apply extconfig parameters stored in the stash. This was added to support
133             # dynamic dispatch functionality such as a 'RequestMapper' Catalyst controller that might
134             # load saved searches by id or name, and might need to apply extra app/module params
135 46     46   329 my $apply_extconfig = try{$self->c->stash->{apply_extconfig}};
  46         1221  
136 46 50       2168 $self->apply_extconfig( %$apply_extconfig ) if (ref($apply_extconfig) eq 'HASH');
137             # ---
138              
139 46         173 my $cnf = $self->get_complete_extconfig;
140            
141 46         1179 my $header_html = $self->get_header_html->($self);
142             $cnf->{headerCfg} = {
143 46 50       116 tag => 'div',
144             cls => 'panel-borders',
145             html => $header_html
146             } if ($header_html);
147            
148 46         1187 my $footer_html = $self->get_footer_html->($self);
149             $cnf->{footerCfg} = {
150 46 50       109 tag => 'div',
151             cls => 'panel-borders',
152             html => $footer_html
153             } if ($footer_html);
154            
155 46         882 return $cnf;
156             }
157              
158              
159             sub get_complete_extconfig {
160 46     46 0 80 my $self = shift;
161 46         136 $self->apply_all_extconfig_attrs;
162 46         6884 $self->call_rapidapp_handlers($self->all_ONCONTENT_calls);
163 46         1183 return $self->extconfig;
164             }
165              
166             sub apply_all_extconfig_attrs {
167 46     46 0 85 my $self = shift;
168 46         190 foreach my $attr ($self->meta->get_all_attributes) {
169             next unless (
170 3856 100 66     482363 $attr->does('RapidApp::Role::AppCmpConfigParam') and
171             $attr->has_value($self)
172             );
173            
174 138         49830 $self->apply_extconfig( $attr->name => $attr->get_value($self) );
175             }
176             }
177              
178              
179             has 'ONCONTENT_calls' => (
180             traits => [ 'Array' ],
181             is => 'ro',
182             isa => 'ArrayRef[RapidApp::Handler]',
183             default => sub { [] },
184             handles => {
185             all_ONCONTENT_calls => 'elements',
186             add_ONCONTENT_calls => 'push',
187             has_no_ONCONTENT_calls => 'is_empty',
188             }
189             );
190             around 'add_ONCONTENT_calls' => __PACKAGE__->add_ONREQUEST_calls_modifier;
191              
192              
193              
194              
195             has 'extconfig' => (
196             traits => [
197             'Hash',
198             'RapidApp::Role::PerRequestBuildDefReset'
199             ],
200             is => 'ro',
201             isa => 'HashRef',
202             default => sub { {} },
203             handles => {
204             apply_extconfig => 'set',
205             get_extconfig_param => 'get',
206             has_extconfig_param => 'exists',
207             has_no_extconfig => 'is_empty',
208             num_extconfig_params => 'count',
209             delete_extconfig_param => 'delete'
210             },
211             );
212             # 'config' is being renamed to 'extconfig'
213             # These mappings are for backward compatability and will be removed
214             # at some point. Once they are removed, it will force an API change
215             # that can be made at any time, since 'extconfig' is already active
216 0     0 0 0 sub config { (shift)->extconfig(@_) }
217 458     458 0 15071 sub apply_config { (shift)->apply_extconfig(@_) }
218 0     0 0 0 sub get_config_param { (shift)->get_extconfig_param(@_) }
219 0     0 0 0 sub has_config_param { (shift)->has_extconfig_param(@_) }
220 0     0 0 0 sub has_no_config { (shift)->has_no_extconfig(@_) }
221 0     0 0 0 sub num_config_params { (shift)->num_extconfig_params(@_) }
222 0     0 0 0 sub delete_config_param { (shift)->delete_extconfig_param(@_) }
223 0     0 0 0 sub apply_all_config_attrs { (shift)->apply_all_extconfig_attrs(@_) }
224              
225              
226              
227              
228             has 'plugins' => (
229             traits => [
230             'Array',
231             'RapidApp::Role::AppCmpConfigParam',
232             'RapidApp::Role::PerRequestBuildDefReset',
233             ],
234             is => 'ro',
235             isa => 'ArrayRef',
236             default => sub { [] },
237             handles => {
238             add_plugin => 'push',
239             has_no_plugins => 'is_empty',
240             plugin_list => 'uniq'
241             }
242             );
243              
244              
245             # -- disabled and replaced by "listener_callbacks"
246             # event_handlers do basically the same thing as "listeners" except it is
247             # setup as a list instead of a hash, allowing multiple handlers to be
248             # defined for the same event. Items should be added as ArrayRefs, with
249             # the first element defining a string representing the event name, and the
250             # remaining elements representing 1 or more handlers (RapidApp::JSONFunc
251             # objects). Unlike "listeners" which is a built-in config param associated
252             # with all Ext.Observable objects, "event_handlers" is a custom param that
253             # is processed in the Ext.ux.RapidApp.Plugin.EventHandlers plugin which is
254             # also setup in AppCmp
255             #has 'event_handlers' => (
256             # traits => [
257             # 'Array',
258             # 'RapidApp::Role::AppCmpConfigParam',
259             # 'RapidApp::Role::PerRequestBuildDefReset',
260             # ],
261             # is => 'ro',
262             # isa => 'ArrayRef[ArrayRef]',
263             # default => sub { [] },
264             # handles => {
265             # add_event_handlers => 'push',
266             # has_no_event_handlers => 'is_empty',
267             # }
268             #);
269              
270              
271              
272             has 'listeners' => (
273             traits => [
274             'Hash',
275             'RapidApp::Role::AppCmpConfigParam',
276             'RapidApp::Role::PerRequestBuildDefReset'
277             ],
278             is => 'ro',
279             isa => 'HashRef',
280             default => sub { {} },
281             handles => {
282             apply_listeners => 'set',
283             get_listener => 'get',
284             has_no_listeners => 'is_empty',
285             num_listeners => 'count',
286             delete_listeners => 'delete'
287             },
288             );
289              
290              
291              
292             has 'listener_callbacks' => (
293             traits => [
294             'Hash',
295             'RapidApp::Role::AppCmpConfigParam',
296             'RapidApp::Role::PerRequestBuildDefReset'
297             ],
298             is => 'ro',
299             isa => 'HashRef[ArrayRef[RapidApp::JSONFunc]]',
300             default => sub { {} },
301             handles => {
302             apply_listener_callbacks => 'set',
303             get_listener_callbacks => 'get',
304             has_listener_callbacks => 'exists'
305             },
306             );
307             sub add_listener_callbacks {
308 65     65 0 204 my ($self,$event,@funcs) = @_;
309            
310 65         144 my $list = [];
311 65 50       2445 $list = $self->get_listener_callbacks($event) if ($self->has_listener_callbacks($event));
312            
313 65         189 foreach my $func (@funcs) {
314            
315 65 50       187 if (ref($func)) {
316 65         178 push @$list, $func;
317             }
318             else {
319             # Auto convert strings into RapidApp::JSONFunc objects:
320 0         0 push @$list, RapidApp::JSONFunc->new( raw => 1, func => $func );
321             }
322             }
323            
324 65         2369 return $self->apply_listener_callbacks( $event => $list );
325             }
326              
327              
328              
329             sub add_listener {
330 65     65 0 132 my $self = shift;
331 65         136 my $event = shift;
332            
333 65         313 $self->add_listener_callbacks($event,@_);
334            
335 65         1845 my $handler = RapidApp::JSONFunc->new( raw => 1, func =>
336             'function(arg1) {' .
337            
338             'var cmp = this;' .
339            
340             'if(arg1.listener_callbacks && !cmp.listener_callbacks) {' .
341             'cmp = arg1;' .
342             '}' .
343            
344             'var args = arguments;' .
345             'if(cmp.listener_callbacks) {' .
346             'var list = this.listener_callbacks["' . $event . '"];' .
347             'if(Ext.isArray(list)) {' .
348             'Ext.each(list,function(fn) {' .
349             'fn.apply(this,args);' .
350             '},this);' .
351             '}' .
352             '}' .
353             '}'
354             );
355            
356 65         2308 return $self->apply_listeners( $event => $handler );
357             }
358              
359              
360             sub is_printview {
361 0     0 0   my $self = shift;
362 0 0         my $header = $self->c->req->header('X-RapidApp-View') or return 0;
363 0 0         return 1 if ($header eq 'print');
364 0           return 0;
365             }
366              
367             # Available to derived classes. Can be added to toolbar buttons, etc
368             sub print_view_button {
369 0     0 0   my $self = shift;
370            
371 0           my $params = $self->c->req->params;
372 0           delete $params->{_dc};
373            
374 0           my $cnf = {
375             url => $self->suburl('printview'),
376             params => $params
377             };
378            
379 0           my $json = $self->json->encode($cnf);
380            
381             return {
382 0           xtype => 'button',
383             text => 'Print View',
384             iconCls => 'ra-icon-printer',
385             handler => jsfunc 'Ext.ux.RapidApp.winLoadUrlGET.createCallback(' . $json . ')'
386             };
387             }
388              
389              
390              
391             #### --------------------- ####
392              
393              
394 5     5   38 no Moose;
  5         10  
  5         32  
395             #__PACKAGE__->meta->make_immutable;
396              
397              
398              
399             1;