alchemymvc 1.2.4 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -317,7 +317,7 @@ Model.prepareProperty('sort', function sort() {
317
317
  *
318
318
  * @author Jelle De Loecker <jelle@develry.be>
319
319
  * @since 1.0.0
320
- * @version 1.1.0
320
+ * @version 1.2.5
321
321
  *
322
322
  * @param {String} value The value in the url
323
323
  * @param {String} name The name of the url parameter
@@ -326,7 +326,7 @@ Model.prepareProperty('sort', function sort() {
326
326
  *
327
327
  * @return {Pledge}
328
328
  */
329
- Model.setStatic(function checkPathValue(value, name, field_name, conduit) {
329
+ Model.setStatic(async function checkPathValue(value, name, field_name, conduit) {
330
330
 
331
331
  var instance,
332
332
  pledge,
@@ -352,9 +352,17 @@ Model.setStatic(function checkPathValue(value, name, field_name, conduit) {
352
352
  // Look for the wanted field
353
353
  crit.where(field_name).equals(value);
354
354
 
355
- pledge = instance.find('first', crit);
355
+ let result = await instance.find('first', crit);
356
356
 
357
- return pledge;
357
+ if (result) {
358
+ let found_value = result[field_name];
359
+
360
+ if (found_value != value && !Object.alike(value, found_value)) {
361
+ conduit.rewriteRequestRouteParam(name, found_value);
362
+ }
363
+ }
364
+
365
+ return result;
358
366
  });
359
367
 
360
368
  /**
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @author Jelle De Loecker <jelle@develry.be>
5
5
  * @since 0.2.0
6
- * @version 1.0.0
6
+ * @version 1.2.5
7
7
  */
8
8
  var Route = Function.inherits('Alchemy.Base', function Route(router, paths, options) {
9
9
 
@@ -41,6 +41,10 @@ var Route = Function.inherits('Alchemy.Base', function Route(router, paths, opti
41
41
  this.breadcrumb = '';
42
42
  this.has_breadcrumb_assignments = false;
43
43
 
44
+ // The optional permissions to check
45
+ this.permission = null;
46
+ this.has_permission_assignments = false;
47
+
44
48
  // If no fnc is given, these will be called
45
49
  this.controller = null;
46
50
  this.action = null;
@@ -84,6 +88,35 @@ Route.setMethod(function setBreadcrumb(breadcrumb) {
84
88
  this.breadcrumb = breadcrumb;
85
89
  });
86
90
 
91
+ /**
92
+ * Set the permissions for this route
93
+ *
94
+ * @author Jelle De Loecker <jelle@elevenways.be>
95
+ * @since 1.2.5
96
+ * @version 1.2.5
97
+ *
98
+ * @param {String|String[]} permission
99
+ */
100
+ Route.setMethod(function setPermission(permission) {
101
+
102
+ if (!permission) {
103
+ return;
104
+ }
105
+
106
+ permission = Array.cast(permission);
107
+
108
+ this.permission = permission;
109
+
110
+ for (let entry of permission) {
111
+ let assignments = entry.assignments();
112
+
113
+ if (assignments.length) {
114
+ this.has_permission_assignments = true;
115
+ break;
116
+ }
117
+ }
118
+ });
119
+
87
120
  /**
88
121
  * Compile paths for this route
89
122
  *
@@ -135,7 +168,7 @@ Route.setMethod(function setPaths(paths) {
135
168
  *
136
169
  * @author Jelle De Loecker <jelle@develry.be>
137
170
  * @since 0.2.0
138
- * @version 1.0.3
171
+ * @version 1.2.5
139
172
  *
140
173
  * @param {String} method
141
174
  * @param {String} path The path (without the prefix)
@@ -145,19 +178,13 @@ Route.setMethod(function setPaths(paths) {
145
178
  */
146
179
  Route.setMethod(function match(conduit, method, path, prefix) {
147
180
 
148
- var definition,
149
- params,
150
- pledge,
151
- result,
152
- temp,
153
- type,
154
- i;
181
+ conduit.markRouteAsTested(this);
155
182
 
156
183
  if (this.methods && this.methods.indexOf(method) == -1) {
157
184
  return false;
158
185
  }
159
186
 
160
- result = false;
187
+ let result = false;
161
188
 
162
189
  if (!prefix) {
163
190
  prefix = '';
@@ -181,17 +208,17 @@ Route.setMethod(function match(conduit, method, path, prefix) {
181
208
  if (this.paths[prefix]) {
182
209
 
183
210
  // Get the path definition, including the regex & keys array
184
- definition = this.paths[prefix];
211
+ let definition = this.paths[prefix];
185
212
 
186
213
  // Test it
187
- temp = definition.test(path, conduit);
214
+ let temp = definition.test(path, conduit);
188
215
 
189
216
  if (!temp) {
190
217
  return false;
191
218
  }
192
219
 
193
220
  if (temp.then) {
194
- pledge = new Blast.Classes.Pledge();
221
+ let pledge = new Blast.Classes.Pledge();
195
222
 
196
223
  temp.then(function gotValue(values) {
197
224
 
@@ -295,12 +322,48 @@ Route.setMethod(function checkPolicy(conduit) {
295
322
  return false;
296
323
  });
297
324
 
325
+ /**
326
+ * Check the permission
327
+ *
328
+ * @author Jelle De Loecker <jelle@elevenways.be>
329
+ * @since 1.2.5
330
+ * @version 1.2.5
331
+ *
332
+ * @param {Conduit} conduit
333
+ */
334
+ Route.setMethod(function checkPermission(conduit) {
335
+
336
+ // Check the rouer section first
337
+ if (!this.router.checkPermission(conduit)) {
338
+ return false;
339
+ }
340
+
341
+ if (!this.permission) {
342
+ return true;
343
+ }
344
+
345
+ let permission;
346
+
347
+ for (permission of this.permission) {
348
+
349
+ if (this.has_permission_assignments) {
350
+ permission = permission.assign(conduit.route_string_parameters).toLowerCase();
351
+ }
352
+
353
+ if (conduit.hasPermission(permission)) {
354
+ return true;
355
+ }
356
+ }
357
+
358
+ return false;
359
+ });
360
+
298
361
  /**
299
362
  * Call the handler for this route
300
363
  *
301
364
  * @author Jelle De Loecker <jelle@develry.be>
302
365
  * @since 0.2.0
303
- * @version 1.1.5
366
+ * @version 1.2.5
304
367
  *
305
368
  * @param {Conduit} conduit
306
369
  * @param {Array} parameters
@@ -314,6 +377,17 @@ Route.setMethod(async function callHandler(conduit, parameters) {
314
377
 
315
378
  if (this.fnc) {
316
379
 
380
+ if (!this.checkPermission(conduit)) {
381
+
382
+ let user = conduit.session('UserData');
383
+
384
+ if (!user) {
385
+ return conduit.notAuthorized();
386
+ } else {
387
+ return conduit.forbidden();
388
+ }
389
+ }
390
+
317
391
  if (this.options.policy && !await this.checkPolicy(conduit)) {
318
392
  return conduit.notAuthorized();
319
393
  }
@@ -403,25 +477,20 @@ Route.setMethod(async function callHandler(conduit, parameters) {
403
477
  *
404
478
  * @author Jelle De Loecker <jelle@develry.be>
405
479
  * @since 0.3.0
406
- * @version 0.5.0
480
+ * @version 1.2.5
407
481
  *
408
482
  * @param {Object} parameters (optional)
409
483
  * @param {Object|Alchemy.Conduit} conduit Conduit or options (optional)
410
484
  */
411
485
  Route.setMethod(function generateUrl(parameters, conduit) {
412
486
 
413
- var locale,
414
- mount,
415
- path,
416
- url,
417
- i;
418
-
419
487
  // Use the "no locale" by default
420
- locale = '';
488
+ let locale = '';
421
489
 
422
490
  // If a conduit is given, get the locale
423
491
  if (conduit) {
424
492
  if (conduit.locales) {
493
+ let i;
425
494
  for (i = 0; i < conduit.locales.length; i++) {
426
495
  if (this.paths[conduit.locales[i]]) {
427
496
  locale = conduit.locales[i];
@@ -435,45 +504,7 @@ Route.setMethod(function generateUrl(parameters, conduit) {
435
504
  }
436
505
  }
437
506
 
438
- // Get the path to use for the url generation
439
- path = this.paths[locale];
440
-
441
- if (!path) {
442
- return null;
443
- }
444
-
445
- if (path && path.source) {
446
- path = path.source;
447
- }
448
-
449
- // Remove type definitions from the path
450
- path = path.replace(/\[.*\]\:/g, ':');
451
-
452
- if (!parameters && conduit) {
453
- if (conduit.param) {
454
- parameters = conduit.param();
455
- } else if (conduit.parameters) {
456
- paramers = conduit.parameters;
457
- }
458
- }
459
-
460
- if (parameters) {
461
- url = path.fillPlaceholders(parameters, true);
462
- } else {
463
- url = path;
464
- }
465
-
466
- mount = this.router.getFullMount();
467
-
468
- if (mount.length > 1) {
469
- url = mount + url;
470
- }
471
-
472
- if (locale) {
473
- url = '/' + locale + url;
474
- }
475
-
476
- return url;
507
+ return Router.getUrl(this.name, parameters, {locale});
477
508
  });
478
509
 
479
510
  /**
@@ -12,7 +12,7 @@ var Url = alchemy.use('url'),
12
12
  *
13
13
  * @author Jelle De Loecker <jelle@develry.be>
14
14
  * @since 0.2.0
15
- * @version 1.1.0
15
+ * @version 1.2.5
16
16
  */
17
17
  var RouterClass = Function.inherits('Alchemy.Base', function Router(name, mount, parent) {
18
18
 
@@ -52,6 +52,9 @@ var RouterClass = Function.inherits('Alchemy.Base', function Router(name, mount,
52
52
  // Routes inside this router
53
53
  this.routes = new Deck();
54
54
 
55
+ // Optional permissions to check for
56
+ this.permissions = null;
57
+
55
58
  this.generateSectionIdentifier();
56
59
  });
57
60
 
@@ -67,6 +70,66 @@ RouterClass.setProperty('default_route_settings', {
67
70
  weight : 10
68
71
  });
69
72
 
73
+ /**
74
+ * Add a required permission
75
+ *
76
+ * @author Jelle De Loecker <jelle@elevenways.be>
77
+ * @since 1.2.5
78
+ * @version 1.2.5
79
+ *
80
+ * @param {String|String[]} permission
81
+ */
82
+ RouterClass.setMethod(function requirePermission(permission) {
83
+
84
+ if (!permission) {
85
+ return;
86
+ }
87
+
88
+ if (!this.permissions) {
89
+ this.permissions = [];
90
+ }
91
+
92
+ this.permissions.include(permission);
93
+ });
94
+
95
+ /**
96
+ * Check the permission
97
+ *
98
+ * @author Jelle De Loecker <jelle@elevenways.be>
99
+ * @since 1.2.5
100
+ * @version 1.2.5
101
+ *
102
+ * @param {Conduit} conduit
103
+ *
104
+ * @return {Boolean}
105
+ */
106
+ RouterClass.setMethod(function checkPermission(conduit) {
107
+
108
+ // Does this section require a permission?
109
+ if (this.permissions?.length) {
110
+ let permission,
111
+ has_permission;
112
+
113
+ for (permission of this.permissions) {
114
+ if (conduit.hasPermission(permission)) {
115
+ has_permission = true;
116
+ break;
117
+ }
118
+ }
119
+
120
+ if (!has_permission) {
121
+ return false;
122
+ }
123
+ }
124
+
125
+ // If this section has a parent, check those permissions too
126
+ if (this.parent) {
127
+ return this.parent.checkPermission(conduit);
128
+ }
129
+
130
+ return true;
131
+ });
132
+
70
133
  /**
71
134
  * Get the complete section identifier
72
135
  *
@@ -424,7 +487,7 @@ RouterClass.setMethod(function getRouteByName(name) {
424
487
  *
425
488
  * @author Jelle De Loecker <jelle@develry.be>
426
489
  * @since 0.2.0
427
- * @version 1.0.0
490
+ * @version 1.2.5
428
491
  *
429
492
  * @param {Conduit} conduit
430
493
  * @param {Router} section
@@ -462,6 +525,16 @@ RouterClass.setMethod(async function getMiddleware(conduit, section, path, prefi
462
525
  for (i = 0; i < routes.length; i++) {
463
526
  route = routes[i];
464
527
 
528
+ // If this route matches the main route of the conduit,
529
+ // don't look for more middleware in this section
530
+ if (conduit.route && conduit.route == route) {
531
+ break;
532
+ }
533
+
534
+ if (conduit.hasRouteBeenTested(route)) {
535
+ continue;
536
+ }
537
+
465
538
  if (route.is_middleware) {
466
539
 
467
540
  temp = route.match(conduit, conduit.method, sectionPath, prefix);
@@ -690,7 +763,7 @@ RouterClass.setMethod(function setOption(name, value) {
690
763
  *
691
764
  * @author Jelle De Loecker <jelle@develry.be>
692
765
  * @since 0.2.0
693
- * @version 1.1.0
766
+ * @version 1.2.5
694
767
  *
695
768
  * @param {Object} args
696
769
  * @param {String} args.name Optional route name
@@ -741,6 +814,10 @@ RouterClass.setMethod(function add(args) {
741
814
  route.methods = args.methods;
742
815
  route.setBreadcrumb(args.breadcrumb);
743
816
 
817
+ if (args.permission) {
818
+ route.setPermission(args.permission);
819
+ }
820
+
744
821
  if (args.breadcrumb) {
745
822
  breadcrumb_options = {
746
823
  route : route
@@ -637,7 +637,7 @@ Schema.setMethod(function getField(name) {
637
637
  *
638
638
  * @author Jelle De Loecker <jelle@develry.be>
639
639
  * @since 0.2.0
640
- * @version 1.1.0
640
+ * @version 1.2.5
641
641
  *
642
642
  * @param {String} name
643
643
  *
@@ -660,9 +660,14 @@ Schema.setMethod(function getFieldChain(name) {
660
660
  // Iterate over all the pieces
661
661
  for (i = 0; i <= max; i++) {
662
662
  current = current.get(pieces[i]);
663
+
664
+ if (!current || typeof current == 'function') {
665
+ break;
666
+ }
667
+
663
668
  result.push(current);
664
669
 
665
- if (!current || i == max) {
670
+ if (i == max) {
666
671
  break;
667
672
  }
668
673
 
@@ -186,6 +186,26 @@ Alchemy.setMethod(function __d(domain, key, parameters) {
186
186
  return hawkejs.scene.generalView.__d(domain, key, parameters);
187
187
  });
188
188
 
189
+ /**
190
+ * Set the permission checker
191
+ *
192
+ * @author Jelle De Loecker <jelle@elevenways.be>
193
+ * @since 1.2.5
194
+ * @version 1.2.5
195
+ *
196
+ * @param {Object} instance
197
+ */
198
+ Alchemy.setMethod(function setPermissionChecker(instance) {
199
+
200
+ if (instance) {
201
+ if (typeof instance.conduitHasPermission != 'function') {
202
+ throw new Error('Permission checker needs to have a `conduitHasPermisison` method');
203
+ }
204
+ }
205
+
206
+ this.permission_checker = instance;
207
+ });
208
+
189
209
  /**
190
210
  * Cast to an object id
191
211
  *
@@ -469,7 +489,7 @@ Alchemy.setMethod(function linkup(type, data, cb) {
469
489
  *
470
490
  * @author Jelle De Loecker <jelle@develry.be>
471
491
  * @since 0.3.0
472
- * @version 1.1.0
492
+ * @version 1.2.5
473
493
  *
474
494
  * @param {Object} variables
475
495
  * @param {Object} render_data
@@ -496,6 +516,13 @@ Alchemy.setMethod(function markLinks(variables, render_data) {
496
516
  // Get the newly set breadcrumb
497
517
  page_breadcrumb = variables.__breadcrumb;
498
518
 
519
+ // Get all the language switcher anchors
520
+ elements = document.querySelectorAll('[data-alchemy-language-switch]');
521
+
522
+ for (i = 0; i < elements.length; i++) {
523
+ hawkejs.scene.general_renderer.helpers.Router.updateLanguageSwitcher(elements[i]);
524
+ }
525
+
499
526
  // Get all the elements with a breadcrumb set
500
527
  elements = document.querySelectorAll('[data-breadcrumb],[data-breadcrumbs]');
501
528
 
@@ -584,25 +611,30 @@ Alchemy.setMethod(function markLinks(variables, render_data) {
584
611
  *
585
612
  * @author Jelle De Loecker <jelle@develry.be>
586
613
  * @since 1.0.0
587
- * @version 1.0.5
614
+ * @version 1.2.5
588
615
  */
589
616
  Alchemy.setMethod(function reloadStylesheets() {
590
617
 
591
- var sheet,
592
- url,
593
- i;
594
-
595
- for (i = 0; i < document.styleSheets.length; i++) {
596
- sheet = document.styleSheets[i];
618
+ for (let i = 0; i < document.styleSheets.length; i++) {
619
+ let sheet = document.styleSheets[i];
597
620
 
598
621
  if (!sheet.href) {
599
622
  continue;
600
623
  }
601
624
 
602
- url = new RURL(sheet.href);
625
+ let url = new RURL(sheet.href);
603
626
  url.param('last_reload', Date.now());
604
627
 
605
- sheet.ownerNode.href = String(url);
628
+ //sheet.ownerNode.href = String(url);
629
+
630
+ let new_element = alchemy.hawkejs.createElement('link');
631
+ new_element.setAttribute('rel', 'stylesheet');
632
+ new_element.setAttribute('href', String(url));
633
+ document.head.append(new_element);
634
+
635
+ new_element.addEventListener('load', e => {
636
+ sheet.ownerNode.remove();
637
+ });
606
638
  }
607
639
  });
608
640
 
@@ -611,22 +643,17 @@ Alchemy.setMethod(function reloadStylesheets() {
611
643
  *
612
644
  * @author Jelle De Loecker <jelle@develry.be>
613
645
  * @since 0.4.1
614
- * @version 1.1.0
646
+ * @version 1.2.5
615
647
  */
616
648
  Alchemy.setMethod(function switchLanguage(prefix) {
617
649
 
618
- var config,
619
- target,
620
- temp,
621
- url,
622
- key,
623
- e;
624
-
625
650
  if (prefix) {
626
651
  if (typeof prefix != 'string') {
652
+ let target,
653
+ temp;
627
654
 
628
655
  if (prefix instanceof Event) {
629
- e = prefix;
656
+ let e = prefix;
630
657
  target = e.srcElement || e.target;
631
658
  e.preventDefault();
632
659
  } else if (typeof prefix.getAttribute == 'function') {
@@ -645,35 +672,13 @@ Alchemy.setMethod(function switchLanguage(prefix) {
645
672
  }
646
673
  }
647
674
 
648
- config = hawkejs.scene.helpers.Router.routeConfig(alchemy.current_route);
675
+ let url = hawkejs.scene.helpers.Router.translateCurrentRoute(prefix);
649
676
 
650
- if (!alchemy.current_route || !config) {
677
+ if (!url) {
651
678
  // Unknown current route, defaulting to /prefix
652
679
  return doNavigate('/' + prefix);
653
680
  }
654
681
 
655
- // Get the url string
656
- url = hawkejs.scene.helpers.Router.routeUrl(alchemy.current_route, alchemy.current_url_params, {locale: prefix});
657
-
658
- // Turn it into a url object
659
- url = RURL.parse(url);
660
-
661
- if (url && url.pathname == '/') {
662
- url.pathname = '/' + prefix + url.pathname;
663
- }
664
-
665
- // Add the get queries
666
- if (alchemy.current_url && alchemy.current_url.search) {
667
- for (key in alchemy.current_url.query) {
668
-
669
- if (key == 'hajax' || key == 'h_diversion' || key == 'htop') {
670
- continue;
671
- }
672
-
673
- url.addQuery(key, alchemy.current_url.query[key]);
674
- }
675
- }
676
-
677
682
  url = String(url);
678
683
 
679
684
  return doNavigate(url);
@@ -15,6 +15,7 @@ var publicDirs = alchemy.shared('public.directories', new Deck()),
15
15
  libpath = alchemy.use('path'),
16
16
  nodent_compiler,
17
17
  regenerator_runtime,
18
+ sass_functions,
18
19
  babel_polyfill,
19
20
  babel_preset,
20
21
  babel_async,
@@ -30,7 +31,61 @@ if (alchemy.settings.css_less !== false) {
30
31
  }
31
32
 
32
33
  if (alchemy.settings.css_sass) {
33
- sass = alchemy.use('sass');
34
+
35
+ // Try to use the embedded DART sass
36
+ sass = alchemy.use('sass-embedded');
37
+
38
+ if (!sass) {
39
+ sass = alchemy.use('sass');
40
+ }
41
+
42
+ sass_functions = {
43
+ 'alchemy_settings($name)': function(name) {
44
+
45
+ if (!(name instanceof sass.types.String)) {
46
+ return sass.types.Null.NULL;
47
+ }
48
+
49
+ name = name.getValue();
50
+
51
+ let value = Object.path(alchemy.settings, name);
52
+
53
+ if (value == null) {
54
+ value = Object.path(alchemy.plugins, name);
55
+ }
56
+
57
+ let type = typeof value,
58
+ result;
59
+
60
+ switch (type) {
61
+ case 'string':
62
+ if (value) {
63
+ result = new sass.types.String(value);
64
+ } else {
65
+ result = sass.types.Null.NULL;
66
+ }
67
+ break;
68
+
69
+ case 'number':
70
+ result = new sass.types.Number(value);
71
+ break;
72
+
73
+ case 'boolean':
74
+ result = new sass.types.Boolean(value);
75
+ break;
76
+
77
+ default:
78
+ if (value == null) {
79
+ result = sass.types.Null.NULL;
80
+ } else {
81
+ result = new sass.types.String(''+value);
82
+ }
83
+ break;
84
+ }
85
+
86
+ return result;
87
+ }
88
+ };
34
89
  }
35
90
 
36
91
  if (alchemy.settings.css_post !== false) {
@@ -945,7 +1000,7 @@ Alchemy.setMethod(function getCompiledLessPath(lessPath, options, callback) {
945
1000
  *
946
1001
  * @author Jelle De Loecker <jelle@develry.be>
947
1002
  * @since 0.2.0
948
- * @version 1.1.0
1003
+ * @version 1.2.5
949
1004
  */
950
1005
  Alchemy.setMethod(function getCompiledSassPath(sassPath, options, callback) {
951
1006
 
@@ -969,12 +1024,22 @@ Alchemy.setMethod(function getCompiledSassPath(sassPath, options, callback) {
969
1024
  return writeToTemp(source);
970
1025
  }
971
1026
 
972
- sass.render({
973
- data : source,
1027
+ const render_options = {
974
1028
  includePaths : ['.'].concat(styleDirs.getSorted()).concat(libpath.dirname(sassPath)),
975
- sourceMapEmbed : alchemy.settings.debug,
976
- sourceMapRoot : '/_sourcemaps/'
977
- }, function gotCss(err, result) {
1029
+ functions : sass_functions,
1030
+ };
1031
+
1032
+ if (alchemy.settings.debug) {
1033
+ render_options.file = sassPath;
1034
+ render_options.sourceMap = 'out.css.map';
1035
+ render_options.sourceMapContents = true;
1036
+ render_options.sourceMapEmbed = true;
1037
+ render_options.sourceMapRoot = '/_sourcemaps/';
1038
+ } else {
1039
+ render_options.data = source;
1040
+ }
1041
+
1042
+ sass.render(render_options, function gotCss(err, result) {
978
1043
 
979
1044
  if (err) {
980
1045
  console.log(''+err, err);