@plone/volto 18.0.0-alpha.41 → 18.0.0-alpha.42
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 +42 -0
- package/locales/ca/LC_MESSAGES/volto.po +31 -1
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +31 -1
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +31 -1
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +31 -1
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +31 -1
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +31 -1
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +31 -1
- package/locales/fr.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +31 -1
- package/locales/hi.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +31 -1
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +31 -1
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +31 -1
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +31 -1
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +31 -1
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +31 -1
- package/locales/ro.json +1 -1
- package/locales/volto.pot +32 -2
- package/locales/zh_CN/LC_MESSAGES/volto.po +31 -1
- package/locales/zh_CN.json +1 -1
- package/package.json +3 -4
- package/razzle.config.js +2 -2
- package/src/components/index.js +0 -1
- package/src/components/manage/Blocks/Block/BlocksForm.jsx +5 -0
- package/src/components/manage/Blocks/Block/Order/Item.jsx +6 -2
- package/src/components/manage/Blocks/Block/Order/Order.jsx +2 -0
- package/src/components/manage/Blocks/Container/Data.jsx +10 -2
- package/src/components/manage/Blocks/Image/ImageSidebar.jsx +10 -2
- package/src/components/manage/Blocks/Listing/ListingData.jsx +10 -2
- package/src/components/manage/Blocks/Maps/MapsSidebar.jsx +3 -1
- package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +2 -0
- package/src/components/manage/Blocks/Search/SearchBlockView.jsx +18 -2
- package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +1 -1
- package/src/components/manage/Blocks/Teaser/Data.jsx +10 -2
- package/src/components/manage/Blocks/ToC/Edit.jsx +1 -0
- package/src/components/manage/Blocks/Video/Edit.jsx +1 -1
- package/src/components/manage/Blocks/Video/VideoSidebar.jsx +3 -1
- package/src/components/manage/Contents/Contents.jsx +1 -1
- package/src/components/manage/Controlpanels/ContentTypeSchema.jsx +1 -0
- package/src/components/manage/Controlpanels/UndoControlpanel.jsx +3 -3
- package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +28 -12
- package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +12 -4
- package/src/components/manage/Form/Form.jsx +85 -20
- package/src/components/manage/Form/InlineForm.jsx +2 -4
- package/src/components/manage/Form/ModalForm.jsx +1 -1
- package/src/components/manage/History/History.jsx +1 -1
- package/src/components/manage/Pluggable/Pluggable.test.js +1 -1
- package/src/components/manage/Toolbar/Toolbar.jsx +1 -1
- package/src/components/manage/Widgets/ArrayWidget.jsx +2 -2
- package/src/components/manage/Widgets/ImageWidget.jsx +6 -5
- package/src/components/manage/Widgets/RecurrenceWidget/EndField.jsx +7 -1
- package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +80 -31
- package/src/components/theme/Search/Search.jsx +13 -5
- package/src/components/theme/View/View.jsx +0 -7
- package/src/components/theme/View/View.test.jsx +0 -3
- package/src/config/Widgets.jsx +1 -1
- package/src/config/index.js +7 -2
- package/src/config/validation.ts +155 -0
- package/src/helpers/Extensions/withBlockExtensions.jsx +1 -1
- package/src/helpers/FormValidation/FormValidation.jsx +109 -170
- package/src/helpers/FormValidation/FormValidation.test.js +836 -8
- package/src/helpers/FormValidation/validators.ts +203 -0
- package/src/helpers/MessageLabels/MessageLabels.js +28 -0
- package/src/helpers/Url/Url.test.js +4 -4
- package/src/helpers/User/User.js +1 -1
- package/src/hooks/client/useClient.js +1 -1
- package/test-setup-config.jsx +7 -0
- package/theme/themes/default/modules/embed.variables +1 -1
- package/theme/themes/pastanaga/collections/form.overrides +34 -0
- package/theme/themes/pastanaga/extras/sidebar.less +4 -0
- package/theme/themes/pastanaga/extras/toolbar.less +10 -3
- package/tsconfig.declarations.json +3 -2
- package/types/components/index.d.ts +0 -1
- package/types/components/manage/Blocks/Block/Order/Order.d.ts +2 -1
- package/types/config/RichTextEditor/ToHTML.d.ts +1 -1
- package/types/config/Widgets.d.ts +2 -2
- package/types/config/validation.d.ts +3 -0
- package/types/helpers/Extensions/withBlockExtensions.d.ts +1 -1
- package/types/helpers/FormValidation/FormValidation.d.ts +1 -0
- package/types/helpers/FormValidation/validators.d.ts +29 -0
- package/types/helpers/MessageLabels/MessageLabels.d.ts +36 -0
- package/types/helpers/User/User.d.ts +1 -1
- package/src/components/theme/SocialSharing/SocialSharing.jsx +0 -48
- package/src/components/theme/SocialSharing/SocialSharing.test.jsx +0 -14
|
@@ -18,7 +18,8 @@ const messages = defineMessages({
|
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
const VideoSidebar = (props) => {
|
|
21
|
-
const { data, block, onChangeBlock, navRoot, contentType } =
|
|
21
|
+
const { data, block, blocksErrors, onChangeBlock, navRoot, contentType } =
|
|
22
|
+
props;
|
|
22
23
|
const intl = useIntl();
|
|
23
24
|
const schema = VideoBlockSchema({ ...props, intl });
|
|
24
25
|
|
|
@@ -44,6 +45,7 @@ const VideoSidebar = (props) => {
|
|
|
44
45
|
block={block}
|
|
45
46
|
navRoot={navRoot}
|
|
46
47
|
contentType={contentType}
|
|
48
|
+
errors={blocksErrors}
|
|
47
49
|
/>
|
|
48
50
|
)}
|
|
49
51
|
</>
|
|
@@ -156,7 +156,7 @@ const messages = defineMessages({
|
|
|
156
156
|
defaultMessage: 'Item(s) has been updated.',
|
|
157
157
|
},
|
|
158
158
|
messageReorder: {
|
|
159
|
-
id: 'Item
|
|
159
|
+
id: 'Item successfully moved.',
|
|
160
160
|
defaultMessage: 'Item successfully moved.',
|
|
161
161
|
},
|
|
162
162
|
messagePasted: {
|
|
@@ -389,7 +389,7 @@ class UndoControlpanel extends Component {
|
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
/**
|
|
392
|
-
* Handle next and prev buttons
|
|
392
|
+
* Handle next and prev buttons visibility when transactions are sorted
|
|
393
393
|
* @method handleSortedNextPrevButtons
|
|
394
394
|
* @returns {undefined}
|
|
395
395
|
*/
|
|
@@ -412,7 +412,7 @@ class UndoControlpanel extends Component {
|
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
/**
|
|
415
|
-
* Handle next and prev buttons
|
|
415
|
+
* Handle next and prev buttons visibility when transactions are not sorted
|
|
416
416
|
* @method handleNotSortedNextPrevButtons
|
|
417
417
|
* @returns {undefined}
|
|
418
418
|
*/
|
|
@@ -435,7 +435,7 @@ class UndoControlpanel extends Component {
|
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
/**
|
|
438
|
-
* Handle next, prev buttons and table
|
|
438
|
+
* Handle next, prev buttons and table visibility
|
|
439
439
|
* @method handleTableVisiblity
|
|
440
440
|
* @returns {undefined}
|
|
441
441
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import { cloneDeep, uniqBy } from 'lodash';
|
|
1
|
+
import React, { useEffect, useState, useMemo } from 'react';
|
|
2
|
+
import { cloneDeep, uniqBy, debounce } from 'lodash';
|
|
3
3
|
import { useIntl } from 'react-intl';
|
|
4
4
|
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
|
|
5
5
|
import jwtDecode from 'jwt-decode';
|
|
@@ -114,25 +114,41 @@ const ListingTemplate = ({
|
|
|
114
114
|
matrix_options = [];
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
const debouncedListUsers = useMemo(
|
|
118
|
+
() =>
|
|
119
|
+
debounce((query_user, groups_filter, userLimit) => {
|
|
120
|
+
dispatch(
|
|
121
|
+
listUsers({
|
|
122
|
+
search: query_user,
|
|
123
|
+
groups_filter: groups_filter.map((el) => el.value),
|
|
124
|
+
limit: userLimit,
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
127
|
+
}, 300),
|
|
128
|
+
[dispatch],
|
|
129
|
+
);
|
|
130
|
+
|
|
117
131
|
useEffect(() => {
|
|
118
132
|
// Get users.
|
|
119
133
|
if (show_users) {
|
|
120
|
-
|
|
121
|
-
listUsers({
|
|
122
|
-
search: query_user,
|
|
123
|
-
groups_filter: groups_filter.map((el) => el.value),
|
|
124
|
-
limit: userLimit,
|
|
125
|
-
}),
|
|
126
|
-
);
|
|
134
|
+
debouncedListUsers(query_user, groups_filter, userLimit);
|
|
127
135
|
}
|
|
128
|
-
}, [
|
|
136
|
+
}, [debouncedListUsers, query_user, groups_filter, show_users, userLimit]);
|
|
137
|
+
|
|
138
|
+
const debouncedListGroups = useMemo(
|
|
139
|
+
() =>
|
|
140
|
+
debounce((query_group) => {
|
|
141
|
+
dispatch(listGroups(query_group));
|
|
142
|
+
}, 300),
|
|
143
|
+
[dispatch],
|
|
144
|
+
);
|
|
129
145
|
|
|
130
146
|
useEffect(() => {
|
|
131
147
|
// Get matrix groups.
|
|
132
148
|
if (show_matrix_options) {
|
|
133
|
-
|
|
149
|
+
debouncedListGroups(query_group);
|
|
134
150
|
}
|
|
135
|
-
}, [
|
|
151
|
+
}, [debouncedListGroups, query_group, show_matrix_options]);
|
|
136
152
|
|
|
137
153
|
const onSelectOptionHandler = (selectedvalue, checked, singleClick) => {
|
|
138
154
|
singleClick = singleClick ?? false;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
1
|
+
import React, { useEffect, useState, useMemo } from 'react';
|
|
2
2
|
import { useSelector, useDispatch } from 'react-redux';
|
|
3
3
|
import { useIntl } from 'react-intl';
|
|
4
4
|
import { Checkbox, Form, Input } from 'semantic-ui-react';
|
|
5
5
|
|
|
6
|
-
import { isEqual } from 'lodash';
|
|
6
|
+
import { isEqual, debounce } from 'lodash';
|
|
7
7
|
|
|
8
8
|
import { messages } from '@plone/volto/helpers';
|
|
9
9
|
import { listGroups } from '@plone/volto/actions'; // getRegistry
|
|
@@ -38,12 +38,20 @@ const UserGroupMembershipMatrix = ({ many_users, many_groups }) => {
|
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
const debouncedListGroups = useMemo(
|
|
42
|
+
() =>
|
|
43
|
+
debounce((query_group_filter) => {
|
|
44
|
+
dispatch(listGroups('', query_group_filter));
|
|
45
|
+
}, 300),
|
|
46
|
+
[dispatch],
|
|
47
|
+
);
|
|
48
|
+
|
|
41
49
|
useEffect(() => {
|
|
42
50
|
// TODO fetch group for at least query_group_filter.length > 1?
|
|
43
51
|
if (!many_groups || (many_groups && query_group_filter.length > 1)) {
|
|
44
|
-
|
|
52
|
+
debouncedListGroups(query_group_filter);
|
|
45
53
|
}
|
|
46
|
-
}, [
|
|
54
|
+
}, [debouncedListGroups, many_groups, query_group_filter]);
|
|
47
55
|
|
|
48
56
|
const onReset = (event) => {
|
|
49
57
|
// event.preventDefault();
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
FormValidation,
|
|
13
13
|
getBlocksFieldname,
|
|
14
14
|
getBlocksLayoutFieldname,
|
|
15
|
+
hasBlocksData,
|
|
15
16
|
messages,
|
|
16
17
|
} from '@plone/volto/helpers';
|
|
17
18
|
import aheadSVG from '@plone/volto/icons/ahead.svg';
|
|
@@ -349,7 +350,7 @@ class Form extends Component {
|
|
|
349
350
|
|
|
350
351
|
/**
|
|
351
352
|
* If user clicks on input, the form will be not considered pristine
|
|
352
|
-
* this will avoid onBlur effects without
|
|
353
|
+
* this will avoid onBlur effects without interaction with the form
|
|
353
354
|
* @param {Object} e event
|
|
354
355
|
*/
|
|
355
356
|
onClickInput(e) {
|
|
@@ -527,30 +528,92 @@ class Form extends Component {
|
|
|
527
528
|
})
|
|
528
529
|
: {};
|
|
529
530
|
|
|
530
|
-
|
|
531
|
+
let blocksErrors = {};
|
|
532
|
+
|
|
533
|
+
if (hasBlocksData(formData)) {
|
|
534
|
+
// Validate blocks
|
|
535
|
+
const blocks = this.state.formData[getBlocksFieldname(formData)];
|
|
536
|
+
const blocksLayout =
|
|
537
|
+
this.state.formData[getBlocksLayoutFieldname(formData)];
|
|
538
|
+
const defaultSchema = {
|
|
539
|
+
properties: {},
|
|
540
|
+
fieldsets: [],
|
|
541
|
+
required: [],
|
|
542
|
+
};
|
|
543
|
+
blocksLayout.items.forEach((block) => {
|
|
544
|
+
let blockSchema =
|
|
545
|
+
config.blocks.blocksConfig[blocks[block]['@type']].blockSchema ||
|
|
546
|
+
defaultSchema;
|
|
547
|
+
if (typeof blockSchema === 'function') {
|
|
548
|
+
blockSchema = blockSchema({
|
|
549
|
+
intl: this.props.intl,
|
|
550
|
+
formData: blocks[block],
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
const blockErrors = FormValidation.validateFieldsPerFieldset({
|
|
554
|
+
schema: blockSchema,
|
|
555
|
+
formData: blocks[block],
|
|
556
|
+
formatMessage: this.props.intl.formatMessage,
|
|
557
|
+
});
|
|
558
|
+
if (keys(blockErrors).length > 0) {
|
|
559
|
+
blocksErrors = {
|
|
560
|
+
...blocksErrors,
|
|
561
|
+
[block]: { ...blockErrors },
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (keys(errors).length > 0 || keys(blocksErrors).length > 0) {
|
|
531
568
|
const activeIndex = FormValidation.showFirstTabWithErrors({
|
|
532
569
|
errors,
|
|
533
570
|
schema: this.props.schema,
|
|
534
571
|
});
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
() => {
|
|
541
|
-
Object.keys(errors).forEach((err) =>
|
|
542
|
-
toast.error(
|
|
543
|
-
<Toast
|
|
544
|
-
error
|
|
545
|
-
title={this.props.schema.properties[err].title || err}
|
|
546
|
-
content={errors[err].join(', ')}
|
|
547
|
-
/>,
|
|
548
|
-
),
|
|
549
|
-
);
|
|
572
|
+
|
|
573
|
+
this.setState({
|
|
574
|
+
errors: {
|
|
575
|
+
...errors,
|
|
576
|
+
...(!isEmpty(blocksErrors) && { blocks: blocksErrors }),
|
|
550
577
|
},
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
578
|
+
activeIndex,
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
if (keys(errors).length > 0) {
|
|
582
|
+
// Changes the focus to the metadata tab in the sidebar if error
|
|
583
|
+
Object.keys(errors).forEach((err) =>
|
|
584
|
+
toast.error(
|
|
585
|
+
<Toast
|
|
586
|
+
error
|
|
587
|
+
title={this.props.schema.properties[err].title || err}
|
|
588
|
+
content={errors[err].join(', ')}
|
|
589
|
+
/>,
|
|
590
|
+
),
|
|
591
|
+
);
|
|
592
|
+
this.props.setSidebarTab(0);
|
|
593
|
+
} else if (keys(blocksErrors).length > 0) {
|
|
594
|
+
const errorField = Object.entries(
|
|
595
|
+
Object.entries(blocksErrors)[0][1],
|
|
596
|
+
)[0][0];
|
|
597
|
+
const errorMessage = Object.entries(
|
|
598
|
+
Object.entries(blocksErrors)[0][1],
|
|
599
|
+
)[0][1];
|
|
600
|
+
toast.error(
|
|
601
|
+
<Toast
|
|
602
|
+
error
|
|
603
|
+
title={this.props.intl.formatMessage(
|
|
604
|
+
messages.blocksFieldsErrorTitle,
|
|
605
|
+
{ errorField },
|
|
606
|
+
)}
|
|
607
|
+
content={errorMessage}
|
|
608
|
+
/>,
|
|
609
|
+
);
|
|
610
|
+
this.props.setSidebarTab(1);
|
|
611
|
+
this.props.setUIState({
|
|
612
|
+
selected: Object.keys(blocksErrors)[0],
|
|
613
|
+
multiSelected: [],
|
|
614
|
+
hovered: null,
|
|
615
|
+
});
|
|
616
|
+
}
|
|
554
617
|
} else {
|
|
555
618
|
// Get only the values that have been modified (Edit forms), send all in case that
|
|
556
619
|
// it's an add form
|
|
@@ -730,6 +793,8 @@ class Form extends Component {
|
|
|
730
793
|
history={this.props.history}
|
|
731
794
|
location={this.props.location}
|
|
732
795
|
token={this.props.token}
|
|
796
|
+
errors={this.state.errors}
|
|
797
|
+
blocksErrors={this.state.errors.blocks}
|
|
733
798
|
/>
|
|
734
799
|
{this.state.isClient &&
|
|
735
800
|
this.state.sidebarMetadataIsAvailable &&
|
|
@@ -142,7 +142,6 @@ const InlineForm = (props) => {
|
|
|
142
142
|
content={error.message}
|
|
143
143
|
/>
|
|
144
144
|
)}
|
|
145
|
-
|
|
146
145
|
<div id={`blockform-fieldset-${defaultFieldset.id}`}>
|
|
147
146
|
<Segment className="form attached">
|
|
148
147
|
{map(defaultFieldset.fields, (field, index) => (
|
|
@@ -157,7 +156,7 @@ const InlineForm = (props) => {
|
|
|
157
156
|
onChangeField(id, value, itemInfo);
|
|
158
157
|
}}
|
|
159
158
|
key={field}
|
|
160
|
-
error={errors[field]}
|
|
159
|
+
error={errors?.[block]?.[field] || {}}
|
|
161
160
|
block={block}
|
|
162
161
|
/>
|
|
163
162
|
))}
|
|
@@ -166,7 +165,6 @@ const InlineForm = (props) => {
|
|
|
166
165
|
)}
|
|
167
166
|
</Segment>
|
|
168
167
|
</div>
|
|
169
|
-
|
|
170
168
|
{other.map((fieldset, index) => (
|
|
171
169
|
<Accordion fluid styled className="form" key={fieldset.id}>
|
|
172
170
|
<div key={fieldset.id} id={`blockform-fieldset-${fieldset.id}`}>
|
|
@@ -199,7 +197,7 @@ const InlineForm = (props) => {
|
|
|
199
197
|
onChangeField(id, value);
|
|
200
198
|
}}
|
|
201
199
|
key={field}
|
|
202
|
-
error={errors[field]}
|
|
200
|
+
error={errors?.[block]?.[field] || {}}
|
|
203
201
|
block={block}
|
|
204
202
|
/>
|
|
205
203
|
))}
|
|
@@ -137,7 +137,7 @@ class ModalForm extends Component {
|
|
|
137
137
|
|
|
138
138
|
/**
|
|
139
139
|
* If user clicks on input, the form will be not considered pristine
|
|
140
|
-
* this will avoid onBlur effects without
|
|
140
|
+
* this will avoid onBlur effects without interaction with the form
|
|
141
141
|
* @param {Object} e event
|
|
142
142
|
*/
|
|
143
143
|
onClickInput(e) {
|
|
@@ -345,7 +345,7 @@ export default compose(
|
|
|
345
345
|
asyncConnect([
|
|
346
346
|
{
|
|
347
347
|
key: 'actions',
|
|
348
|
-
// Dispatch async/await to make the operation
|
|
348
|
+
// Dispatch async/await to make the operation synchronous, otherwise it returns
|
|
349
349
|
// before the promise is resolved
|
|
350
350
|
promise: async ({ location, store: { dispatch } }) =>
|
|
351
351
|
await dispatch(listActions(getBaseUrl(location.pathname))),
|
|
@@ -623,7 +623,7 @@ class Toolbar extends Component {
|
|
|
623
623
|
aria-label={this.props.intl.formatMessage(
|
|
624
624
|
messages.shrinkToolbar,
|
|
625
625
|
)}
|
|
626
|
-
className={cx({
|
|
626
|
+
className={cx('toolbar-handler-button', {
|
|
627
627
|
[this.props.content?.review_state]:
|
|
628
628
|
this.props.content?.review_state,
|
|
629
629
|
})}
|
|
@@ -129,7 +129,7 @@ const compareOption = (inputValue = '', option, accessors) => {
|
|
|
129
129
|
* @class ArrayWidget
|
|
130
130
|
* @extends Component
|
|
131
131
|
*
|
|
132
|
-
* A
|
|
132
|
+
* A creatable select array widget will be rendered if the named vocabulary is
|
|
133
133
|
* in the widget definition (hint) like:
|
|
134
134
|
*
|
|
135
135
|
* ```
|
|
@@ -280,7 +280,7 @@ class ArrayWidget extends Component {
|
|
|
280
280
|
const { SortableContainer } = this.props.reactSortableHOC;
|
|
281
281
|
const Select = this.props.reactSelect.default;
|
|
282
282
|
const SortableSelect =
|
|
283
|
-
// It will be only
|
|
283
|
+
// It will be only creatable if the named vocabulary is in the widget definition
|
|
284
284
|
// (hint) like:
|
|
285
285
|
// list_field_voc_unconstrained = schema.List(
|
|
286
286
|
// title=u"List field with values from vocabulary but not constrained to them.",
|
|
@@ -78,6 +78,7 @@ const UnconnectedImageInput = (props) => {
|
|
|
78
78
|
placeholderLinkInput = '',
|
|
79
79
|
onSelectItem,
|
|
80
80
|
} = props;
|
|
81
|
+
const imageValue = value?.[0]?.['@id'] || value;
|
|
81
82
|
|
|
82
83
|
const intl = useIntl();
|
|
83
84
|
const linkEditor = useLinkEditor();
|
|
@@ -155,7 +156,7 @@ const UnconnectedImageInput = (props) => {
|
|
|
155
156
|
}, [restrictFileUpload]);
|
|
156
157
|
const onDragLeave = React.useCallback(() => setDragging(false), []);
|
|
157
158
|
|
|
158
|
-
return
|
|
159
|
+
return imageValue ? (
|
|
159
160
|
<div
|
|
160
161
|
className="image-upload-widget-image"
|
|
161
162
|
onClick={onFocus}
|
|
@@ -166,9 +167,9 @@ const UnconnectedImageInput = (props) => {
|
|
|
166
167
|
<img
|
|
167
168
|
className={props.className}
|
|
168
169
|
src={
|
|
169
|
-
isInternalURL(
|
|
170
|
-
? `${flattenToAppURL(
|
|
171
|
-
:
|
|
170
|
+
isInternalURL(imageValue)
|
|
171
|
+
? `${flattenToAppURL(imageValue)}/@@images/image/${imageSize}`
|
|
172
|
+
: imageValue
|
|
172
173
|
}
|
|
173
174
|
alt=""
|
|
174
175
|
/>
|
|
@@ -272,7 +273,7 @@ const UnconnectedImageInput = (props) => {
|
|
|
272
273
|
</div>
|
|
273
274
|
{linkEditor.anchorNode && (
|
|
274
275
|
<linkEditor.LinkEditor
|
|
275
|
-
value={
|
|
276
|
+
value={imageValue}
|
|
276
277
|
placeholder={
|
|
277
278
|
placeholderLinkInput ||
|
|
278
279
|
intl.formatMessage(messages.linkAnImage)
|
|
@@ -81,7 +81,13 @@ const EndField = ({ value, count, until, onChange, intl }) => {
|
|
|
81
81
|
id="until"
|
|
82
82
|
title={intl.formatMessage(messages.recurrenceEndsUntil)}
|
|
83
83
|
dateOnly={true}
|
|
84
|
-
value={
|
|
84
|
+
value={
|
|
85
|
+
until
|
|
86
|
+
? typeof until === 'string'
|
|
87
|
+
? until
|
|
88
|
+
: until?.toISOString()
|
|
89
|
+
: ''
|
|
90
|
+
}
|
|
85
91
|
resettable={false}
|
|
86
92
|
onChange={(id, value) => {
|
|
87
93
|
onChange(id, value === '' ? undefined : value);
|
|
@@ -37,7 +37,6 @@ import {
|
|
|
37
37
|
FREQUENCES,
|
|
38
38
|
WEEKLY_DAYS,
|
|
39
39
|
MONDAYFRIDAY_DAYS,
|
|
40
|
-
toISOString,
|
|
41
40
|
rrulei18n,
|
|
42
41
|
} from './Utils';
|
|
43
42
|
|
|
@@ -219,26 +218,40 @@ class RecurrenceWidget extends Component {
|
|
|
219
218
|
|
|
220
219
|
componentDidUpdate(prevProps) {
|
|
221
220
|
if (this.props.value) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
this.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
rruleSet
|
|
240
|
-
|
|
241
|
-
|
|
221
|
+
const changedStart =
|
|
222
|
+
prevProps.formData?.start !== this.props.formData?.start;
|
|
223
|
+
const changedEnd = prevProps.formData?.end !== this.props.formData?.end;
|
|
224
|
+
|
|
225
|
+
if (changedStart || changedEnd) {
|
|
226
|
+
let start = this.getUTCDate(this.props.formData?.start).toDate();
|
|
227
|
+
|
|
228
|
+
let changeFormValues = {};
|
|
229
|
+
if (changedEnd) {
|
|
230
|
+
changeFormValues.until = this.getUTCDate(
|
|
231
|
+
this.props.formData?.end,
|
|
232
|
+
).toDate();
|
|
233
|
+
}
|
|
234
|
+
this.setState(
|
|
235
|
+
(prevState) => {
|
|
236
|
+
let rruleSet = prevState.rruleSet;
|
|
237
|
+
|
|
238
|
+
rruleSet = this.updateRruleSet(
|
|
239
|
+
rruleSet,
|
|
240
|
+
{ ...prevState.formValues, ...changeFormValues },
|
|
241
|
+
'dtstart',
|
|
242
|
+
start,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
...prevState,
|
|
247
|
+
rruleSet,
|
|
248
|
+
};
|
|
249
|
+
},
|
|
250
|
+
() => {
|
|
251
|
+
//then, after set state, set recurrence rrule value
|
|
252
|
+
this.saveRrule();
|
|
253
|
+
},
|
|
254
|
+
);
|
|
242
255
|
}
|
|
243
256
|
}
|
|
244
257
|
}
|
|
@@ -250,7 +263,8 @@ class RecurrenceWidget extends Component {
|
|
|
250
263
|
setRecurrenceStartEnd = () => {
|
|
251
264
|
const start = this.props.formData?.start;
|
|
252
265
|
|
|
253
|
-
|
|
266
|
+
// The `start` date from Plone is in UTC
|
|
267
|
+
const _start = new Date(start);
|
|
254
268
|
|
|
255
269
|
this.setState((prevState) => {
|
|
256
270
|
let rruleSet = prevState.rruleSet;
|
|
@@ -339,7 +353,7 @@ class RecurrenceWidget extends Component {
|
|
|
339
353
|
case 'until':
|
|
340
354
|
if (value != null) {
|
|
341
355
|
formValues['recurrenceEnds'] = option;
|
|
342
|
-
formValues[option] =
|
|
356
|
+
formValues[option] = value;
|
|
343
357
|
}
|
|
344
358
|
break;
|
|
345
359
|
case 'byweekday':
|
|
@@ -422,7 +436,24 @@ class RecurrenceWidget extends Component {
|
|
|
422
436
|
}
|
|
423
437
|
break;
|
|
424
438
|
case 'until':
|
|
425
|
-
|
|
439
|
+
let mDate = null;
|
|
440
|
+
if (value) {
|
|
441
|
+
mDate = this.moment(new Date(value));
|
|
442
|
+
if (typeof value === 'string') {
|
|
443
|
+
mDate = this.moment(new Date(value));
|
|
444
|
+
} else {
|
|
445
|
+
//object-->Date()
|
|
446
|
+
mDate = this.moment(value);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (this.props.formData.end) {
|
|
450
|
+
//set time from formData.end
|
|
451
|
+
const mEnd = this.moment(new Date(this.props.formData.end));
|
|
452
|
+
mDate.set('hour', mEnd.get('hour'));
|
|
453
|
+
mDate.set('minute', mEnd.get('minute'));
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
value = value ? mDate.toDate() : null;
|
|
426
457
|
break;
|
|
427
458
|
default:
|
|
428
459
|
break;
|
|
@@ -447,7 +478,7 @@ class RecurrenceWidget extends Component {
|
|
|
447
478
|
? value
|
|
448
479
|
: rruleSet.dtstart()
|
|
449
480
|
? rruleSet.dtstart()
|
|
450
|
-
:
|
|
481
|
+
: new Date();
|
|
451
482
|
var exdates =
|
|
452
483
|
field === 'exdates' ? value : Object.assign([], rruleSet.exdates());
|
|
453
484
|
|
|
@@ -470,13 +501,14 @@ class RecurrenceWidget extends Component {
|
|
|
470
501
|
|
|
471
502
|
getDefaultUntil = (freq) => {
|
|
472
503
|
const moment = this.moment;
|
|
504
|
+
|
|
473
505
|
var end = this.props.formData?.end
|
|
474
|
-
?
|
|
506
|
+
? moment(new Date(this.props.formData.end))
|
|
475
507
|
: null;
|
|
476
|
-
var tomorrow =
|
|
477
|
-
var nextWeek =
|
|
478
|
-
var nextMonth =
|
|
479
|
-
var nextYear =
|
|
508
|
+
var tomorrow = moment().add(1, 'days');
|
|
509
|
+
var nextWeek = moment().add(7, 'days');
|
|
510
|
+
var nextMonth = moment().add(1, 'months');
|
|
511
|
+
var nextYear = moment().add(1, 'years');
|
|
480
512
|
|
|
481
513
|
var until = end;
|
|
482
514
|
switch (freq) {
|
|
@@ -502,6 +534,19 @@ class RecurrenceWidget extends Component {
|
|
|
502
534
|
break;
|
|
503
535
|
}
|
|
504
536
|
|
|
537
|
+
if (this.props.formData.end) {
|
|
538
|
+
//set default end time
|
|
539
|
+
until.set('hour', end.get('hour'));
|
|
540
|
+
until.set('minute', end.get('minute'));
|
|
541
|
+
}
|
|
542
|
+
until = new Date(
|
|
543
|
+
until.get('year'),
|
|
544
|
+
until.get('month'),
|
|
545
|
+
until.get('date'),
|
|
546
|
+
until.get('hour'),
|
|
547
|
+
until.get('minute'),
|
|
548
|
+
);
|
|
549
|
+
|
|
505
550
|
return until;
|
|
506
551
|
};
|
|
507
552
|
|
|
@@ -716,9 +761,13 @@ class RecurrenceWidget extends Component {
|
|
|
716
761
|
}
|
|
717
762
|
};
|
|
718
763
|
|
|
719
|
-
|
|
764
|
+
saveRrule = () => {
|
|
720
765
|
var value = this.state.rruleSet.toString();
|
|
721
766
|
this.props.onChange(this.props.id, value);
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
save = () => {
|
|
770
|
+
this.saveRrule();
|
|
722
771
|
this.close();
|
|
723
772
|
};
|
|
724
773
|
|
|
@@ -71,6 +71,7 @@ class Search extends Component {
|
|
|
71
71
|
|
|
72
72
|
constructor(props) {
|
|
73
73
|
super(props);
|
|
74
|
+
this.defaultPageSize = config.settings.defaultPageSize;
|
|
74
75
|
this.state = { currentPage: 1, isClient: false, active: 'relevance' };
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -109,19 +110,24 @@ class Search extends Component {
|
|
|
109
110
|
const options = qs.parse(this.props.history.location.search);
|
|
110
111
|
this.setState({ currentPage: 1 });
|
|
111
112
|
options['use_site_search_settings'] = 1;
|
|
112
|
-
this.props.searchContent('',
|
|
113
|
+
this.props.searchContent('', {
|
|
114
|
+
b_size: this.defaultPageSize,
|
|
115
|
+
...options,
|
|
116
|
+
});
|
|
113
117
|
};
|
|
114
118
|
|
|
115
119
|
handleQueryPaginationChange = (e, { activePage }) => {
|
|
116
|
-
const { settings } = config;
|
|
117
120
|
window.scrollTo(0, 0);
|
|
118
121
|
let options = qs.parse(this.props.history.location.search);
|
|
119
122
|
options['use_site_search_settings'] = 1;
|
|
120
123
|
|
|
121
124
|
this.setState({ currentPage: activePage }, () => {
|
|
122
125
|
this.props.searchContent('', {
|
|
126
|
+
b_size: this.defaultPageSize,
|
|
123
127
|
...options,
|
|
124
|
-
b_start:
|
|
128
|
+
b_start:
|
|
129
|
+
(this.state.currentPage - 1) *
|
|
130
|
+
(options.b_size || this.defaultPageSize),
|
|
125
131
|
});
|
|
126
132
|
});
|
|
127
133
|
};
|
|
@@ -149,7 +155,8 @@ class Search extends Component {
|
|
|
149
155
|
* @returns {string} Markup for the component.
|
|
150
156
|
*/
|
|
151
157
|
render() {
|
|
152
|
-
const
|
|
158
|
+
const options = qs.parse(this.props.history.location.search);
|
|
159
|
+
|
|
153
160
|
return (
|
|
154
161
|
<Container id="page-search">
|
|
155
162
|
<Helmet title={this.props.intl.formatMessage(messages.Search)} />
|
|
@@ -282,7 +289,8 @@ class Search extends Component {
|
|
|
282
289
|
<Pagination
|
|
283
290
|
activePage={this.state.currentPage}
|
|
284
291
|
totalPages={Math.ceil(
|
|
285
|
-
this.props.search.items_total /
|
|
292
|
+
this.props.search.items_total /
|
|
293
|
+
(options.b_size || this.defaultPageSize),
|
|
286
294
|
)}
|
|
287
295
|
onPageChange={this.handleQueryPaginationChange}
|
|
288
296
|
firstItem={null}
|