forked from coldbox-modules/cbwire
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathComponent.cfc
More file actions
1762 lines (1609 loc) · 60.4 KB
/
Component.cfc
File metadata and controls
1762 lines (1609 loc) · 60.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
component output="true" accessors="true" {
property name="_interceptorService" inject="coldbox:interceptorService";
property name="_configService" inject="provider:ConfigService@cbwire";
property name="_CBWIREController" inject="provider:CBWIREController@cbwire";
property name="_checksumService" inject="provider:ChecksumService@cbwire";
property name="_validationService" inject="provider:ValidationService@cbwire";
property name="_renderService" inject="provider:RenderService@cbwire";
property name="_wirebox" inject="provider:wirebox";
property name="_cbSecurity";
property name="_cbSecurityEnabled";
property name="data";
property name="_id";
property name="_compileTimeKey";
property name="_parent";
property name="_initialLoad";
property name="_lazyLoad";
property name="_lazyIsolated";
property name="_initialDataProperties";
property name="_incomingPayload";
property name="_dataPropertyNames";
property name="_validationResult";
property name="_params";
property name="_key";
property name="_event";
property name="_children";
property name="_metaData";
property name="_dispatches";
property name="_cache"; // internal cache for storing data
property name="_xjs";
property name="_returnValues";
property name="_redirect";
property name="_redirectUsingNavigate";
property name="_isolate";
property name="_path";
property name="_renderedContent";
property name="_scripts";
property name="_assets";
property name="_listeners";
/**
* Constructor
*
* @return The initialized component instance.
*/
function init() {
return this;
}
/**
* Initializes the component after dependency injection, setting a unique ID if not already set.
* This method should be called by any extending component's init method if overridden.
* Extending components should invoke super.init() to ensure the base initialization is performed.
*
* @return The initialized component instance.
*/
function onDIComplete() {
if ( isNull( variables._id ) ) {
variables._id = lCase( hash( createUUID() ) );
}
variables._cbSecurityEnabled = false;
variables._params = [:];
variables._compileTimeKey = hash( getCurrentTemplatePath() );
variables._key = "";
variables._cache = [:];
variables._dispatches = [];
variables._children = [:];
variables._initialLoad = true;
variables._lazyLoad = false;
variables._lazyIsolated = true;
variables._xjs = [];
variables._returnValues = [];
variables._redirect = "";
variables._redirectUsingNavigate = false;
variables._isolate = false;
variables._renderedContent = "";
variables._scripts = [:];
variables._assets = [:];
/*
Cache the component's meta data on initialization
for fast access where needed.
*/
variables._metaData = getMetaData( this );
/*
Inject cbSecurity if installed and active
*/
if( application.cbcontroller.getWireBox().getInstance( "coldbox:moduleService" ).isModuleActive( 'cbSecurity' ) ){
variables._cbSecurity = application.cbcontroller.getWireBox().getInstance("cbsecurity@cbsecurity");
variables._cbSecurityEnabled = true;
}
/*
Prep our data properties
*/
_prepareDataProperties();
/*
Prep our computed properties for caching
*/
_prepareComputedProperties();
/*
Prep generated getters and setters for data properties
*/
_prepareGeneratedGettersAndSetters();
/*
Prep isolation
*/
_prepareIsolation();
/*
Prep for lazy loading
*/
_prepareLazyLoading();
/*
Prep listeners
*/
_prepareListeners();
/*
Fire onBoot lifecycle method
if it exists
*/
if ( structKeyExists( this, "onBoot" ) ) {
invoke( this, "onBoot" );
}
return this;
}
/*
==================================================================
Public API
==================================================================
*/
/**
* Returns the CBWIRE Controller
*
* @return CBWIREController
*/
function getCBWIREController(){
return variables._CBWIREController;
}
/**
* renderIt left for backwards compatibility.
*
* @return string
*/
function renderIt() {
return "";
}
/**
* Renders the component's HTML output.
* This method should be overridden by subclasses to implement specific rendering logic.
* If not overridden, this method will simply render the view.
*/
function onRender() {
local.renderIt = renderIt();
if ( local.renderIt.len() ) {
return local.renderIt;
}
return template( _getTemplatePath() );
}
/**
* Pass-through method for ColdBox's view() method.
*
* @view The the view to render, if not passed, then we look in the request context for the current set view.
* @args A struct of arguments to pass into the view for rendering, will be available as 'args' in the view.
* @module The module to render the view from explicitly
* @cache Cached the view output or not, defaults to false
* @cacheTimeout The time in minutes to cache the view
* @cacheLastAccessTimeout The time in minutes the view will be removed from cache if idle or requested
* @cacheSuffix The suffix to add into the cache entry for this view rendering
* @cacheProvider The provider to cache this view in, defaults to 'template'
* @collection A collection to use by this Renderer to render the view as many times as the items in the collection (Array or Query)
* @collectionAs The name of the collection variable in the partial rendering. If not passed, we will use the name of the view by convention
* @collectionStartRow The start row to limit the collection rendering with
* @collectionMaxRows The max rows to iterate over the collection rendering with
* @collectionDelim A string to delimit the collection renderings by
* @prePostExempt If true, pre/post view interceptors will not be fired. By default they do fire
* @name The name of the rendering region to render out, Usually all arguments are coming from the stored region but you override them using this function's arguments.
*
* @return The rendered view
*/
function view(
view = "",
struct args = {},
module = "",
boolean cache = false,
cacheTimeout = "",
cacheLastAccessTimeout = "",
cacheSuffix = "",
cacheProvider = "template",
collection,
collectionAs = "",
numeric collectionStartRow = "1",
numeric collectionMaxRows = 0,
collectionDelim = "",
boolean prePostExempt = false,
name
) {
return variables._CBWIREController.view( argumentCollection=arguments );
}
/**
* Renders a specified template by converting dot notation to path notation and appending .cfm if necessary.
* Then, it returns the HTML content.
*
* @viewPath string | The dot notation path to the template to be rendered, without the .cfm extension.
* @params struct | A struct containing the parameters to be passed to the view template.
*
* @return The rendered HTML content as a string.
*/
function template( viewPath, params = {} ) {
// Normalize the view path
local.normalizedPath = variables._renderService.normalizeViewPath( arguments.viewPath );
// Render the view content and trim the result
return variables._renderService.renderViewContent( this, local.normalizedPath, arguments.params );
}
/**
* Get a instance object from WireBox
*
* @name string | The mapping name or CFC path or DSL to retrieve
* @initArguments struct | The constructor structure of arguments to passthrough when initializing the instance
* @dsl string | The DSL string to use to retrieve an instance
*
* @return The requested instance
*/
function getInstance( name, initArguments = {}, dsl ) {
return variables._wirebox.getInstance( argumentCollection=arguments );
}
/**
* Redirects a user to a specified URL or URI.
*
* @redirectURL string | The URL or URI to redirect the user to.
* @redirectUsingNavigate boolean | Whether to use the navigate method to redirect.
*/
function redirect( redirectURL, redirectUsingNavigate = false ) {
variables._redirect = arguments.redirectURL;
variables._redirectUsingNavigate = arguments.redirectUsingNavigate;
}
/**
* Captures a dispatch to be executed later
* by the browser.
*
* @event string | The event to dispatch.
* @params | The parameters to pass to the listeners.
*
* @return void
*/
function dispatch( event, params = [:] ) {
// Convert params to an array first
local.params = _parseDispatchParams( arguments.params );
// Append the dispatch to our dispatches array
variables._dispatches.append( [ "name": arguments.event, "params": local.params ] );
}
/**
* Dispatches an event to the current component.
*
* @event string | The event to dispatch.
* @params struct | The parameters to pass to the method.
*
* @return void
*/
function dispatchSelf( event, params = [:] ) {
local.params = _parseDispatchParams( arguments.params );
// Append the dispatch to our dispatches array
variables._dispatches.append( [ "name": arguments.event, "params": local.params, "self": true ] );
}
/**
* Dispatches a event to another component
*
* @to string | The component to dispatch to.
* @event string | The method to dispatch.
* @params struct | The parameters to pass to the method.
*
* @return void
*/
function dispatchTo( to, event, params = [:]) {
local.params = _parseDispatchParams( arguments.params );
// Append the dispatch to our dispatches array
variables._dispatches.append( [ "name": arguments.event, "params": local.params, "to": arguments.to ] );
}
/**
* Instantiates a CBWIRE component, mounts it,
* and then calls its internal onRender() method.
*
* This is nearly identical to the wire method defined
* in the CBWIREController component, but it is intended
* to provide the wire() method when including nested components
* and provides tracking of the child.
*
* @name string | The name of the component to load.
* @params struct | The parameters you want mounted initially. Defaults to an empty struct.
* @key string | An optional key parameter. Defaults to an empty string.
* @lazy boolean | Optional parameter to lazy load the component.
* @lazyIsolated boolean | Optional parameter to lazy load the component in an isolated scope. Defaults to true.
*
* @return An instance of the specified component after rendering.
*/
function wire(required string name, struct params = {}, string key = "", lazy, lazyIsolated = true ) {
// Generate a key if one is not provided
if ( !arguments.key.len() ) {
arguments.key = _generateWireKey();
}
/*
If the parent is loaded from a subsequent request,
check if the child has already been rendered.
*/
if ( !variables._initialLoad ) {
local.incomingPayload = variables._incomingPayload;
local.children = local.incomingPayload.snapshot.memo.children;
// Are we trying to render a child that has already been rendered?
if ( isStruct( local.children ) && local.children.keyExists( arguments.key ) ) {
local.componentTag = local.children[ arguments.key ][1];
local.componentId = local.children[ arguments.key ][2];
// Re-track the rendered child
variables._children.append( {
"#arguments.key#": [
local.componentTag,
local.componentId
]
} );
// We've already rendered this child, so return a stub for it
return "<#local.componentTag# wire:id=""#local.componentId#""></#local.componentTag#>";
}
}
// Instaniate this child component as a new component
local.instance = variables._CBWIREController.createInstance(argumentCollection=arguments)
._withPath( arguments.name )
._withParent( this )
._withEvent( variables._event )
._withParams( arguments.params, isNull( arguments.lazy ) ? false : arguments.lazy )
._withKey( arguments.key )
// Determine if component should be lazy loaded
// If lazy parameter is explicitly provided, use that value
// Otherwise, use the component's lazy preference
local.shouldLazyLoad = isNull( arguments.lazy ) ?
local.instance._getLazyLoad() : // Use component's preference if no explicit parameter
arguments.lazy; // Use explicit parameter value
// Check if lazy loading is enabled
if ( local.shouldLazyLoad ) {
// Set lazy rendering on the instance
local.instance._withLazy( true );
local.lazyRendering = local.instance._generateXIntersectLazyLoadSnapshot( params=arguments.params );
// Based on the rendering, determine our outer component tag
local.componentTag = variables._renderService.getComponentTag( local.lazyRendering );
// Track the rendered child
variables._children.append( [
"#arguments.key#": [
local.componentTag,
local.instance._getId()
]
] );
return local.lazyRendering;
} else {
// Set lazy rendering off the instance
local.instance._withLazy( false );
// Render it out normally
local.rendering = local.instance._render();
// Based on the rendering, determine our outer component tag
local.componentTag = variables._renderService.getComponentTag( local.rendering );
// Track the rendered child
variables._children.append( {
"#arguments.key#": [
local.componentTag,
local.instance._getId()
]
} );
return local.instance._render();
}
}
/**
* Provides cbvalidation method to be used in actions and views.
*
* @return ValidationResult
*/
function validate( target, fields, constraints, locale, excludeFields, includeFields, profiles ){
arguments.wire = this;
variables._validationResult = variables._validationService.validate( argumentCollection = arguments );
return variables._validationResult;
}
/**
* Provides cbvalidation method to be used in actions and views,
* throwing an exception if validation fails.
*
*
* @throws ValidationException
*/
function validateOrFail(){
variables._validationService.validateOrFail( this );
}
/**
* Returns true if the validation result has errors.
*
* @return boolean
*/
function hasErrors() {
return variables._validationResult.hasErrors();
}
/**
* Returns true if a specific property has errors.
*
* @return boolean
*/
function hasError( prop ) {
return variables._validationResult.hasErrors( arguments.prop );
}
/**
* Returns array of ValidationError objects containing all of theerrors.
*
* @return array
*/
function getErrors() {
return variables._validationResult.getErrors();
}
/**
* Returns the first error message for a given field.
*
* @return string
*/
function getError( prop ) {
local.allErrors = variables._validationResult.getAllErrors( arguments.prop );
if ( local.allErrors.len() ) {
return local.allErrors.first();
}
return "";
}
/**
* Returns true if property passes validation.
*
* @return boolean
*/
function validates( prop ) {
return !hasErrors( arguments.prop );
}
/**
* Resets a data property to it's initial value.
* Can be used to reset all data properties, a single data property, an array, or comma seperated list of data properties.
*
* @property string|list|array | The property or properties to reset. If null, all properties will be reset.
*
* @return
*/
function reset( property ){
// if no property argument get array of all data keys (in dot notation when appropriate)
if ( isNull( arguments.property ) )
arguments.property = _getDotNotationKeys();
// convert comma separated list to array ( single key string becomes single item array )
arguments.property = !isArray( arguments.property ) ? listToArray( arguments.property, "," ) : arguments.property;
// reset all data properties
arguments.property.each( function( element, index ) {
var initialValue = structGet( "variables._initialDataProperties." & element );
if( isStruct( initialValue ) )
initialValue = "";
_updateDataValue( element, initialValue );
});
}
/**
* Resets all data properties except the ones specified.
*
* @property string|list|array | The property or properties to NOT reset.
*
* @return void
* @throws ResetException
*/
function resetExcept( property ){
if ( isNull( arguments.property ) )
throw( type="ResetException", message="Cannot reset a null property." );
// convert comma separated list to array ( single key string becomes single item array )
arguments.property = !isArray( arguments.property ) ? listToArray( arguments.property, "," ) : arguments.property;
// Get all data property keys
var resetKeys = _getDotNotationKeys();
// remove provided properties from reset keys array
for( var removeKey in arguments.property ) {
resetKeys.delete( removeKey );
}
// Reset all properties except what was removed above
reset( resetKeys );
}
/**
* Returns a reference to the LivewireJS entangle method
* which provides model binding between AlpineJS and CBWIRE.
*
* @prop string | The data property you want to bind client and server side.
*
* @returns string
*/
function entangle( required prop ) {
return "window.Livewire.find( '#variables._id#' ).entangle( '#arguments.prop#' )";
}
/**
* Provide ability to return and execute Javascript
* in the browser.
*
* @expression string | The javascript expression to execute.
* @params array | (Optional) An array of parameters. Currently a placeholder for compatibility
*
* @return void
*/
function js( expression, params=[] ) {
variables._xjs.append( { "expression" : expression, "params" : params } );
}
/**
* Streams content to the client.
*
* @target string | The target to stream to.
* @content string | The content to stream.
* @replace boolean | Whether to replace the content.
*
* @return void
*/
function stream( target, content, replace ) output="true"{
if ( !variables._event.privateValueExists( "_cbwire_stream" ) ) {
cfcontent( reset=true );
variables._event.setPrivateValue( "_cbwire_stream", true );
cfheader( statusCode=200 );
cfheader( name="Cache-Control", value="no-cache, private" );
cfheader( name="Host", value=cgi.http_host );
cfheader( name="Content-Type", value="text/event-stream" );
cfheader( name="Connection", value="close" );
cfheader( name="X-Accel-Buffering", value="no" );
cfheader( name="X-Livewire-Stream", value=1 );
}
local.streamResponse = [
"stream": true,
"body": [
"name": arguments.target,
"content": arguments.content,
"replace": arguments.replace
],
"endStream": true
];
writeOutput( serializeJson( local.streamResponse ) );
cfflush();
}
/**
* Provides a placeholder that is used when lazy loading components.
* This method returns an empty string. Override this method in your
* component to provide a custom placeholder.
*
* @return string
*/
function placeholder() {
return "";
}
/**
* Built in action that does nothing but causes the template
* to re-render on subsequent requests.
*
* @return void
*/
function $refresh() {}
/*
==================================================================
Internal API
==================================================================
*/
/**
* Returns the id of the component.
*
* @return string
*/
function _getId() {
return variables._id;
}
/**
* Returns the lazy load preference of the component.
*
* @return boolean
*/
function _getLazyLoad() {
return variables._lazyLoad;
}
/**
* Passes a reference to the parent of a child component.
*
* @return Component
*/
function _withParent( parent ) {
variables._parent = arguments.parent;
return this;
}
/**
* Passes the path of the component.
*
* @path string | The path of the component.
*
* @return Component
*/
function _withPath( path ) {
variables._path = arguments.path;
return this;
}
/**
* Passes the current event into our component.
*
* @return Component
*/
function _withEvent( event ) {
variables._event = arguments.event;
return this;
}
/**
* Passes in incoming payload to the component
*
* @return Component
*/
function _withIncomingPayload( payload ) {
variables._incomingPayload = arguments.payload;
variables._initialLoad = false;
return this;
}
/**
* Passes params to the component to be used with onMount.
*
* @params struct | The parameters to be passed to the component.
* @lazy boolean | (Optional) A boolean value indicating whether the component should be lazily loaded. Default is false.
*
* @return Component The updated component with the specified parameters.
*/
function _withParams( params, lazy = false ) {
variables._params = arguments.params;
// intercept secureMountFailMessage in params if exists and set as variable
if( arguments.params.keyExists( "secureMountFailMessage" ) ){
variables.secureMountFailMessage = arguments.params.secureMountFailMessage;
arguments.params.delete( "secureMountFailMessage" );
}
if ( arguments.lazy ) return this; // Skip onMount here for lazy loaded components
// Loop over our params and set them as data properties
if ( !structKeyExists( this, "onMount" ) ) {
arguments.params.each( function( key, value ) {
if ( variables.data.keyExists( key ) ) {
variables.data[ key ] = value;
}
} );
} else {
try {
// Fire onMount if it exists
onMount(
event=variables._event,
rc=variables._event.getCollection(),
prc=variables._event.getPrivateCollection(),
params=arguments.params
);
} catch ( any e ) {
throw( type="CBWIREException", message="Failure when calling onMount(). #e.message#" );
}
}
// Announce the onCBWIREMount event to global interceptors
_fireInterceptorEvent( "onCBWIREMount", { "params" : arguments.params, "lazy" : false} );
return this;
}
/**
* Passes a key to the component to be used to identify the component
* on subsequent requests.
*
* @key string | The key to be used to identify the component.
*
* @return Component
*/
function _withKey( key ) {
variables._key = arguments.key;
return this;
}
/**
* Passes a lazy load flag to the component.
*
* @lazy boolean | A boolean value indicating whether the component should be lazily loaded.
*
* @return Component
*/
function _withLazy( lazy ) {
variables._lazyLoad = arguments.lazy;
variables._isolate = true;
return this;
}
/**
* Determines if the onSecure method allows rendering.
* checks if cbSecurity is enabled and if onSecure exists.
*
* @isInitial boolean | Indicates if this is the initial render.
*
* @return boolean
*/
function _onSecureShouldRender() {
// cbSecurity checks
if( variables._cbSecurityEnabled ){
// check wire component annotation
if( _metaData.keyExists( "secured" ) ){
var securedAnnotationAllows = _securedAnnotationEvaluate( _metaData.secured );
if( !securedAnnotationAllows ){
// fireInterceptor event for secure mount fail
_fireInterceptorEvent( "onCBWIRESecureFail", {
"method" : "component",
"cbSecurity" : true,
"annotation" : _metaData.secured
} );
}
return securedAnnotationAllows;
}
}
// onSecure method
if ( structKeyExists( this, "onSecure" ) ) {
try {
// Fire onSecure if it exists
var onSecureResults = onSecure(
event=variables._event,
prc=variables._event.getPrivateCollection(),
isInitial=variables._initialLoad,
params=local.keyExists( "mountParams" ) ? local.mountParams : variables._params
);
} catch ( any e ) {
throw( type="CBWIREException", message="Failure when calling onSecure(). #e.message#" );
}
if( !isNull( onSecureResults ) && isBoolean( onSecureResults ) ){
if( !onSecureResults ){
// fireInterceptor event for secure mount fail
_fireInterceptorEvent( "onCBWIRESecureFail", {
"method" : "onSecure",
"cbSecurity" : false
} );
}
return onSecureResults;
}
}
return true;
}
/**
* Evaluates if a method with a secured annotation can be executed.
* fires onCBWIRESecureFail interceptor event if not allowed.
*
* @methodName string | The name of the method to evaluate.
*
* @return boolean | True if the method can be executed, false otherwise.
*/
function _securedAnnotationAllows( methodName ){
if( variables._cbSecurityEnabled && _metaData.keyExists( "functions" ) ){
// find metadata for method
var functionMetaData = variables._metaData.functions.filter( function( item ){
return item.name == methodName;
} );
if( functionMetaData.len() && functionMetaData[1].keyExists( "secured" ) ){
var annotationAllows = _securedAnnotationEvaluate( functionMetaData[1].secured );
if( !annotationAllows ){
// fireInterceptor event for secure mount fail
_fireInterceptorEvent( "onCBWIRESecureFail", {
"method" : arguments.methodName,
"cbSecurity" : true,
"annotation" : functionMetaData[1].secured
} );
}
return annotationAllows;
}
}
return true;
}
/**
* Evaluates a secured annotation value.
*
* @annotation mixed | The secured annotation value to evaluate.
*
* @return boolean | True if the annotation allows access, false otherwise.
*/
function _securedAnnotationEvaluate( annotation ){
// comvert blank secured annotaiton to secured="true"
arguments.annotation = !len( arguments.annotation ) ? true : arguments.annotation;
// if secured is a boolean then return login status or false
if( isBoolean( arguments.annotation ) ){
return arguments.annotation ? variables._cbSecurity.isLoggedIn() : true;
}
// before checking roles/permissions, ensure user is logged in
if( !variables._cbSecurity.isLoggedIn() ){
return false;
}
// secured is NOT boolean, so check permissions/roles via cbSecurity
return _cbSecurity.has( arguments.annotation );
}
/**
* Retrieves the secure mount failure message.
*
* @return string
*/
function _getSecureMountFailMessage(){
// check if overridden via variables first and return
if( variables.keyExists( "secureMountFailMessage" ) ){
return variables.secureMountFailMessage;
}
// get from module settings
var moduleSettings = variables._CBWIREController.getmoduleSettings();
return moduleSettings.keyExists( "secureMountFailMessage" ) ?
moduleSettings.secureMountFailMessage :
"";
// <!-- BLOCKED -->
}
/**
* Hydrate the component
*
* @componentPayload struct | A struct containing the payload to hydrate the component with.
*
* @return void
*/
function _hydrate( componentPayload ) {
// Set our component's id to the incoming memo id
variables._id = arguments.componentPayload.snapshot.memo.id;
// Append the incoming data to our component's data
// It important that we run through all the incoming snapshot
// data and set it to our component's data before calling
// the onHydrate events.
arguments.componentPayload.snapshot.data.each( function( key, value ) {
variables.data[ arguments.key ] = arguments.value;
} );
// Run onHydrateProperty events
arguments.componentPayload.snapshot.data.filter( function( key, value ) {
return structKeyExists( this, "onHydrate#arguments.key#" );
} ).each( function( key, value ) {
if( _securedAnnotationAllows( "onHydrate#arguments.key#" ) ){
invoke( this, "onHydrate#arguments.key#" );
}else{
// Method is secured and user is not authorized!
// TODO: how to handle, maybe fire interceptor event for onCBWIRESecureMethodFail?
}
} );
// Run onHydrate if it exists
if ( structKeyExists( this, "onHydrate" ) ){
if( _securedAnnotationAllows( "onHydrate" ) ){
invoke( this, "onHydrate", { incomingPayload: arguments.componentPayload.snapshot.data } );
}else{
// Method is secured and user is not authorized!
// TODO: how to handle? NOTE: _securedAnnotationAllows() above fires interceptor if not allowed
}
}
if ( arguments.componentPayload.calls.len() && arguments.componentPayload.calls[1].method == "_finishUpload" ) {
local.files = arguments.componentPayload.calls[ 1 ].params[ 2 ];
local.dataProp = componentPayload.calls[1].params[1];
local.files.each( function( uuid ) {
if ( isArray( variables.data[ dataProp ] ) ) {
variables.data[ componentPayload.calls[1].params[1] ].append( "fileupload:" & uuid );
} else {
variables.data[ componentPayload.calls[1].params[1] ] = "fileupload:" & uuid;
}
} );
}
/*
Provide file uploads to view
*/
variables.data.each( function( key, value ) {
if ( isArray( arguments.value ) && arguments.value.len() && isSimpleValue( arguments.value.first() ) && arguments.value[ 1 ] contains "fileupload:" ) {
// This property is holding an array of file uploads.
value.each( function( uuid, index ) {
local.fileUpload = getInstance( dsl="FileUpload@cbwire" ).load(
wire = this,
dataPropertyName = key,
uuid = uuid.replaceNoCase( "fileupload:", "" )
);
variables.data[ key ][ index ] = local.fileUpload;
} );
} else if ( isSimpleValue( arguments.value ) && arguments.value contains "fileupload:" ) {
// This property is holding a single file upload.
variables.data[ arguments.key ] = getInstance( dsl="FileUpload@cbwire" ).load(
wire = this,
dataPropertyName = key,
uuid = arguments.value.replaceNoCase( "fileupload:", "" )
);
}
} );
}
/**
* Apply updates to the component
*
* @updates struct | A struct containing the updates to apply to the component.
*
* @return void
*/
function _applyUpdates( updates ) {
// skip applying updates if not secure
if( !_onSecureShouldRender() ){
return;
}
if ( !updates.count() ) return;
// Capture old values
local.oldValues = duplicate( data );
// Array to track which array props were updated
local.updatedArrayProps = [];
// Loop over the updates and apply them
arguments.updates.each( function( key, value ) {
// validate if key is locked
_validateLockedProperty( key );
// Check if we should trim if simple value
if ( isSimpleValue( arguments.value ) && variables._configService.trimStringValues() ) {
arguments.value = trim( arguments.value );
}
// Determine if this is an array update
if ( reFindNoCase( "\.[0-9]+", arguments.key ) ) {
local.regexMatch = reFindNoCase( "(.+)\.([0-9]+)", arguments.key, 1, true );
local.propertyName = local.regexMatch.match[ 2 ];
local.arrayIndex = local.regexMatch.match[ 3 ];
local.currentArray = structGet( "variables.data." & local.propertyName );
local.currentArray[ local.arrayIndex + 1 ] = isNumeric( arguments.value ) ? val( arguments.value ) : arguments.value;
_updateDataValue( local.propertyName, local.currentArray );
// Track that we updated an array property
if ( !arrayFindNoCase( updatedArrayProps, local.propertyName ) ) {
updatedArrayProps.append( local.propertyName );
}
} else {
local.oldValue = structGet( "variables.data." & key );
_updateDataValue( key, arguments.value );
var onUpdateFunctionName = "onUpdate" & key.replace( ".", "_", "all" );
if ( structKeyExists( this, onUpdateFunctionName) ) {
if( _securedAnnotationAllows( onUpdateFunctionName ) ){
invoke( this, onUpdateFunctionName, { value: arguments.value, oldValue: local.oldValue });
}else{
// Method is secured and user is not authorized!
// TODO: how to handle? NOTE: _securedAnnotationAllows() above fires interceptor if not allowed
}
}
}
} );
local.updatedArrayProps.each( function( prop ) {
var currentArray = structGet( "variables.data." & prop );
_updateDataValue(
prop,
currentArray.filter( function( value ) {