@plone/volto 17.0.0-alpha.25 → 17.0.0-alpha.26
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/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +52 -5
- package/README.md +8 -7
- package/cypress/support/commands.js +12 -9
- package/cypress.config.js +1 -0
- package/locales/ca/LC_MESSAGES/volto.po +36 -15
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +36 -15
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +35 -14
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +65 -44
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +35 -14
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +35 -14
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +36 -15
- package/locales/fr.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +35 -14
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +35 -14
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +36 -15
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +36 -15
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +35 -14
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +36 -15
- package/locales/ro.json +1 -1
- package/locales/volto.pot +35 -14
- package/locales/zh_CN/LC_MESSAGES/volto.po +36 -15
- package/locales/zh_CN.json +1 -1
- package/package.json +4 -4
- package/packages/volto-slate/package.json +1 -1
- package/packages/volto-slate/src/editor/render.jsx +2 -3
- package/src/actions/index.js +3 -0
- package/src/actions/navroot/navroot.js +16 -0
- package/src/actions/navroot/navroot.test.js +15 -0
- package/src/actions/site/site.js +16 -0
- package/src/actions/site/site.test.js +15 -0
- package/src/actions/userSession/userSession.js +17 -1
- package/src/components/manage/Blocks/Block/Settings.jsx +2 -0
- package/src/components/manage/Blocks/Block/Settings.test.jsx +90 -0
- package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +1 -1
- package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +42 -25
- package/src/components/manage/Blocks/ToC/View.jsx +75 -13
- package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx +2 -12
- package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +65 -38
- package/src/components/manage/Controlpanels/Rules/AddRule.jsx +1 -1
- package/src/components/manage/Controlpanels/Rules/EditRule.jsx +1 -1
- package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +95 -5
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +116 -103
- package/src/components/manage/Form/BlockDataForm.jsx +3 -2
- package/src/components/manage/Form/BlockDataForm.test.jsx +34 -2
- package/src/components/manage/LinksToItem/LinksToItem.test.jsx +4 -1
- package/src/components/manage/Messages/Messages.jsx +32 -99
- package/src/components/manage/Messages/Messages.test.jsx +0 -1
- package/src/components/manage/Sharing/Sharing.jsx +39 -16
- package/src/components/manage/UniversalLink/UniversalLink.jsx +4 -6
- package/src/components/manage/Widgets/ArrayWidget.jsx +3 -1
- package/src/components/manage/Widgets/ArrayWidget.test.jsx +45 -1
- package/src/components/manage/Widgets/RegistryImageWidget.jsx +210 -0
- package/src/components/manage/Widgets/RegistryImageWidget.test.jsx +91 -0
- package/src/components/manage/Widgets/SelectWidget.jsx +15 -1
- package/src/components/manage/Widgets/SelectWidget.test.jsx +45 -1
- package/src/components/theme/ContentMetadataTags/ContentMetadataTags.jsx +37 -3
- package/src/components/theme/Login/Login.jsx +159 -241
- package/src/components/theme/Logo/Logo.Multilingual.test.jsx +131 -1
- package/src/components/theme/Logo/Logo.jsx +35 -29
- package/src/components/theme/Logo/Logo.test.jsx +135 -1
- package/src/components/theme/Logout/Logout.jsx +1 -1
- package/src/components/theme/Navigation/Navigation.jsx +86 -171
- package/src/components/theme/SearchWidget/SearchWidget.jsx +15 -3
- package/src/components/theme/SearchWidget/SearchWidget.test.jsx +8 -0
- package/src/components/theme/View/View.jsx +2 -0
- package/src/config/ControlPanels.js +0 -1
- package/src/config/Widgets.jsx +2 -0
- package/src/config/index.js +15 -3
- package/src/constants/ActionTypes.js +3 -0
- package/src/express-middleware/images.js +1 -0
- package/src/helpers/MessageLabels/MessageLabels.js +26 -4
- package/src/helpers/Site/index.js +21 -0
- package/src/helpers/index.js +1 -0
- package/src/reducers/index.js +4 -0
- package/src/reducers/navroot/navroot.js +79 -0
- package/src/reducers/navroot/navroot.test.js +110 -0
- package/src/reducers/site/site.js +51 -0
- package/src/reducers/site/site.test.js +67 -0
- package/src/reducers/userSession/userSession.js +15 -1
- package/test-setup-config.js +1 -0
- package/theme/themes/pastanaga/elements/input.overrides +5 -1
- package/theme/themes/pastanaga/extras/login.less +3 -0
- package/webpack-plugins/webpack-less-plugin.js +19 -0
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getControlpanel,
|
|
12
12
|
updateUser,
|
|
13
13
|
updateGroup,
|
|
14
|
+
getUserSchema,
|
|
14
15
|
} from '@plone/volto/actions';
|
|
15
16
|
import {
|
|
16
17
|
Icon,
|
|
@@ -98,6 +99,7 @@ class UsersControlpanel extends Component {
|
|
|
98
99
|
this.updateUserRole = this.updateUserRole.bind(this);
|
|
99
100
|
this.state = {
|
|
100
101
|
search: '',
|
|
102
|
+
isLoading: false,
|
|
101
103
|
showAddUser: false,
|
|
102
104
|
showAddUserErrorConfirm: false,
|
|
103
105
|
addUserError: '',
|
|
@@ -121,6 +123,7 @@ class UsersControlpanel extends Component {
|
|
|
121
123
|
entries: this.props.users,
|
|
122
124
|
});
|
|
123
125
|
}
|
|
126
|
+
await this.props.getUserSchema();
|
|
124
127
|
};
|
|
125
128
|
|
|
126
129
|
// Because username field needs to be disabled if email login is enabled!
|
|
@@ -181,9 +184,19 @@ class UsersControlpanel extends Component {
|
|
|
181
184
|
*/
|
|
182
185
|
onSearch(event) {
|
|
183
186
|
event.preventDefault();
|
|
184
|
-
this.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
this.setState({ isLoading: true });
|
|
188
|
+
this.props
|
|
189
|
+
.listUsers({
|
|
190
|
+
search: this.state.search,
|
|
191
|
+
})
|
|
192
|
+
.then(() => {
|
|
193
|
+
this.setState({ isLoading: false });
|
|
194
|
+
})
|
|
195
|
+
.catch((error) => {
|
|
196
|
+
this.setState({ isLoading: false });
|
|
197
|
+
// eslint-disable-next-line no-console
|
|
198
|
+
console.error('Error searching users', error);
|
|
199
|
+
});
|
|
187
200
|
}
|
|
188
201
|
|
|
189
202
|
/**
|
|
@@ -265,12 +278,28 @@ class UsersControlpanel extends Component {
|
|
|
265
278
|
* @returns {undefined}
|
|
266
279
|
*/
|
|
267
280
|
onAddUserSubmit(data, callback) {
|
|
268
|
-
const { groups, sendPasswordReset } = data;
|
|
269
|
-
if (
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
281
|
+
const { groups, sendPasswordReset, password } = data;
|
|
282
|
+
if (
|
|
283
|
+
sendPasswordReset !== undefined &&
|
|
284
|
+
sendPasswordReset === true &&
|
|
285
|
+
password !== undefined
|
|
286
|
+
) {
|
|
287
|
+
toast.error(
|
|
288
|
+
<Toast
|
|
289
|
+
error
|
|
290
|
+
title={this.props.intl.formatMessage(messages.error)}
|
|
291
|
+
content={this.props.intl.formatMessage(
|
|
292
|
+
messages.addUserFormPasswordAndSendPasswordTogetherNotAllowed,
|
|
293
|
+
)}
|
|
294
|
+
/>,
|
|
295
|
+
);
|
|
296
|
+
} else {
|
|
297
|
+
if (groups && groups.length > 0) this.addUserToGroup(data);
|
|
298
|
+
this.props.createUser(data, sendPasswordReset);
|
|
299
|
+
this.setState({
|
|
300
|
+
addUserSetFormDataCallback: callback,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
274
303
|
}
|
|
275
304
|
|
|
276
305
|
/**
|
|
@@ -392,6 +421,65 @@ class UsersControlpanel extends Component {
|
|
|
392
421
|
let usernameToDelete = this.state.userToDelete
|
|
393
422
|
? this.state.userToDelete.username
|
|
394
423
|
: '';
|
|
424
|
+
// Copy the userschema using JSON serialization/deserialization
|
|
425
|
+
// this is really ugly, but if we don't do this the original value
|
|
426
|
+
// of the userschema is changed and it is used like that through
|
|
427
|
+
// the lifecycle of the application
|
|
428
|
+
let adduserschema = {};
|
|
429
|
+
if (this.props?.userschema?.loaded) {
|
|
430
|
+
adduserschema = JSON.parse(
|
|
431
|
+
JSON.stringify(this.props?.userschema?.userschema),
|
|
432
|
+
);
|
|
433
|
+
adduserschema.properties['username'] = {
|
|
434
|
+
title: this.props.intl.formatMessage(messages.addUserFormUsernameTitle),
|
|
435
|
+
type: 'string',
|
|
436
|
+
description: this.props.intl.formatMessage(
|
|
437
|
+
messages.addUserFormUsernameDescription,
|
|
438
|
+
),
|
|
439
|
+
};
|
|
440
|
+
adduserschema.properties['password'] = {
|
|
441
|
+
title: this.props.intl.formatMessage(messages.addUserFormPasswordTitle),
|
|
442
|
+
type: 'password',
|
|
443
|
+
description: this.props.intl.formatMessage(
|
|
444
|
+
messages.addUserFormPasswordDescription,
|
|
445
|
+
),
|
|
446
|
+
widget: 'password',
|
|
447
|
+
};
|
|
448
|
+
adduserschema.properties['sendPasswordReset'] = {
|
|
449
|
+
title: this.props.intl.formatMessage(
|
|
450
|
+
messages.addUserFormSendPasswordResetTitle,
|
|
451
|
+
),
|
|
452
|
+
type: 'boolean',
|
|
453
|
+
};
|
|
454
|
+
adduserschema.properties['roles'] = {
|
|
455
|
+
title: this.props.intl.formatMessage(messages.addUserFormRolesTitle),
|
|
456
|
+
type: 'array',
|
|
457
|
+
choices: this.props.roles.map((role) => [role.id, role.title]),
|
|
458
|
+
noValueOption: false,
|
|
459
|
+
};
|
|
460
|
+
adduserschema.properties['groups'] = {
|
|
461
|
+
title: this.props.intl.formatMessage(messages.addUserGroupNameTitle),
|
|
462
|
+
type: 'array',
|
|
463
|
+
choices: this.props.groups.map((group) => [group.id, group.id]),
|
|
464
|
+
noValueOption: false,
|
|
465
|
+
};
|
|
466
|
+
if (
|
|
467
|
+
adduserschema.fieldsets &&
|
|
468
|
+
adduserschema.fieldsets.length > 0 &&
|
|
469
|
+
!adduserschema.fieldsets[0]['fields'].includes('username')
|
|
470
|
+
) {
|
|
471
|
+
adduserschema.fieldsets[0]['fields'] = adduserschema.fieldsets[0][
|
|
472
|
+
'fields'
|
|
473
|
+
].concat([
|
|
474
|
+
'username',
|
|
475
|
+
'password',
|
|
476
|
+
'sendPasswordReset',
|
|
477
|
+
'roles',
|
|
478
|
+
'groups',
|
|
479
|
+
]);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
395
483
|
return (
|
|
396
484
|
<Container className="users-control-panel">
|
|
397
485
|
<Helmet title={this.props.intl.formatMessage(messages.users)} />
|
|
@@ -418,7 +506,7 @@ class UsersControlpanel extends Component {
|
|
|
418
506
|
onConfirm={this.onDeleteOk}
|
|
419
507
|
size={null}
|
|
420
508
|
/>
|
|
421
|
-
{this.state.showAddUser ? (
|
|
509
|
+
{this.props?.userschema?.loaded && this.state.showAddUser ? (
|
|
422
510
|
<ModalForm
|
|
423
511
|
open={this.state.showAddUser}
|
|
424
512
|
className="modal"
|
|
@@ -429,96 +517,7 @@ class UsersControlpanel extends Component {
|
|
|
429
517
|
}
|
|
430
518
|
title={this.props.intl.formatMessage(messages.addUserFormTitle)}
|
|
431
519
|
loading={this.props.createRequest.loading}
|
|
432
|
-
schema={
|
|
433
|
-
fieldsets: [
|
|
434
|
-
{
|
|
435
|
-
id: 'default',
|
|
436
|
-
title: 'FIXME: User Data',
|
|
437
|
-
fields: [
|
|
438
|
-
...(!this.state.loginUsingEmail ? ['username'] : []),
|
|
439
|
-
'fullname',
|
|
440
|
-
'email',
|
|
441
|
-
'password',
|
|
442
|
-
'sendPasswordReset',
|
|
443
|
-
'roles',
|
|
444
|
-
'groups',
|
|
445
|
-
],
|
|
446
|
-
},
|
|
447
|
-
],
|
|
448
|
-
properties: {
|
|
449
|
-
...(!this.state.loginUsingEmail
|
|
450
|
-
? {
|
|
451
|
-
username: {
|
|
452
|
-
title: this.props.intl.formatMessage(
|
|
453
|
-
messages.addUserFormUsernameTitle,
|
|
454
|
-
),
|
|
455
|
-
type: 'string',
|
|
456
|
-
description: this.props.intl.formatMessage(
|
|
457
|
-
messages.addUserFormUsernameDescription,
|
|
458
|
-
),
|
|
459
|
-
},
|
|
460
|
-
}
|
|
461
|
-
: {}),
|
|
462
|
-
fullname: {
|
|
463
|
-
title: this.props.intl.formatMessage(
|
|
464
|
-
messages.addUserFormFullnameTitle,
|
|
465
|
-
),
|
|
466
|
-
type: 'string',
|
|
467
|
-
description: this.props.intl.formatMessage(
|
|
468
|
-
messages.addUserFormFullnameDescription,
|
|
469
|
-
),
|
|
470
|
-
},
|
|
471
|
-
email: {
|
|
472
|
-
title: this.props.intl.formatMessage(
|
|
473
|
-
messages.addUserFormEmailTitle,
|
|
474
|
-
),
|
|
475
|
-
type: 'string',
|
|
476
|
-
description: this.props.intl.formatMessage(
|
|
477
|
-
messages.addUserFormEmailDescription,
|
|
478
|
-
),
|
|
479
|
-
widget: 'email',
|
|
480
|
-
},
|
|
481
|
-
password: {
|
|
482
|
-
title: this.props.intl.formatMessage(
|
|
483
|
-
messages.addUserFormPasswordTitle,
|
|
484
|
-
),
|
|
485
|
-
type: 'password',
|
|
486
|
-
description: this.props.intl.formatMessage(
|
|
487
|
-
messages.addUserFormPasswordDescription,
|
|
488
|
-
),
|
|
489
|
-
widget: 'password',
|
|
490
|
-
},
|
|
491
|
-
sendPasswordReset: {
|
|
492
|
-
title: this.props.intl.formatMessage(
|
|
493
|
-
messages.addUserFormSendPasswordResetTitle,
|
|
494
|
-
),
|
|
495
|
-
type: 'boolean',
|
|
496
|
-
},
|
|
497
|
-
roles: {
|
|
498
|
-
title: this.props.intl.formatMessage(
|
|
499
|
-
messages.addUserFormRolesTitle,
|
|
500
|
-
),
|
|
501
|
-
type: 'array',
|
|
502
|
-
choices: this.props.roles.map((role) => [
|
|
503
|
-
role.id,
|
|
504
|
-
role.title,
|
|
505
|
-
]),
|
|
506
|
-
noValueOption: false,
|
|
507
|
-
},
|
|
508
|
-
groups: {
|
|
509
|
-
title: this.props.intl.formatMessage(
|
|
510
|
-
messages.addUserGroupNameTitle,
|
|
511
|
-
),
|
|
512
|
-
type: 'array',
|
|
513
|
-
choices: this.props.groups.map((group) => [
|
|
514
|
-
group.id,
|
|
515
|
-
group.id,
|
|
516
|
-
]),
|
|
517
|
-
noValueOption: false,
|
|
518
|
-
},
|
|
519
|
-
},
|
|
520
|
-
required: ['username', 'email'],
|
|
521
|
-
}}
|
|
520
|
+
schema={adduserschema}
|
|
522
521
|
/>
|
|
523
522
|
) : null}
|
|
524
523
|
</div>
|
|
@@ -547,7 +546,11 @@ class UsersControlpanel extends Component {
|
|
|
547
546
|
<Form.Field>
|
|
548
547
|
<Input
|
|
549
548
|
name="SearchableText"
|
|
550
|
-
action={{
|
|
549
|
+
action={{
|
|
550
|
+
icon: 'search',
|
|
551
|
+
loading: this.state.isLoading,
|
|
552
|
+
disabled: this.state.isLoading,
|
|
553
|
+
}}
|
|
551
554
|
placeholder={this.props.intl.formatMessage(
|
|
552
555
|
messages.searchUsers,
|
|
553
556
|
)}
|
|
@@ -558,7 +561,8 @@ class UsersControlpanel extends Component {
|
|
|
558
561
|
</Form>
|
|
559
562
|
</Segment>
|
|
560
563
|
<Form>
|
|
561
|
-
|
|
564
|
+
{((this.props.many_users && this.state.entries.length > 0) ||
|
|
565
|
+
!this.props.many_users) && (
|
|
562
566
|
<Table padded striped attached unstackable>
|
|
563
567
|
<Table.Header>
|
|
564
568
|
<Table.Row>
|
|
@@ -592,11 +596,18 @@ class UsersControlpanel extends Component {
|
|
|
592
596
|
user={user}
|
|
593
597
|
updateUser={this.updateUserRole}
|
|
594
598
|
inheritedRole={this.props.inheritedRole}
|
|
599
|
+
userschema={this.props.userschema}
|
|
600
|
+
listUsers={this.props.listUsers}
|
|
595
601
|
/>
|
|
596
602
|
))}
|
|
597
603
|
</Table.Body>
|
|
598
604
|
</Table>
|
|
599
|
-
|
|
605
|
+
)}
|
|
606
|
+
{this.state.entries.length === 0 && this.state.search && (
|
|
607
|
+
<Segment>
|
|
608
|
+
{this.props.intl.formatMessage(messages.userSearchNoResults)}
|
|
609
|
+
</Segment>
|
|
610
|
+
)}
|
|
600
611
|
<div className="contents-pagination">
|
|
601
612
|
<Pagination
|
|
602
613
|
current={this.state.currentPage}
|
|
@@ -684,6 +695,7 @@ export default compose(
|
|
|
684
695
|
createRequest: state.users.create,
|
|
685
696
|
loadRolesRequest: state.roles,
|
|
686
697
|
inheritedRole: state.authRole.authenticatedRole,
|
|
698
|
+
userschema: state.userschema,
|
|
687
699
|
controlPanelData: state.controlpanels?.controlpanel,
|
|
688
700
|
}),
|
|
689
701
|
(dispatch) =>
|
|
@@ -697,6 +709,7 @@ export default compose(
|
|
|
697
709
|
createUser,
|
|
698
710
|
updateUser,
|
|
699
711
|
updateGroup,
|
|
712
|
+
getUserSchema,
|
|
700
713
|
},
|
|
701
714
|
dispatch,
|
|
702
715
|
),
|
|
@@ -5,7 +5,7 @@ import { withVariationSchemaEnhancer } from '@plone/volto/helpers';
|
|
|
5
5
|
const EnhancedBlockDataForm = withVariationSchemaEnhancer(InlineForm);
|
|
6
6
|
|
|
7
7
|
export default function BlockDataForm(props) {
|
|
8
|
-
const { onChangeBlock, block } = props;
|
|
8
|
+
const { onChangeBlock, block, applySchemaEnhancers = true } = props;
|
|
9
9
|
|
|
10
10
|
if (!onChangeBlock) {
|
|
11
11
|
// eslint-disable-next-line no-console
|
|
@@ -19,8 +19,9 @@ export default function BlockDataForm(props) {
|
|
|
19
19
|
[block, onChangeBlock],
|
|
20
20
|
);
|
|
21
21
|
|
|
22
|
+
const Form = applySchemaEnhancers ? EnhancedBlockDataForm : InlineForm;
|
|
22
23
|
return (
|
|
23
|
-
<
|
|
24
|
+
<Form
|
|
24
25
|
{...props}
|
|
25
26
|
onChangeFormData={onChangeBlock ? onChangeFormData : undefined}
|
|
26
27
|
/>
|
|
@@ -72,7 +72,7 @@ beforeAll(() => {
|
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
describe('BlockDataForm', () => {
|
|
75
|
-
it('should
|
|
75
|
+
it('should not add variations to schema when unneeded', () => {
|
|
76
76
|
const WrappedBlockDataForm = withStateManagement(BlockDataForm);
|
|
77
77
|
const store = mockStore({
|
|
78
78
|
intl: {
|
|
@@ -103,7 +103,7 @@ describe('BlockDataForm', () => {
|
|
|
103
103
|
expect(testSchema.fieldsets[0].fields).toStrictEqual([]);
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
it('should
|
|
106
|
+
it('should not add variations when only one variation', () => {
|
|
107
107
|
const WrappedBlockDataForm = withStateManagement(BlockDataForm);
|
|
108
108
|
const store = mockStore({
|
|
109
109
|
intl: {
|
|
@@ -164,4 +164,36 @@ describe('BlockDataForm', () => {
|
|
|
164
164
|
// schema is cloned, not mutated in place
|
|
165
165
|
expect(testSchema.fieldsets[0].fields).toStrictEqual([]);
|
|
166
166
|
});
|
|
167
|
+
|
|
168
|
+
it('should not add variations to schema when explicitly disabled', () => {
|
|
169
|
+
const WrappedBlockDataForm = withStateManagement(BlockDataForm);
|
|
170
|
+
const store = mockStore({
|
|
171
|
+
intl: {
|
|
172
|
+
locale: 'en',
|
|
173
|
+
messages: {},
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
const testSchema = {
|
|
177
|
+
fieldsets: [{ title: 'Default', id: 'default', fields: [] }],
|
|
178
|
+
properties: {},
|
|
179
|
+
required: [],
|
|
180
|
+
};
|
|
181
|
+
const formData = {
|
|
182
|
+
'@type': 'testBlock',
|
|
183
|
+
};
|
|
184
|
+
const { container } = render(
|
|
185
|
+
<Provider store={store}>
|
|
186
|
+
<WrappedBlockDataForm
|
|
187
|
+
formData={formData}
|
|
188
|
+
schema={testSchema}
|
|
189
|
+
onChangeField={(id, value) => {}}
|
|
190
|
+
applySchemaEnhancers={false}
|
|
191
|
+
/>
|
|
192
|
+
</Provider>,
|
|
193
|
+
);
|
|
194
|
+
expect(container).toMatchSnapshot();
|
|
195
|
+
|
|
196
|
+
// schema is cloned, not mutated in place
|
|
197
|
+
expect(testSchema.fieldsets[0].fields).toStrictEqual([]);
|
|
198
|
+
});
|
|
167
199
|
});
|
|
@@ -3,6 +3,7 @@ import renderer from 'react-test-renderer';
|
|
|
3
3
|
import { Provider } from 'react-intl-redux';
|
|
4
4
|
import configureMockStore from 'redux-mock-store';
|
|
5
5
|
import thunk from 'redux-thunk';
|
|
6
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
6
7
|
|
|
7
8
|
import LinksToItem from './LinksToItem';
|
|
8
9
|
|
|
@@ -88,7 +89,9 @@ describe('LinksToItem', () => {
|
|
|
88
89
|
});
|
|
89
90
|
const component = renderer.create(
|
|
90
91
|
<Provider store={store}>
|
|
91
|
-
<
|
|
92
|
+
<MemoryRouter>
|
|
93
|
+
<LinksToItem location={{ pathname: '/page-1/links-to-item' }} />
|
|
94
|
+
</MemoryRouter>
|
|
92
95
|
</Provider>,
|
|
93
96
|
);
|
|
94
97
|
const json = component.toJSON();
|
|
@@ -1,107 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
* Messages component.
|
|
3
|
-
* @module components/manage/Messages/Messages
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { Component } from 'react';
|
|
7
|
-
import PropTypes from 'prop-types';
|
|
8
|
-
import { connect } from 'react-redux';
|
|
1
|
+
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
|
|
9
2
|
import { Message, Container } from 'semantic-ui-react';
|
|
10
3
|
import { map } from 'lodash';
|
|
11
4
|
|
|
12
5
|
import { removeMessage } from '@plone/volto/actions';
|
|
13
6
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* @class Messages
|
|
17
|
-
* @extends Component
|
|
18
|
-
*/
|
|
19
|
-
class Messages extends Component {
|
|
20
|
-
/**
|
|
21
|
-
* Property types.
|
|
22
|
-
* @property {Object} propTypes Property types.
|
|
23
|
-
* @static
|
|
24
|
-
*/
|
|
25
|
-
static propTypes = {
|
|
26
|
-
removeMessage: PropTypes.func.isRequired,
|
|
27
|
-
messages: PropTypes.arrayOf(
|
|
28
|
-
PropTypes.shape({
|
|
29
|
-
title: PropTypes.string,
|
|
30
|
-
body: PropTypes.string,
|
|
31
|
-
level: PropTypes.string,
|
|
32
|
-
}),
|
|
33
|
-
).isRequired,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Constructor
|
|
38
|
-
* @method constructor
|
|
39
|
-
* @param {Object} props Component properties
|
|
40
|
-
* @constructs Messages
|
|
41
|
-
*/
|
|
42
|
-
constructor(props) {
|
|
43
|
-
super(props);
|
|
44
|
-
this.onDismiss = this.onDismiss.bind(this);
|
|
45
|
-
}
|
|
7
|
+
const Messages = () => {
|
|
8
|
+
const dispatch = useDispatch();
|
|
46
9
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// * @returns {undefined}
|
|
52
|
-
// */
|
|
53
|
-
// componentWillReceiveProps(nextProps) {
|
|
54
|
-
// if (nextProps.messages.length > this.props.messages.length) {
|
|
55
|
-
// window.setTimeout(() => {
|
|
56
|
-
// if (this.props.messages.length > 0) {
|
|
57
|
-
// this.props.removeMessage(-1);
|
|
58
|
-
// }
|
|
59
|
-
// }, 6000);
|
|
60
|
-
// }
|
|
61
|
-
// }
|
|
10
|
+
const messages = useSelector(
|
|
11
|
+
(state) => state.messages.messages,
|
|
12
|
+
shallowEqual,
|
|
13
|
+
);
|
|
62
14
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
* @param {Object} event Event object
|
|
67
|
-
* @param {number} value Index of message
|
|
68
|
-
* @returns {undefined}
|
|
69
|
-
*/
|
|
70
|
-
onDismiss(event, { value }) {
|
|
71
|
-
this.props.removeMessage(value);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Render method.
|
|
76
|
-
* @method render
|
|
77
|
-
* @returns {string} Markup for the component.
|
|
78
|
-
*/
|
|
79
|
-
render() {
|
|
80
|
-
return (
|
|
81
|
-
this.props.messages && (
|
|
82
|
-
<Container className="messages">
|
|
83
|
-
{map(this.props.messages, (message, index) => (
|
|
84
|
-
<Message
|
|
85
|
-
key={message.id}
|
|
86
|
-
value={index}
|
|
87
|
-
onDismiss={this.onDismiss}
|
|
88
|
-
error={message.level === 'error'}
|
|
89
|
-
success={message.level === 'success'}
|
|
90
|
-
warning={message.level === 'warning'}
|
|
91
|
-
info={message.level === 'info'}
|
|
92
|
-
header={message.title}
|
|
93
|
-
content={message.body}
|
|
94
|
-
/>
|
|
95
|
-
))}
|
|
96
|
-
</Container>
|
|
97
|
-
)
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
15
|
+
const onDismiss = (event, { value }) => {
|
|
16
|
+
dispatch(removeMessage(value));
|
|
17
|
+
};
|
|
101
18
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
19
|
+
return (
|
|
20
|
+
messages && (
|
|
21
|
+
<Container className="messages">
|
|
22
|
+
{map(messages, (message, index) => (
|
|
23
|
+
<Message
|
|
24
|
+
key={message.id}
|
|
25
|
+
value={index}
|
|
26
|
+
onDismiss={onDismiss}
|
|
27
|
+
error={message.level === 'error'}
|
|
28
|
+
success={message.level === 'success'}
|
|
29
|
+
warning={message.level === 'warning'}
|
|
30
|
+
info={message.level === 'info'}
|
|
31
|
+
header={message.title}
|
|
32
|
+
content={message.body}
|
|
33
|
+
/>
|
|
34
|
+
))}
|
|
35
|
+
</Container>
|
|
36
|
+
)
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default Messages;
|
|
@@ -145,6 +145,7 @@ class SharingComponent extends Component {
|
|
|
145
145
|
this.onToggleInherit = this.onToggleInherit.bind(this);
|
|
146
146
|
this.state = {
|
|
147
147
|
search: '',
|
|
148
|
+
isLoading: false,
|
|
148
149
|
inherit: props.inherit,
|
|
149
150
|
entries: props.entries,
|
|
150
151
|
isClient: false,
|
|
@@ -225,7 +226,17 @@ class SharingComponent extends Component {
|
|
|
225
226
|
*/
|
|
226
227
|
onSearch(event) {
|
|
227
228
|
event.preventDefault();
|
|
228
|
-
this.
|
|
229
|
+
this.setState({ isLoading: true });
|
|
230
|
+
this.props
|
|
231
|
+
.getSharing(getBaseUrl(this.props.pathname), this.state.search)
|
|
232
|
+
.then(() => {
|
|
233
|
+
this.setState({ isLoading: false });
|
|
234
|
+
})
|
|
235
|
+
.catch((error) => {
|
|
236
|
+
this.setState({ isLoading: false });
|
|
237
|
+
// eslint-disable-next-line no-console
|
|
238
|
+
console.error('Error searching users or groups', error);
|
|
239
|
+
});
|
|
229
240
|
}
|
|
230
241
|
|
|
231
242
|
/**
|
|
@@ -296,7 +307,10 @@ class SharingComponent extends Component {
|
|
|
296
307
|
<Container id="page-sharing">
|
|
297
308
|
<Helmet title={this.props.intl.formatMessage(messages.sharing)} />
|
|
298
309
|
<Segment.Group raised>
|
|
299
|
-
<Pluggable
|
|
310
|
+
<Pluggable
|
|
311
|
+
name="sharing-component"
|
|
312
|
+
params={{ isLoading: this.state.isLoading }}
|
|
313
|
+
/>
|
|
300
314
|
<Plug pluggable="sharing-component" id="sharing-component-title">
|
|
301
315
|
<Segment className="primary">
|
|
302
316
|
<FormattedMessage
|
|
@@ -318,20 +332,29 @@ class SharingComponent extends Component {
|
|
|
318
332
|
</Segment>
|
|
319
333
|
</Plug>
|
|
320
334
|
<Plug pluggable="sharing-component" id="sharing-component-search">
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
<
|
|
324
|
-
<
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
+
{({ isLoading }) => {
|
|
336
|
+
return (
|
|
337
|
+
<Segment>
|
|
338
|
+
<Form onSubmit={this.onSearch}>
|
|
339
|
+
<Form.Field>
|
|
340
|
+
<Input
|
|
341
|
+
name="SearchableText"
|
|
342
|
+
action={{
|
|
343
|
+
icon: 'search',
|
|
344
|
+
loading: isLoading,
|
|
345
|
+
disabled: isLoading,
|
|
346
|
+
}}
|
|
347
|
+
placeholder={this.props.intl.formatMessage(
|
|
348
|
+
messages.searchForUserOrGroup,
|
|
349
|
+
)}
|
|
350
|
+
onChange={this.onChangeSearch}
|
|
351
|
+
id="sharing-component-search"
|
|
352
|
+
/>
|
|
353
|
+
</Form.Field>
|
|
354
|
+
</Form>
|
|
355
|
+
</Segment>
|
|
356
|
+
);
|
|
357
|
+
}}
|
|
335
358
|
</Plug>
|
|
336
359
|
<Plug
|
|
337
360
|
pluggable="sharing-component"
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from '@plone/volto/helpers/Url/Url';
|
|
15
15
|
|
|
16
16
|
import config from '@plone/volto/registry';
|
|
17
|
+
import cx from 'classnames';
|
|
17
18
|
|
|
18
19
|
const UniversalLink = ({
|
|
19
20
|
href,
|
|
@@ -88,19 +89,16 @@ const UniversalLink = ({
|
|
|
88
89
|
);
|
|
89
90
|
|
|
90
91
|
if (isExternal) {
|
|
92
|
+
const isTelephoneOrMail = checkedURL.isMail || checkedURL.isTelephone;
|
|
91
93
|
tag = (
|
|
92
94
|
<a
|
|
93
95
|
href={url}
|
|
94
96
|
title={title}
|
|
95
97
|
target={
|
|
96
|
-
!
|
|
97
|
-
!checkedURL.isTelephone &&
|
|
98
|
-
!(openLinkInNewTab === false)
|
|
99
|
-
? '_blank'
|
|
100
|
-
: null
|
|
98
|
+
!isTelephoneOrMail && !(openLinkInNewTab === false) ? '_blank' : null
|
|
101
99
|
}
|
|
102
100
|
rel="noopener noreferrer"
|
|
103
|
-
className={className}
|
|
101
|
+
className={cx({ external: !isTelephoneOrMail }, className)}
|
|
104
102
|
{...props}
|
|
105
103
|
>
|
|
106
104
|
{children}
|
|
@@ -325,7 +325,9 @@ class ArrayWidget extends Component {
|
|
|
325
325
|
: this.props.choices
|
|
326
326
|
? [
|
|
327
327
|
...choices,
|
|
328
|
-
...(this.props.noValueOption &&
|
|
328
|
+
...(this.props.noValueOption &&
|
|
329
|
+
(this.props.default === undefined ||
|
|
330
|
+
this.props.default === null)
|
|
329
331
|
? [
|
|
330
332
|
{
|
|
331
333
|
label: this.props.intl.formatMessage(
|