@plone/volto 16.23.0 → 16.25.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.
Files changed (96) hide show
  1. package/.changelog.draft +8 -9
  2. package/.yarn/install-state.gz +0 -0
  3. package/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +541 -0
  4. package/.yarn/releases/yarn-3.6.3.cjs +874 -0
  5. package/CHANGELOG.md +35 -0
  6. package/cypress.config.js +17 -0
  7. package/locales/ca/LC_MESSAGES/volto.po +31 -5
  8. package/locales/ca.json +1 -1
  9. package/locales/de/LC_MESSAGES/volto.po +31 -5
  10. package/locales/de.json +1 -1
  11. package/locales/en/LC_MESSAGES/volto.po +30 -4
  12. package/locales/en.json +1 -1
  13. package/locales/es/LC_MESSAGES/volto.po +31 -5
  14. package/locales/es.json +1 -1
  15. package/locales/eu/LC_MESSAGES/volto.po +30 -4
  16. package/locales/eu.json +1 -1
  17. package/locales/fi/LC_MESSAGES/volto.po +30 -4
  18. package/locales/fi.json +1 -1
  19. package/locales/fr/LC_MESSAGES/volto.po +31 -5
  20. package/locales/fr.json +1 -1
  21. package/locales/it/LC_MESSAGES/volto.po +30 -4
  22. package/locales/it.json +1 -1
  23. package/locales/ja/LC_MESSAGES/volto.po +30 -4
  24. package/locales/ja.json +1 -1
  25. package/locales/nl/LC_MESSAGES/volto.po +31 -5
  26. package/locales/nl.json +1 -1
  27. package/locales/pt/LC_MESSAGES/volto.po +31 -5
  28. package/locales/pt.json +1 -1
  29. package/locales/pt_BR/LC_MESSAGES/volto.po +30 -4
  30. package/locales/pt_BR.json +1 -1
  31. package/locales/ro/LC_MESSAGES/volto.po +31 -5
  32. package/locales/ro.json +1 -1
  33. package/locales/volto.pot +30 -4
  34. package/locales/zh_CN/LC_MESSAGES/volto.po +31 -5
  35. package/locales/zh_CN.json +1 -1
  36. package/package.json +2 -2
  37. package/packages/volto-slate/package.json +1 -1
  38. package/packages/volto-slate/src/editor/plugins/Link/render.jsx +5 -6
  39. package/src/components/manage/Blocks/Listing/ListingData.jsx +3 -3
  40. package/src/components/manage/Blocks/Listing/ListingData.test.jsx +2 -0
  41. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +1 -1
  42. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +42 -25
  43. package/src/components/manage/Blocks/ToC/View.jsx +1 -0
  44. package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +65 -38
  45. package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +95 -5
  46. package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +116 -103
  47. package/src/components/manage/Sharing/Sharing.jsx +39 -16
  48. package/src/components/manage/Widgets/QueryWidget.jsx +6 -2
  49. package/src/helpers/MessageLabels/MessageLabels.js +26 -4
  50. package/src/reducers/navigation/navigation.js +5 -5
  51. package/src/reducers/navigation/navigation.test.js +30 -0
  52. package/styles/Microsoft/AMPM.yml +9 -0
  53. package/styles/Microsoft/Accessibility.yml +25 -0
  54. package/styles/Microsoft/Acronyms.yml +64 -0
  55. package/styles/Microsoft/Adverbs.yml +270 -0
  56. package/styles/Microsoft/Auto.yml +11 -0
  57. package/styles/Microsoft/Avoid.yml +14 -0
  58. package/styles/Microsoft/ComplexWords.yml +120 -0
  59. package/styles/Microsoft/Contractions.yml +50 -0
  60. package/styles/Microsoft/Dashes.yml +13 -0
  61. package/styles/Microsoft/DateFormat.yml +8 -0
  62. package/styles/Microsoft/DateNumbers.yml +40 -0
  63. package/styles/Microsoft/DateOrder.yml +8 -0
  64. package/styles/Microsoft/Ellipses.yml +9 -0
  65. package/styles/Microsoft/FirstPerson.yml +16 -0
  66. package/styles/Microsoft/Foreign.yml +13 -0
  67. package/styles/Microsoft/Gender.yml +8 -0
  68. package/styles/Microsoft/GenderBias.yml +44 -0
  69. package/styles/Microsoft/GeneralURL.yml +11 -0
  70. package/styles/Microsoft/HeadingAcronyms.yml +7 -0
  71. package/styles/Microsoft/HeadingColons.yml +8 -0
  72. package/styles/Microsoft/HeadingPunctuation.yml +13 -0
  73. package/styles/Microsoft/Headings.yml +28 -0
  74. package/styles/Microsoft/Hyphens.yml +14 -0
  75. package/styles/Microsoft/Negative.yml +13 -0
  76. package/styles/Microsoft/Ordinal.yml +13 -0
  77. package/styles/Microsoft/OxfordComma.yml +8 -0
  78. package/styles/Microsoft/Passive.yml +183 -0
  79. package/styles/Microsoft/Percentages.yml +7 -0
  80. package/styles/Microsoft/Quotes.yml +7 -0
  81. package/styles/Microsoft/RangeFormat.yml +13 -0
  82. package/styles/Microsoft/RangeTime.yml +13 -0
  83. package/styles/Microsoft/Ranges.yml +7 -0
  84. package/styles/Microsoft/Semicolon.yml +8 -0
  85. package/styles/Microsoft/SentenceLength.yml +7 -0
  86. package/styles/Microsoft/Spacing.yml +8 -0
  87. package/styles/Microsoft/Suspended.yml +7 -0
  88. package/styles/Microsoft/Terms.yml +41 -0
  89. package/styles/Microsoft/URLFormat.yml +10 -0
  90. package/styles/Microsoft/Units.yml +16 -0
  91. package/styles/Microsoft/Vocab.yml +25 -0
  92. package/styles/Microsoft/We.yml +11 -0
  93. package/styles/Microsoft/Wordiness.yml +122 -0
  94. package/styles/Microsoft/meta.json +4 -0
  95. package/theme/themes/pastanaga/elements/input.overrides +5 -1
  96. package/theme/themes/pastanaga/extras/login.less +2 -0
