apostrophe 2.225.0 → 2.227.0
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.
- package/CHANGELOG.md +16 -0
- package/lib/modules/apostrophe-schemas/index.js +49 -24
- package/lib/modules/apostrophe-schemas/public/js/user.js +88 -41
- package/lib/modules/apostrophe-schemas/views/macros.html +2 -0
- package/lib/modules/apostrophe-ui/public/css/components/fields.less +17 -0
- package/lib/modules/apostrophe-ui/views/components/fields.html +1 -0
- package/package.json +2 -2
- package/test/schemas.js +106 -0
- package/test/package.json +0 -73
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.227.0 (2023-03-16)
|
|
4
|
+
|
|
5
|
+
### Adds
|
|
6
|
+
|
|
7
|
+
* Add a read-only overlay to prevent users from interacting with areas set as read-only, as it was done for checkboxes and color field types.
|
|
8
|
+
|
|
9
|
+
## 2.226.0 (2023-03-06)
|
|
10
|
+
|
|
11
|
+
### Adds
|
|
12
|
+
|
|
13
|
+
* Add `readOnlyFields` option. Exactly like `showFields`, a boolean/select/checkboxes field in the document can control whether other fields are read-only or not (as opposed to visible or not).
|
|
14
|
+
|
|
15
|
+
### Security
|
|
16
|
+
|
|
17
|
+
* Upgrades passport to the latest version in order to ensure session regeneration when logging in or out. This adds additional security to logins by mitigating any risks due to XSS attacks. Apostrophe is already robust against XSS attacks. For passport methods that are internally used by Apostrophe everything is still working. For projects that are accessing the passport instance directly through `self.apos.login.passport`, some verifications may be necessary to avoid any compatibility issue. The internally used methods are `authenticate`, `use`, `serializeUser`, `deserializeUser`, `initialize`, `session`.
|
|
18
|
+
|
|
3
19
|
## 2.225.0 (2023-02-17)
|
|
4
20
|
|
|
5
21
|
### Adds
|
|
@@ -485,7 +485,7 @@ module.exports = {
|
|
|
485
485
|
}
|
|
486
486
|
var errors = {};
|
|
487
487
|
return async.eachSeries(schema, function(field, callback) {
|
|
488
|
-
if (field.readOnly) {
|
|
488
|
+
if (field.readOnly || self.isReadOnly(schema, object, field.name)) {
|
|
489
489
|
return setImmediate(callback);
|
|
490
490
|
}
|
|
491
491
|
// Fields that are contextual are edited in the context of a
|
|
@@ -545,41 +545,66 @@ module.exports = {
|
|
|
545
545
|
// based on showFields options of all fields
|
|
546
546
|
|
|
547
547
|
self.isVisible = function(schema, object, name) {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
548
|
+
const transform = value => !value;
|
|
549
|
+
const buildWarningMessage = name => `⚠️ showFields misconfigured, attempts to show/hide ${name} which does not exist`;
|
|
550
|
+
|
|
551
|
+
return self.doesMatchConditionalChoices(schema, object, name, 'showFields', transform, buildWarningMessage);
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
// Determine whether the given field is read-only
|
|
555
|
+
// based on readOnlyFields options of all fields
|
|
556
|
+
|
|
557
|
+
self.isReadOnly = function(schema, object, name) {
|
|
558
|
+
const transform = value => value;
|
|
559
|
+
const buildWarningMessage = name => `⚠️ readOnlyFields misconfigured, attempts to set ${name} which does not exist as read only`;
|
|
560
|
+
|
|
561
|
+
return self.doesMatchConditionalChoices(schema, object, name, 'readOnlyFields', transform, buildWarningMessage);
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
self.doesMatchConditionalChoices = function(schema, object, name, actionName, transform, buildWarningMessage) {
|
|
565
|
+
var memo = {};
|
|
566
|
+
|
|
567
|
+
_.each(schema, field => {
|
|
568
|
+
if (!_.find(field.choices || [], choice => choice[actionName])) {
|
|
553
569
|
return;
|
|
554
570
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
571
|
+
|
|
572
|
+
_.each(field.choices, choice => {
|
|
573
|
+
if (!choice[actionName]) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (field.type === 'checkboxes') {
|
|
578
|
+
if (object[field.name] && transform(object[field.name].includes(choice.value))) {
|
|
579
|
+
_.each(choice[actionName], action);
|
|
563
580
|
}
|
|
581
|
+
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (transform(object[field.name] === choice.value)) {
|
|
586
|
+
_.each(choice[actionName], action);
|
|
564
587
|
}
|
|
565
588
|
});
|
|
566
589
|
});
|
|
567
|
-
return !hidden[name];
|
|
568
590
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
591
|
+
return transform(memo[name]);
|
|
592
|
+
|
|
593
|
+
function action(name) {
|
|
594
|
+
memo[name] = true;
|
|
595
|
+
|
|
596
|
+
var field = _.find(schema, { name });
|
|
573
597
|
if (!field) {
|
|
574
598
|
// Do not crash. The linter at startup also catches this,
|
|
575
599
|
// but is a nonfatal warning for bc, so we should also catch it
|
|
576
|
-
self.apos.utils.warnDev(
|
|
600
|
+
self.apos.utils.warnDev(buildWarningMessage(name));
|
|
601
|
+
|
|
577
602
|
return;
|
|
578
603
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
604
|
+
|
|
605
|
+
_.each(field.choices || [], choice => {
|
|
606
|
+
// Cope with nested showFields/readOnlyFields
|
|
607
|
+
_.each(choice[actionName] || [], action);
|
|
583
608
|
});
|
|
584
609
|
}
|
|
585
610
|
};
|
|
@@ -670,94 +670,138 @@ apos.define('apostrophe-schemas', {
|
|
|
670
670
|
};
|
|
671
671
|
|
|
672
672
|
self.enableShowFields = function(data, name, $field, $el, field) {
|
|
673
|
+
self.enableConditionalFields(data, name, $field, $el, field, 'showFields', function ($fieldset, match) {
|
|
674
|
+
$fieldset.toggleClass('apos-hidden', !match);
|
|
675
|
+
});
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
self.enableReadOnlyFields = function(data, name, $field, $el, field) {
|
|
679
|
+
self.enableConditionalFields(data, name, $field, $el, field, 'readOnlyFields', function ($fieldset, match) {
|
|
680
|
+
// Disable inputs, and color picker via the spectrum API
|
|
681
|
+
$fieldset
|
|
682
|
+
.find('input')
|
|
683
|
+
.attr('disabled', match)
|
|
684
|
+
.filter('[data-apos-color]')
|
|
685
|
+
.spectrum(match ? 'disable' : 'enable');
|
|
686
|
+
|
|
687
|
+
// Disable select element
|
|
688
|
+
$fieldset
|
|
689
|
+
.find('select')
|
|
690
|
+
.attr('disabled', match);
|
|
691
|
+
|
|
692
|
+
// Add a visual overlay to show that the field is read-only
|
|
693
|
+
$fieldset
|
|
694
|
+
.find('.apos-field-readonly-overlay')
|
|
695
|
+
.toggleClass('apos-field-readonly-overlay--active', match);
|
|
696
|
+
|
|
697
|
+
// Hide area's controls
|
|
698
|
+
if ($fieldset.hasClass('apos-field-area')) {
|
|
699
|
+
waitForEl($fieldset, '[data-apos-widget-controls]', 10, function($el) {
|
|
700
|
+
$el.toggleClass('apos-area-widget-controls--disabled', match);
|
|
701
|
+
});
|
|
702
|
+
waitForEl($fieldset, '.apos-ui', 10, function($el) {
|
|
703
|
+
$el.css('display', match ? 'none' : 'block');
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function waitForEl($scope, selector, maxRetries, callback) {
|
|
708
|
+
if (this.retries > maxRetries) {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
var $el = $scope.find(selector);
|
|
713
|
+
if ($el.length) {
|
|
714
|
+
callback($el);
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
setTimeout(function() {
|
|
719
|
+
waitForEl($scope, selector, maxRetries, callback);
|
|
720
|
+
}, 100);
|
|
721
|
+
|
|
722
|
+
this.retries = this.retries || {};
|
|
723
|
+
this.retries[selector] = this.retries[selector] ? ++this.retries[selector] : 1;
|
|
724
|
+
};
|
|
725
|
+
});
|
|
726
|
+
};
|
|
673
727
|
|
|
728
|
+
self.enableConditionalFields = function(data, name, $field, $el, field, actionName, callback) {
|
|
729
|
+
var eventName = 'apos' + apos.utils.capitalizeFirst(actionName);
|
|
674
730
|
var $fieldset = self.findFieldset($el, name);
|
|
675
731
|
|
|
676
|
-
// afterChange shows and hides other fieldsets based on
|
|
677
|
-
// the current value of this field
|
|
732
|
+
// afterChange shows and hides or disables other fieldsets based on
|
|
733
|
+
// the current value of this field..
|
|
678
734
|
// We do this in three situations: at startup, when the
|
|
679
|
-
// user changes the value, and when the visibility of this
|
|
735
|
+
// user changes the value, and when the visibility/disability of this
|
|
680
736
|
// field has been affected by another field with the
|
|
681
|
-
// showFields option.
|
|
682
|
-
// work properly. -Tom
|
|
737
|
+
// showFields/readOnlyFields option.
|
|
738
|
+
// This allows nested showFields/readOnlyFields to work properly. -Tom
|
|
683
739
|
|
|
684
740
|
afterChange();
|
|
685
741
|
|
|
686
742
|
$field.on('change', afterChange);
|
|
687
|
-
$fieldset.on(
|
|
688
|
-
function afterChange() {
|
|
689
|
-
// Implement showFields
|
|
743
|
+
$fieldset.on(eventName, afterChange);
|
|
690
744
|
|
|
745
|
+
function afterChange() {
|
|
691
746
|
if (!_.find(field.choices || [], function(choice) {
|
|
692
|
-
return choice
|
|
747
|
+
return choice[actionName];
|
|
693
748
|
})) {
|
|
694
|
-
// showFields is not in use for this select
|
|
695
749
|
return;
|
|
696
750
|
}
|
|
697
751
|
|
|
698
|
-
var val
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
} else {
|
|
702
|
-
val = $field.val();
|
|
703
|
-
}
|
|
752
|
+
var val = field.checkbox
|
|
753
|
+
? $field.is(':checked')
|
|
754
|
+
: $field.val();
|
|
704
755
|
|
|
705
|
-
// Recall if another choice currently active already chose to show each field.
|
|
706
|
-
// That way, if two choices show the same field, the fact that the second one
|
|
707
|
-
// is not currently selected does not hide the field the first one just showed
|
|
708
|
-
var
|
|
756
|
+
// Recall if another choice currently active already chose to show/disable each field.
|
|
757
|
+
// That way, if two choices show/disable the same field, the fact that the second one
|
|
758
|
+
// is not currently selected does not hide/enable the field the first one just showed/disabled
|
|
759
|
+
var memo = {};
|
|
709
760
|
|
|
710
761
|
_.each(field.choices || [], function(choice) {
|
|
762
|
+
var match;
|
|
711
763
|
|
|
712
|
-
// Show the fields for this value if it is the current value
|
|
764
|
+
// Show/disable the fields for this value if it is the current value
|
|
713
765
|
// *and* the select field itself is currently visible
|
|
714
|
-
|
|
715
|
-
var show;
|
|
716
|
-
|
|
717
766
|
if ($fieldset.hasClass('apos-hidden')) {
|
|
718
|
-
|
|
767
|
+
match = false;
|
|
719
768
|
} else if (field.type === 'boolean') {
|
|
720
769
|
// Comparing boolean values is hard because
|
|
721
770
|
// the string '0' must be considered falsy in
|
|
722
771
|
// order to permit use of select elements. -Tom
|
|
723
772
|
if (val === choice.value) {
|
|
724
|
-
|
|
773
|
+
match = true;
|
|
725
774
|
} else if (!choice.value) {
|
|
726
775
|
if ((!val) || (val === '0')) {
|
|
727
|
-
|
|
776
|
+
match = true;
|
|
728
777
|
}
|
|
729
778
|
} else {
|
|
730
779
|
if (val && (val !== '0')) {
|
|
731
|
-
|
|
780
|
+
match = true;
|
|
732
781
|
}
|
|
733
782
|
}
|
|
734
783
|
} else if (field.type === 'checkboxes') {
|
|
735
784
|
_.each($field || [], function(checkbox) {
|
|
736
|
-
if (checkbox.checked && checkbox.value === choice.value && choice
|
|
737
|
-
|
|
785
|
+
if (checkbox.checked && checkbox.value === choice.value && choice[actionName]) {
|
|
786
|
+
match = true;
|
|
738
787
|
}
|
|
739
788
|
});
|
|
740
789
|
} else {
|
|
741
790
|
// type select
|
|
742
791
|
if (val === choice.value.toString()) {
|
|
743
|
-
|
|
792
|
+
match = true;
|
|
744
793
|
}
|
|
745
794
|
}
|
|
746
795
|
|
|
747
|
-
_.each(choice
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
} else {
|
|
751
|
-
shown[fieldName] = false;
|
|
752
|
-
}
|
|
796
|
+
_.each(choice[actionName] || [], function(fieldName) {
|
|
797
|
+
memo[fieldName] = match || !!memo[fieldName];
|
|
798
|
+
|
|
753
799
|
var $fieldset = self.findFieldset($el, fieldName);
|
|
754
800
|
|
|
755
|
-
$fieldset
|
|
756
|
-
$fieldset.trigger(
|
|
801
|
+
callback($fieldset, memo[fieldName]);
|
|
802
|
+
$fieldset.trigger(eventName);
|
|
757
803
|
});
|
|
758
|
-
|
|
759
804
|
});
|
|
760
|
-
|
|
761
805
|
}
|
|
762
806
|
};
|
|
763
807
|
|
|
@@ -1277,6 +1321,7 @@ apos.define('apostrophe-schemas', {
|
|
|
1277
1321
|
$field.val('0');
|
|
1278
1322
|
}
|
|
1279
1323
|
self.enableShowFields(data, name, $field, $el, field);
|
|
1324
|
+
self.enableReadOnlyFields(data, name, $field, $el, field);
|
|
1280
1325
|
return setImmediate(callback);
|
|
1281
1326
|
},
|
|
1282
1327
|
convert: function(data, name, $field, $el, field, callback) {
|
|
@@ -1340,6 +1385,7 @@ apos.define('apostrophe-schemas', {
|
|
|
1340
1385
|
self.findSafe($fieldset, 'input[name="' + name + '"][value="' + data[name][c] + '"]', '.apos-field').prop('checked', true);
|
|
1341
1386
|
}
|
|
1342
1387
|
self.enableShowFields(data, name, $field, $el, field);
|
|
1388
|
+
self.enableReadOnlyFields(data, name, $field, $el, field);
|
|
1343
1389
|
return setImmediate(callback);
|
|
1344
1390
|
}
|
|
1345
1391
|
},
|
|
@@ -1448,6 +1494,7 @@ apos.define('apostrophe-schemas', {
|
|
|
1448
1494
|
}
|
|
1449
1495
|
$field.val(value);
|
|
1450
1496
|
self.enableShowFields(data, name, $field, $el, field);
|
|
1497
|
+
self.enableReadOnlyFields(data, name, $field, $el, field);
|
|
1451
1498
|
return setImmediate(callback);
|
|
1452
1499
|
}
|
|
1453
1500
|
},
|
|
@@ -137,6 +137,7 @@
|
|
|
137
137
|
{%- endmacro -%}
|
|
138
138
|
|
|
139
139
|
{%- macro checkboxesBody(field) -%}
|
|
140
|
+
<div class="apos-field-readonly-overlay"></div>
|
|
140
141
|
{%- for choice in field.choices -%}
|
|
141
142
|
<div class="apos-form-checkbox">
|
|
142
143
|
<label class="apos-form-checkbox-label apos-text-small">
|
|
@@ -204,6 +205,7 @@
|
|
|
204
205
|
{%- endmacro -%}
|
|
205
206
|
|
|
206
207
|
{%- macro singletonBody(field) -%}
|
|
208
|
+
<div class="apos-field-readonly-overlay"></div>
|
|
207
209
|
{# js adds this singleton to the dialog #}
|
|
208
210
|
<div data-{{ field.name }}-edit-view></div>
|
|
209
211
|
{%- endmacro -%}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
.apos-field
|
|
8
8
|
{
|
|
9
|
+
position: relative;
|
|
9
10
|
margin-bottom: @apos-margin-4;
|
|
10
11
|
width: 100%;
|
|
11
12
|
max-width: 540px;
|
|
@@ -300,6 +301,22 @@
|
|
|
300
301
|
display: inline-block;
|
|
301
302
|
}
|
|
302
303
|
|
|
304
|
+
// Field readonly overlay ===================================
|
|
305
|
+
.apos-field-readonly-overlay {
|
|
306
|
+
display: none;
|
|
307
|
+
position: absolute;
|
|
308
|
+
z-index: @apos-z-index-max;
|
|
309
|
+
top: 0;
|
|
310
|
+
left: 0;
|
|
311
|
+
width: 100%;
|
|
312
|
+
height: 100%;
|
|
313
|
+
background-color: rgba(255, 255, 255, 0.75);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.apos-field-readonly-overlay--active {
|
|
317
|
+
display: block;
|
|
318
|
+
}
|
|
319
|
+
|
|
303
320
|
// Browse and autocomplete combo
|
|
304
321
|
.apos-browse-and-autocomplete {
|
|
305
322
|
display: flex;
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
{% macro color(name, placeholder, value, readOnly, options) -%}
|
|
32
32
|
<label>
|
|
33
|
+
<div class="apos-field-readonly-overlay"></div>
|
|
33
34
|
<div class="apos-field-input-color-preview" data-apos-color-preview></div>
|
|
34
35
|
<input id="{{ options.id }}" name="{{ name }}" data-apos-color-empty-label="{{ __ns('apostrophe', "None selected") }}" class="apos-field-input apos-field-input-color{% if options.fieldClasses %} {{ options.fieldClasses }}{% endif %}" data-apos-color type="text" value="{{__ns('apostrophe', value | d(''))}}"{% if readOnly %} disabled{% endif %}{% if options.fieldAttributes %} {{ options.fieldAttributes }}{% endif %}>
|
|
35
36
|
<span class="apos-field-input-colorpicker-value" data-apos-color-value></span>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apostrophe",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.227.0",
|
|
4
4
|
"description": "The Apostrophe Content Management System.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"moog-require": "^1.1.0",
|
|
61
61
|
"nodemailer": "^6.6.2",
|
|
62
62
|
"oembetter": "^1.0.1",
|
|
63
|
-
"passport": "^0.
|
|
63
|
+
"passport": "^0.6.0",
|
|
64
64
|
"passport-local": "^1.0.0",
|
|
65
65
|
"passport-totp": "0.0.2",
|
|
66
66
|
"path-to-regexp": "^1.7.0",
|
package/test/schemas.js
CHANGED
|
@@ -2129,4 +2129,110 @@ describe('Schemas', function() {
|
|
|
2129
2129
|
});
|
|
2130
2130
|
});
|
|
2131
2131
|
|
|
2132
|
+
it('should disregard read-only fields set by a boolean field', function(done) {
|
|
2133
|
+
var req = apos.tasks.getReq();
|
|
2134
|
+
var schema = apos.schemas.compose({
|
|
2135
|
+
addFields: [
|
|
2136
|
+
{
|
|
2137
|
+
name: 'age',
|
|
2138
|
+
type: 'integer'
|
|
2139
|
+
},
|
|
2140
|
+
{
|
|
2141
|
+
name: 'canEditAge',
|
|
2142
|
+
type: 'boolean',
|
|
2143
|
+
choices: [
|
|
2144
|
+
{
|
|
2145
|
+
value: true
|
|
2146
|
+
},
|
|
2147
|
+
{
|
|
2148
|
+
value: false,
|
|
2149
|
+
readOnlyFields: [ 'age' ]
|
|
2150
|
+
}
|
|
2151
|
+
]
|
|
2152
|
+
}
|
|
2153
|
+
]
|
|
2154
|
+
});
|
|
2155
|
+
var object = {
|
|
2156
|
+
age: 20,
|
|
2157
|
+
canEditAge: false
|
|
2158
|
+
};
|
|
2159
|
+
apos.schemas.convert(req, schema, 'form', { age: '30' }, object, function(err) {
|
|
2160
|
+
assert(!err);
|
|
2161
|
+
assert(object.age === 20);
|
|
2162
|
+
done();
|
|
2163
|
+
});
|
|
2164
|
+
});
|
|
2165
|
+
|
|
2166
|
+
it('should disregard read-only fields set by a select field', function(done) {
|
|
2167
|
+
var req = apos.tasks.getReq();
|
|
2168
|
+
var schema = apos.schemas.compose({
|
|
2169
|
+
addFields: [
|
|
2170
|
+
{
|
|
2171
|
+
name: 'age',
|
|
2172
|
+
type: 'integer'
|
|
2173
|
+
},
|
|
2174
|
+
{
|
|
2175
|
+
name: 'edit',
|
|
2176
|
+
type: 'select',
|
|
2177
|
+
choices: [
|
|
2178
|
+
{
|
|
2179
|
+
label: 'Can edit anything',
|
|
2180
|
+
value: 'canEditAnything'
|
|
2181
|
+
},
|
|
2182
|
+
{
|
|
2183
|
+
label: 'Cannot edit age',
|
|
2184
|
+
value: 'cannotEditAge',
|
|
2185
|
+
readOnlyFields: [ 'age' ]
|
|
2186
|
+
}
|
|
2187
|
+
]
|
|
2188
|
+
}
|
|
2189
|
+
]
|
|
2190
|
+
});
|
|
2191
|
+
var object = {
|
|
2192
|
+
age: 20,
|
|
2193
|
+
edit: 'cannotEditAge'
|
|
2194
|
+
};
|
|
2195
|
+
apos.schemas.convert(req, schema, 'form', { age: '30' }, object, function(err) {
|
|
2196
|
+
assert(!err);
|
|
2197
|
+
assert(object.age === 20);
|
|
2198
|
+
done();
|
|
2199
|
+
});
|
|
2200
|
+
});
|
|
2201
|
+
|
|
2202
|
+
it('should disregard read-only fields set by a checkboxes field', function(done) {
|
|
2203
|
+
var req = apos.tasks.getReq();
|
|
2204
|
+
var schema = apos.schemas.compose({
|
|
2205
|
+
addFields: [
|
|
2206
|
+
{
|
|
2207
|
+
name: 'age',
|
|
2208
|
+
type: 'integer'
|
|
2209
|
+
},
|
|
2210
|
+
{
|
|
2211
|
+
name: 'edit',
|
|
2212
|
+
type: 'checkboxes',
|
|
2213
|
+
choices: [
|
|
2214
|
+
{
|
|
2215
|
+
label: 'Can edit anything',
|
|
2216
|
+
value: 'canEditAnything'
|
|
2217
|
+
},
|
|
2218
|
+
{
|
|
2219
|
+
label: 'Cannot edit age',
|
|
2220
|
+
value: 'cannotEditAge',
|
|
2221
|
+
readOnlyFields: [ 'age' ]
|
|
2222
|
+
}
|
|
2223
|
+
]
|
|
2224
|
+
}
|
|
2225
|
+
]
|
|
2226
|
+
});
|
|
2227
|
+
var object = {
|
|
2228
|
+
age: 20,
|
|
2229
|
+
edit: 'cannotEditAge'
|
|
2230
|
+
};
|
|
2231
|
+
apos.schemas.convert(req, schema, 'form', { age: '30' }, object, function(err) {
|
|
2232
|
+
assert(!err);
|
|
2233
|
+
assert(object.age === 20);
|
|
2234
|
+
done();
|
|
2235
|
+
});
|
|
2236
|
+
});
|
|
2237
|
+
|
|
2132
2238
|
});
|
package/test/package.json
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"//": "Automatically generated to satisfy moog-require, do not edit",
|
|
3
|
-
"dependencies": {
|
|
4
|
-
"@apostrophecms/nunjucks": "^2.5.4",
|
|
5
|
-
"@sailshq/lodash": "^3.10.4",
|
|
6
|
-
"async": "^1.5.2",
|
|
7
|
-
"bless": "^3.0.3",
|
|
8
|
-
"bluebird": "^3.7.1",
|
|
9
|
-
"body-parser": "^1.19.0",
|
|
10
|
-
"cheerio": "^1.0.0-rc.10",
|
|
11
|
-
"chokidar": "^3.5.1",
|
|
12
|
-
"connect-flash": "^0.1.1",
|
|
13
|
-
"connect-multiparty": "^2.2.0",
|
|
14
|
-
"cookie-parser": "^1.4.5",
|
|
15
|
-
"credentials": "^3.0.2",
|
|
16
|
-
"cuid": "^1.3.8",
|
|
17
|
-
"diff": "^4.0.1",
|
|
18
|
-
"emulate-mongo-2-driver": "^1.2.3",
|
|
19
|
-
"express": "^4.17.1",
|
|
20
|
-
"express-session": "^1.17.0",
|
|
21
|
-
"glob": "^5.0.15",
|
|
22
|
-
"he": "^0.5.0",
|
|
23
|
-
"heic-to-jpeg-middleware": "^2.0.0",
|
|
24
|
-
"html-to-plaintext": "^0.1.1",
|
|
25
|
-
"html-to-text": "^5.1.1",
|
|
26
|
-
"i18n": "^0.8.6",
|
|
27
|
-
"is-wsl": "^2.2.0",
|
|
28
|
-
"joinr": "^1.0.2",
|
|
29
|
-
"jpeg-exif": "^1.1.4",
|
|
30
|
-
"launder": "^1.5.0",
|
|
31
|
-
"less": "^3.13.1",
|
|
32
|
-
"less-middleware": "^3.1.0",
|
|
33
|
-
"minimatch": "^3.0.4",
|
|
34
|
-
"mkdirp": "^1.0.3",
|
|
35
|
-
"moment": "^2.29.1",
|
|
36
|
-
"moog-require": "^1.1.0",
|
|
37
|
-
"nodemailer": "^6.6.2",
|
|
38
|
-
"oembetter": "^1.0.1",
|
|
39
|
-
"passport": "^0.3.2",
|
|
40
|
-
"passport-local": "^1.0.0",
|
|
41
|
-
"passport-totp": "0.0.2",
|
|
42
|
-
"path-to-regexp": "^1.7.0",
|
|
43
|
-
"performance-now": "^2.1.0",
|
|
44
|
-
"qs": "^6.9.6",
|
|
45
|
-
"regexp-quote": "0.0.0",
|
|
46
|
-
"request": "^2.88.2",
|
|
47
|
-
"request-promise": "^4.2.4",
|
|
48
|
-
"resolve": "^1.20.0",
|
|
49
|
-
"rimraf": "^2.7.1",
|
|
50
|
-
"sanitize-html": "^2.7.1",
|
|
51
|
-
"server-destroy": "^1.0.1",
|
|
52
|
-
"sluggo": "^0.2.0",
|
|
53
|
-
"syntax-error": "^1.3.0",
|
|
54
|
-
"thirty-two": "^1.0.2",
|
|
55
|
-
"tinycolor2": "^1.4.1",
|
|
56
|
-
"uglify-js": "^2.8.29",
|
|
57
|
-
"underscore.string": "^3.3.5",
|
|
58
|
-
"uploadfs": "^1.18.4",
|
|
59
|
-
"xregexp": "^2.0.0",
|
|
60
|
-
"yargs": "^3.32.0",
|
|
61
|
-
"apostrophe": "^2.0.0"
|
|
62
|
-
},
|
|
63
|
-
"devDependencies": {
|
|
64
|
-
"eslint": "^6.5.1",
|
|
65
|
-
"eslint-config-apostrophe": "^2.0.2",
|
|
66
|
-
"eslint-config-standard": "^11.0.0",
|
|
67
|
-
"eslint-plugin-import": "^2.18.2",
|
|
68
|
-
"eslint-plugin-node": "^6.0.1",
|
|
69
|
-
"eslint-plugin-promise": "^3.8.0",
|
|
70
|
-
"eslint-plugin-standard": "^3.1.0",
|
|
71
|
-
"mocha": "^7.0.0"
|
|
72
|
-
}
|
|
73
|
-
}
|