@@ -7,8 +7,14 @@ import React, { Component } from 'react';
7
7
  import { FormattedMessage, injectIntl } from 'react-intl';
8
8
  import { Dropdown, Table, Checkbox } from 'semantic-ui-react';
9
9
  import trashSVG from '@plone/volto/icons/delete.svg';
10
- import { Icon } from '@plone/volto/components';
10
+ import editSVG from '@plone/volto/icons/editing.svg';
11
+ import { Icon, ModalForm, Toast } from '@plone/volto/components';
12
+ import { updateUser } from '@plone/volto/actions';
11
13
  import ploneSVG from '@plone/volto/icons/plone.svg';
14
+ import { compose } from 'redux';
15
+ import { connect } from 'react-redux';
16
+ import { messages } from '@plone/volto/helpers';
17
+ import { toast } from 'react-toastify';
12
18
 
13
19
  /**
14
20
  * UsersControlpanelUser class.
@@ -43,9 +49,14 @@ class RenderUsers extends Component {
43
49
  */
44
50
  constructor(props) {
45
51
  super(props);
46
- this.state = {};
52
+ this.state = {
53
+ user: {},
54
+ };
47
55
  this.onChange = this.onChange.bind(this);
56
+ this.onEditUserError = this.onEditUserError.bind(this);
57
+ this.onEditUserSubmit = this.onEditUserSubmit.bind(this);
48
58
  }
59
+
49
60
  /**
50
61
  * @param {*} event
51
62
  * @param {*} { value }
@@ -56,6 +67,49 @@ class RenderUsers extends Component {
56
67
  const [user, role] = value.split('&role=');
57
68
  this.props.updateUser(user, role);
58
69
  }
70
+
71
+ componentDidUpdate(prevProps) {
72
+ if (
73
+ prevProps.updateRequest.loading &&
74
+ this.props.updateRequest.loaded &&
75
+ this.state?.user?.id === this.props?.user?.id
76
+ ) {
77
+ this.setState({ user: {} });
78
+ this.props.listUsers();
79
+ return toast.success(
80
+ <Toast
81
+ success
82
+ title={this.props.intl.formatMessage(messages.success)}
83
+ content={this.props.intl.formatMessage(messages.updateUserSuccess)}
84
+ />,
85
+ );
86
+ }
87
+ }
88
+
89
+ onEditUserSubmit(data, callback) {
90
+ // Do not handle groups and roles in this form
91
+ delete data.groups;
92
+ delete data.roles;
93
+ this.props.updateUserData(data.id, data);
94
+ }
95
+
96
+ onEditUserError() {
97
+ return toast.error(
98
+ <Toast
99
+ error
100
+ title={this.props.intl.formatMessage(messages.error)}
101
+ content={this.props.intl.formatMessage(
102
+ messages.addUserFormPasswordAndSendPasswordTogetherNotAllowed,
103
+ )}
104
+ />,
105
+ );
106
+ }
107
+
108
+ onClickEdit(props) {
109
+ const { formData } = props;
110
+ this.setState({ user: { ...formData } });
111
+ }
112
+
59
113
  /**
60
114
  * Render method.
61
115
  * @method render
@@ -67,7 +121,8 @@ class RenderUsers extends Component {
67
121
  <Table.Cell className="fullname">
68
122
  {this.props.user.fullname
69
123
  ? this.props.user.fullname
70
- : this.props.user.username}
124
+ : this.props.user.username}{' '}
125
+ ({this.props.user.username})
71
126
  </Table.Cell>
72
127
  {this.props.roles.map((role) => (
73
128
  <Table.Cell key={role.id}>
@@ -91,7 +146,20 @@ class RenderUsers extends Component {
91
146
  <Table.Cell textAlign="right">
92
147
  <Dropdown icon="ellipsis horizontal">
93
148
  <Dropdown.Menu className="left">
149
+ {this.props.userschema && (
150
+ <Dropdown.Item
151
+ id="edit-user-button"
152
+ onClick={() => {
153
+ this.onClickEdit({ formData: this.props.user });
154
+ }}
155
+ value={this.props.user['@id']}
156
+ >
157
+ <Icon name={editSVG} size="15px" />
158
+ <FormattedMessage id="Edit" defaultMessage="Edit" />
159
+ </Dropdown.Item>
160
+ )}
94
161
  <Dropdown.Item
162
+ id="delete-user-button"
95
163
  onClick={this.props.onDelete}
96
164
  value={this.props.user['@id']}
97
165
  >
@@ -101,9 +169,31 @@ class RenderUsers extends Component {
101
169
  </Dropdown.Menu>
102
170
  </Dropdown>
103
171
  </Table.Cell>
172
+ {Object.keys(this.state.user).length > 0 &&
173
+ this.props.userschema.loaded && (
174
+ <ModalForm
175
+ className="modal"
176
+ onSubmit={this.onEditUserSubmit}
177
+ submitError={this.state.editUserError}
178
+ formData={this.state.user}
179
+ onCancel={() => this.setState({ user: {} })}
180
+ title={this.props.intl.formatMessage(
181
+ messages.updateUserFormTitle,
182
+ )}
183
+ loading={this.props.updateRequest.loading}
184
+ schema={this.props.userschema.userschema}
185
+ />
186
+ )}
104
187
  </Table.Row>
105
188
  );
106
189
  }
107
190
  }
108
-
109
- export default injectIntl(RenderUsers);
191
+ export default compose(
192
+ injectIntl,
193
+ connect(
194
+ (state, props) => ({
195
+ updateRequest: state.users?.update,
196
+ }),
197
+ { updateUserData: updateUser },
198
+ ),
199
+ )(RenderUsers);
@@ -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.props.listUsers({
185
- search: this.state.search,
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 (groups && groups.length > 0) this.addUserToGroup(data);
270
- this.props.createUser(data, sendPasswordReset);
271
- this.setState({
272
- addUserSetFormDataCallback: callback,
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={{ icon: 'search' }}
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
- <div className="table">
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
- </div>
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
  ),
@@ -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.props.getSharing(getBaseUrl(this.props.pathname), this.state.search);
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 name="sharing-component" />
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
- <Segment>
322
- <Form onSubmit={this.onSearch}>
323
- <Form.Field>
324
- <Input
325
- name="SearchableText"
326
- action={{ icon: 'search' }}
327
- placeholder={this.props.intl.formatMessage(
328
- messages.searchForUserOrGroup,
329
- )}
330
- onChange={this.onChangeSearch}
331
- />
332
- </Form.Field>
333
- </Form>
334
- </Segment>
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"
@@ -314,7 +314,8 @@ export class QuerystringWidgetComponent extends Component {
314
314
  label: field[1].title,
315
315
  value: field[0],
316
316
  isDisabled: (value || []).some(
317
- (v) => v['i'] === field[0],
317
+ (v) =>
318
+ v['i'] !== 'path' && v['i'] === field[0],
318
319
  ),
319
320
  }),
320
321
  ),
@@ -444,8 +445,11 @@ export class QuerystringWidgetComponent extends Component {
444
445
  (field) => ({
445
446
  label: field[1].title,
446
447
  value: field[0],
448
+ // disable selecting indexes that are already used,
449
+ // except for path, which has explicit support
450
+ // in the backend for multipath queries
447
451
  isDisabled: (value || []).some(
448
- (v) => v['i'] === field[0],
452
+ (v) => v['i'] !== 'path' && v['i'] === field[0],
449
453
  ),
450
454
  }),
451
455
  ),
@@ -172,6 +172,28 @@ export const messages = defineMessages({
172
172
  id: 'Roles',
173
173
  defaultMessage: 'Roles',
174
174
  },
175
+ addUserFormPasswordAndSendPasswordTogetherNotAllowed: {
176
+ id:
177
+ 'It is not allowed to define both the password and to request sending the password reset message by e-mail. You need to select one of them.',
178
+ defaultMessage:
179
+ 'It is not allowed to define both the password and to request sending the password reset message by e-mail. You need to select one of them.',
180
+ },
181
+ userSearchNoResults: {
182
+ id: 'There are no users with the searched criteria',
183
+ defaultMessage: 'There are no users with the searched criteria',
184
+ },
185
+ groupSearchNoResults: {
186
+ id: 'There are no groups with the searched criteria',
187
+ defaultMessage: 'There are no groups with the searched criteria',
188
+ },
189
+ updateUserFormTitle: {
190
+ id: 'Update User',
191
+ defaultMessage: 'Update User',
192
+ },
193
+ updateUserSuccess: {
194
+ id: 'User updated successfuly',
195
+ defaultMessage: 'User updated successfuly',
196
+ },
175
197
  updateRoles: {
176
198
  id: 'User roles updated',
177
199
  defaultMessage: 'User roles updated',
@@ -234,19 +256,19 @@ export const messages = defineMessages({
234
256
  },
235
257
  copyBlocks: {
236
258
  id: 'Copy blocks',
237
- defaultMesages: 'Copy blocks',
259
+ defaultMessage: 'Copy blocks',
238
260
  },
239
261
  cutBlocks: {
240
262
  id: 'Cut blocks',
241
- defaultMesages: 'Cut blocks',
263
+ defaultMessage: 'Cut blocks',
242
264
  },
243
265
  pasteBlocks: {
244
266
  id: 'Paste blocks',
245
- defaultMesages: 'Paste blocks',
267
+ defaultMessage: 'Paste blocks',
246
268
  },
247
269
  deleteBlocks: {
248
270
  id: 'Delete blocks',
249
- defaultMesages: 'Delete blocks',
271
+ defaultMessage: 'Delete blocks',
250
272
  },
251
273
  showAllUserButton: {
252
274
  id: 'Show All',
@@ -73,11 +73,11 @@ export default function navigation(state = initialState, action = {}) {
73
73
  }
74
74
  return state;
75
75
  case `${GET_NAVIGATION}_SUCCESS`:
76
- hasExpander = hasApiExpander(
77
- 'navigation',
78
- getBaseUrl(flattenToAppURL(action.result['@id'])),
79
- );
80
- if (!hasExpander) {
76
+ // Even if the expander is set or not, if the GET_NAVIGATION is
77
+ // called, we want it to store the data if the actions data is
78
+ // not set in the expander data (['@components']) but in the "normal"
79
+ // action result (we look for the object property returned by the endpoint)
80
+ if (!action.result?.['@components'] && action.result?.items) {
81
81
  return {
82
82
  ...state,
83
83
  error: null,
@@ -172,4 +172,34 @@ describe('Navigation reducer (NAVIGATION)GET_CONTENT', () => {
172
172
  loading: false,
173
173
  });
174
174
  });
175
+
176
+ it('should handle (NAVIGATION)GET_NAVIGATION_SUCCESS (standalone with apiExpander enabled)', () => {
177
+ expect(
178
+ navigation(undefined, {
179
+ type: `${GET_NAVIGATION}_SUCCESS`,
180
+ result: {
181
+ items: [
182
+ {
183
+ title: 'Welcome to Plone!',
184
+ description:
185
+ 'Congratulations! You have successfully installed Plone.',
186
+ '@id': `${settings.apiPath}/front-page`,
187
+ },
188
+ ],
189
+ },
190
+ }),
191
+ ).toEqual({
192
+ error: null,
193
+ items: [
194
+ {
195
+ title: 'Welcome to Plone!',
196
+ description:
197
+ 'Congratulations! You have successfully installed Plone.',
198
+ url: '/front-page',
199
+ },
200
+ ],
201
+ loaded: true,
202
+ loading: false,
203
+ });
204
+ });
175
205
  });
@@ -0,0 +1,9 @@
1
+ extends: existence
2
+ message: Use 'AM' or 'PM' (preceded by a space).
3
+ link: https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/date-time-terms
4
+ level: error
5
+ nonword: true
6
+ tokens:
7
+ - '\d{1,2}[AP]M'
8
+ - '\d{1,2} ?[ap]m'
9
+ - '\d{1,2} ?[aApP]\.[mM]\.'