@eeacms/volto-eea-website-theme 1.34.0 → 2.0.1

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 (54) hide show
  1. package/.eslintrc.js +7 -2
  2. package/CHANGELOG.md +53 -1
  3. package/docker-compose.yml +1 -1
  4. package/jest-addon.config.js +3 -0
  5. package/package.json +2 -1
  6. package/src/components/manage/Blocks/LayoutSettings/index.js +3 -1
  7. package/src/components/manage/Blocks/Title/index.js +3 -1
  8. package/src/components/manage/Blocks/Title/schema.js +3 -1
  9. package/src/components/theme/Banner/View.jsx +12 -5
  10. package/src/components/theme/DraftBackground/DraftBackground.jsx +30 -21
  11. package/src/components/theme/DraftBackground/DraftBackground.test.jsx +85 -0
  12. package/src/config.js +2 -0
  13. package/src/customizations/@plone/volto-slate/blocks/Text/TextBlockView.jsx +32 -0
  14. package/src/customizations/@plone/volto-slate/editor/render.jsx +75 -0
  15. package/src/customizations/@plone/volto-slate/elementEditor/utils.js +76 -75
  16. package/src/customizations/volto/components/manage/Blocks/Grid/Edit.jsx +70 -0
  17. package/src/customizations/volto/components/manage/Blocks/Grid/View.jsx +61 -0
  18. package/src/customizations/volto/components/manage/Blocks/Grid/readme.md +1 -0
  19. package/src/customizations/volto/components/manage/Blocks/Image/Edit.jsx +82 -23
  20. package/src/customizations/volto/components/manage/Blocks/Image/Edit.test.jsx +10 -3
  21. package/src/customizations/volto/components/manage/Blocks/Image/View.jsx +110 -111
  22. package/src/customizations/volto/components/manage/Blocks/Image/schema.js +17 -2
  23. package/src/customizations/volto/components/manage/Blocks/LeadImage/Edit.jsx +35 -14
  24. package/src/customizations/volto/components/manage/Blocks/LeadImage/View.jsx +65 -79
  25. package/src/customizations/volto/components/manage/Display/Display.jsx +306 -0
  26. package/src/customizations/volto/components/manage/Display/Readme.md +1 -0
  27. package/src/customizations/volto/components/manage/Sidebar/SidebarPopup copy.jsx +82 -0
  28. package/src/customizations/volto/components/manage/Toolbar/More.jsx +541 -0
  29. package/src/customizations/volto/components/manage/UniversalLink/UniversalLink.jsx +3 -1
  30. package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.jsx +24 -14
  31. package/src/customizations/volto/components/manage/Widgets/README.md +1 -0
  32. package/src/customizations/volto/components/manage/Workflow/README.txt +1 -0
  33. package/src/customizations/volto/components/manage/Workflow/Workflow.jsx +324 -0
  34. package/src/customizations/volto/components/manage/Workflow/Workflow.test.jsx +81 -0
  35. package/src/customizations/volto/components/theme/Comments/Comments.jsx +1 -2
  36. package/src/customizations/volto/components/theme/ContactForm/ContactForm.jsx +1 -1
  37. package/src/customizations/volto/components/theme/EventDetails/EventDetails.jsx +1 -0
  38. package/src/index.js +21 -16
  39. package/src/middleware/ok.js +4 -2
  40. package/src/middleware/voltoCustom.js +4 -2
  41. package/src/slate.js +10 -8
  42. package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/README.txt +0 -1
  43. package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/StyleMenu.jsx +0 -157
  44. package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/utils.js +0 -168
  45. package/src/customizations/volto/components/manage/Add/Add.jsx +0 -498
  46. package/src/customizations/volto/components/manage/Add/readme.md +0 -1
  47. package/src/customizations/volto/components/manage/Contents/ContentsPropertiesModal.jsx +0 -232
  48. package/src/customizations/volto/components/manage/Form/Form.jsx +0 -810
  49. package/src/customizations/volto/components/manage/Form/Form.test.jsx +0 -1124
  50. package/src/customizations/volto/components/manage/Form/ModalForm.jsx +0 -326
  51. package/src/customizations/volto/components/manage/Sharing/Sharing.jsx +0 -528
  52. package/src/customizations/volto/components/manage/Sharing/Sharing.test.jsx +0 -72
  53. package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.test.jsx +0 -193
  54. package/src/customizations/volto/components/theme/AppExtras/AppExtras.jsx +0 -27
@@ -0,0 +1,541 @@
1
+ /**
2
+ * More component.
3
+ * @module components/manage/Toolbar/More
4
+ */
5
+
6
+ import React, { Component } from 'react';
7
+ import { defineMessages, injectIntl } from 'react-intl';
8
+ import PropTypes from 'prop-types';
9
+ import { connect } from 'react-redux';
10
+ import { compose } from 'redux';
11
+ import { Link, withRouter } from 'react-router-dom';
12
+ import { find } from 'lodash';
13
+ import { toast } from 'react-toastify';
14
+ import { Toast } from '@plone/volto/components';
15
+ import { Pluggable, Plug } from '@plone/volto/components/manage/Pluggable';
16
+ import {
17
+ FormattedDate,
18
+ Icon,
19
+ Display,
20
+ Workflow,
21
+ } from '@plone/volto/components';
22
+ import {
23
+ applyWorkingCopy,
24
+ createWorkingCopy,
25
+ removeWorkingCopy,
26
+ } from '@plone/volto/actions';
27
+ import { flattenToAppURL, getBaseUrl } from '@plone/volto/helpers';
28
+ import config from '@plone/volto/registry';
29
+
30
+ import rightArrowSVG from '@plone/volto/icons/right-key.svg';
31
+ import userSVG from '@plone/volto/icons/user.svg';
32
+ import applySVG from '@plone/volto/icons/ready.svg';
33
+ import removeSVG from '@plone/volto/icons/circle-dismiss.svg';
34
+
35
+ const messages = defineMessages({
36
+ personalTools: {
37
+ id: 'Personal tools',
38
+ defaultMessage: 'Personal tools',
39
+ },
40
+ history: {
41
+ id: 'History',
42
+ defaultMessage: 'History',
43
+ },
44
+ sharing: {
45
+ id: 'Sharing',
46
+ defaultMessage: 'Sharing',
47
+ },
48
+ rules: {
49
+ id: 'Rules',
50
+ defaultMessage: 'Rules',
51
+ },
52
+ aliases: {
53
+ id: 'URL Management',
54
+ defaultMessage: 'URL Management',
55
+ },
56
+ ManageTranslations: {
57
+ id: 'Manage Translations',
58
+ defaultMessage: 'Manage Translations',
59
+ },
60
+ manageContent: {
61
+ id: 'Manage content…',
62
+ defaultMessage: 'Manage content…',
63
+ },
64
+ CreateWorkingCopy: {
65
+ id: 'Create working copy',
66
+ defaultMessage: 'Create working copy',
67
+ },
68
+ applyWorkingCopy: {
69
+ id: 'Apply working copy',
70
+ defaultMessage: 'Apply working copy',
71
+ },
72
+ removeWorkingCopy: {
73
+ id: 'Remove working copy',
74
+ defaultMessage: 'Remove working copy',
75
+ },
76
+ viewWorkingCopy: {
77
+ id: 'View working copy',
78
+ defaultMessage: 'View working copy',
79
+ },
80
+ workingAppliedTitle: {
81
+ id: 'Changes applied.',
82
+ defaultMessage: 'Changes applied',
83
+ },
84
+ workingCopyAppliedBy: {
85
+ id: 'Made by {creator} on {date}. This is not a working copy anymore, but the main content.',
86
+ defaultMessage:
87
+ 'Made by {creator} on {date}. This is not a working copy anymore, but the main content.',
88
+ },
89
+ workingCopyRemovedTitle: {
90
+ id: 'The working copy was discarded',
91
+ defaultMessage: 'The working copy was discarded',
92
+ },
93
+ Unauthorized: {
94
+ id: 'Unauthorized',
95
+ defaultMessage: 'Unauthorized',
96
+ },
97
+ workingCopyErrorUnauthorized: {
98
+ id: 'workingCopyErrorUnauthorized',
99
+ defaultMessage: 'You are not authorized to perform this operation.',
100
+ },
101
+ Error: {
102
+ id: 'Error',
103
+ defaultMessage: 'Error',
104
+ },
105
+ workingCopyGenericError: {
106
+ id: 'workingCopyGenericError',
107
+ defaultMessage: 'An error occurred while performing this operation.',
108
+ },
109
+ });
110
+
111
+ /**
112
+ * More container class.
113
+ * @class More
114
+ * @extends Component
115
+ */
116
+ class More extends Component {
117
+ static propTypes = {
118
+ actions: PropTypes.shape({
119
+ object: PropTypes.arrayOf(PropTypes.object),
120
+ object_buttons: PropTypes.arrayOf(PropTypes.object),
121
+ user: PropTypes.arrayOf(PropTypes.object),
122
+ }),
123
+ pathname: PropTypes.string.isRequired,
124
+ content: PropTypes.shape({
125
+ title: PropTypes.string,
126
+ '@type': PropTypes.string,
127
+ is_folderish: PropTypes.bool,
128
+ review_state: PropTypes.string,
129
+ }),
130
+ loadComponent: PropTypes.func.isRequired,
131
+ closeMenu: PropTypes.func.isRequired,
132
+ };
133
+
134
+ /**
135
+ * Default properties.
136
+ * @property {Object} defaultProps Default properties.
137
+ * @static
138
+ */
139
+ static defaultProps = {
140
+ actions: null,
141
+ content: null,
142
+ };
143
+ state = {
144
+ openManageTranslations: false,
145
+ pushed: false,
146
+ };
147
+
148
+ push = (selector) => {
149
+ this.setState(() => ({
150
+ pushed: true,
151
+ }));
152
+ this.props.loadComponent(selector);
153
+ document.removeEventListener('mousedown', this.handleClickOutside, false);
154
+ };
155
+
156
+ componentDidUpdate(prevProps, prevState) {
157
+ let erroredAction = '';
158
+ if (
159
+ prevProps.workingCopy.apply.loading &&
160
+ this.props.workingCopy.apply.error
161
+ ) {
162
+ erroredAction = 'apply';
163
+ } else if (
164
+ prevProps.workingCopy.create.loading &&
165
+ this.props.workingCopy.create.error
166
+ ) {
167
+ erroredAction = 'create';
168
+ } else if (
169
+ prevProps.workingCopy.remove.loading &&
170
+ this.props.workingCopy.remove.error
171
+ ) {
172
+ erroredAction = 'remove';
173
+ }
174
+
175
+ if (erroredAction) {
176
+ const errorStatus = this.props.workingCopy[erroredAction].error.status;
177
+ if (errorStatus === 401 || errorStatus === 403) {
178
+ toast.error(
179
+ <Toast
180
+ error
181
+ title={this.props.intl.formatMessage(messages.Unauthorized)}
182
+ content={this.props.intl.formatMessage(
183
+ messages.workingCopyErrorUnauthorized,
184
+ )}
185
+ />,
186
+ {
187
+ toastId: 'workingCopyErrorUnauthorized',
188
+ autoClose: 10000,
189
+ },
190
+ );
191
+ } else {
192
+ toast.error(
193
+ <Toast
194
+ error
195
+ title={this.props.intl.formatMessage(messages.Error)}
196
+ content={this.props.intl.formatMessage(
197
+ messages.workingCopyGenericError,
198
+ )}
199
+ />,
200
+ {
201
+ toastId: 'workingCopyGenericError',
202
+ autoClose: 10000,
203
+ },
204
+ );
205
+ }
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Render method.
211
+ * @method render
212
+ * @returns {string} Markup for the component.
213
+ */
214
+ render() {
215
+ const path = getBaseUrl(this.props.pathname);
216
+ const editAction = find(this.props.actions.object, { id: 'edit' });
217
+ const historyAction = find(this.props.actions.object, { id: 'history' });
218
+ const sharingAction = find(this.props.actions.object, {
219
+ id: 'local_roles',
220
+ });
221
+
222
+ const rulesAction = find(this.props.actions.object, {
223
+ id: 'contentrules',
224
+ });
225
+
226
+ const aliasesAction = find(this.props.actions.object_buttons, {
227
+ id: 'redirection',
228
+ });
229
+ const { content, intl } = this.props;
230
+
231
+ const dateOptions = {
232
+ year: 'numeric',
233
+ month: 'long',
234
+ day: 'numeric',
235
+ };
236
+
237
+ return (
238
+ <div
239
+ className="menu-more pastanaga-menu"
240
+ style={{
241
+ flex: this.props.theToolbar.current
242
+ ? `0 0 ${
243
+ this.props.theToolbar.current.getBoundingClientRect().width
244
+ }px`
245
+ : null,
246
+ }}
247
+ >
248
+ <header>
249
+ <h2>{this.props.content.title}</h2>
250
+ <button
251
+ className="more-user"
252
+ aria-label={this.props.intl.formatMessage(messages.personalTools)}
253
+ onClick={() => this.push('personalTools')}
254
+ tabIndex={0}
255
+ >
256
+ <Icon
257
+ name={userSVG}
258
+ size="30px"
259
+ title={this.props.intl.formatMessage(messages.personalTools)}
260
+ />
261
+ </button>
262
+ </header>
263
+ <div className="pastanaga-menu-list">
264
+ <ul>
265
+ <Pluggable name="toolbar-more-menu-list" />
266
+ <Plug pluggable="toolbar-more-menu-list" id="state">
267
+ {this.props.content['@type'] !== 'Plone Site' && (
268
+ // Plone Site does not have workflow
269
+ <li className="state-select">
270
+ <Workflow pathname={path} />
271
+ </li>
272
+ )}
273
+ </Plug>
274
+ <Plug pluggable="toolbar-more-menu-list" id="view">
275
+ {this.props.content['@type'] !== 'Plone Site' && (
276
+ // Plone Site does not have view (yet)
277
+ <li className="display-select">
278
+ {editAction && <Display pathname={path} />}
279
+ </li>
280
+ )}
281
+ </Plug>
282
+ <Plug pluggable="toolbar-more-menu-list" id="history">
283
+ {this.props.content['@type'] !== 'Plone Site' && (
284
+ // Plone Site does not have history (yet)
285
+ <li>
286
+ <Link to={`${path}/historyview`}>
287
+ <div>
288
+ <span className="pastanaga-menu-label">
289
+ {historyAction?.title ||
290
+ this.props.intl.formatMessage(messages.history)}
291
+ </span>
292
+ <span className="pastanaga-menu-value" />
293
+ </div>
294
+ <Icon name={rightArrowSVG} size="24px" />
295
+ </Link>
296
+ </li>
297
+ )}
298
+ </Plug>
299
+ <Plug pluggable="toolbar-more-menu-list" id="sharing">
300
+ {sharingAction && (
301
+ <li>
302
+ <Link to={`${path}/sharing`}>
303
+ {this.props.intl.formatMessage(messages.sharing)}
304
+ <Icon name={rightArrowSVG} size="24px" />
305
+ </Link>
306
+ </li>
307
+ )}
308
+ </Plug>
309
+ <Plug pluggable="toolbar-more-menu-list" id="aliases">
310
+ {aliasesAction && (
311
+ <li>
312
+ <Link to={`${path}/aliases`}>
313
+ {this.props.intl.formatMessage(messages.aliases)}
314
+ <Icon name={rightArrowSVG} size="24px" />
315
+ </Link>
316
+ </li>
317
+ )}
318
+ </Plug>
319
+ <Plug pluggable="toolbar-more-menu-list" id="rules">
320
+ {rulesAction && (
321
+ <li>
322
+ <Link to={`${path}/rules`}>
323
+ {this.props.intl.formatMessage(messages.rules)}
324
+ <Icon name={rightArrowSVG} size="24px" />
325
+ </Link>
326
+ </li>
327
+ )}
328
+ </Plug>
329
+ </ul>
330
+ </div>
331
+ <Pluggable name="toolbar-more-manage-content">
332
+ {(pluggables) => (
333
+ <>
334
+ {pluggables.length > 0 && (
335
+ <>
336
+ <header>
337
+ <h2>
338
+ {this.props.intl.formatMessage(messages.manageContent)}
339
+ </h2>
340
+ </header>
341
+ <div className="pastanaga-menu-list">
342
+ <ul>
343
+ {pluggables.map((p) => (
344
+ <React.Fragment key={p.id}>{p()}</React.Fragment>
345
+ ))}
346
+ </ul>
347
+ </div>
348
+ </>
349
+ )}
350
+ </>
351
+ )}
352
+ </Pluggable>
353
+ {config.settings.hasWorkingCopySupport &&
354
+ this.props.content['@type'] !== 'Plone Site' &&
355
+ this.props.content['@type'] !== 'ims_indicator' && (
356
+ <>
357
+ {!this.props.content.working_copy && (
358
+ <Plug pluggable="toolbar-more-manage-content" id="workingcopy">
359
+ <li>
360
+ <button
361
+ aria-label={this.props.intl.formatMessage(
362
+ messages.CreateWorkingCopy,
363
+ )}
364
+ onClick={() => {
365
+ this.props.createWorkingCopy(path).then((response) => {
366
+ this.props.history.push(
367
+ flattenToAppURL(response['@id']),
368
+ );
369
+ this.props.closeMenu();
370
+ });
371
+ }}
372
+ >
373
+ {this.props.intl.formatMessage(
374
+ messages.CreateWorkingCopy,
375
+ )}
376
+
377
+ <Icon name={rightArrowSVG} size="24px" />
378
+ </button>
379
+ </li>
380
+ </Plug>
381
+ )}
382
+ {this.props.content.working_copy &&
383
+ this.props.content.working_copy_of && (
384
+ <Plug
385
+ pluggable="toolbar-more-manage-content"
386
+ id="workingcopy"
387
+ >
388
+ <li>
389
+ <button
390
+ aria-label={this.props.intl.formatMessage(
391
+ messages.applyWorkingCopy,
392
+ )}
393
+ onClick={() => {
394
+ this.props.applyWorkingCopy(path).then((response) => {
395
+ this.props.history.push(
396
+ flattenToAppURL(
397
+ this.props.content.working_copy_of['@id'],
398
+ ),
399
+ );
400
+ this.props.closeMenu();
401
+ toast.info(
402
+ <Toast
403
+ info
404
+ title={intl.formatMessage(
405
+ messages.workingAppliedTitle,
406
+ )}
407
+ content={intl.formatMessage(
408
+ messages.workingCopyAppliedBy,
409
+ {
410
+ creator: content.working_copy?.creator_name,
411
+ date: (
412
+ <FormattedDate
413
+ date={content.working_copy?.created}
414
+ format={dateOptions}
415
+ />
416
+ ),
417
+ },
418
+ )}
419
+ />,
420
+ {
421
+ toastId: 'workingcopyapplyinfo',
422
+ autoClose: 10000,
423
+ },
424
+ );
425
+ });
426
+ }}
427
+ >
428
+ {this.props.intl.formatMessage(
429
+ messages.applyWorkingCopy,
430
+ )}
431
+
432
+ <Icon
433
+ name={applySVG}
434
+ size="24px"
435
+ title={this.props.intl.formatMessage(
436
+ messages.applyWorkingCopy,
437
+ )}
438
+ />
439
+ </button>
440
+ </li>
441
+ <li>
442
+ <button
443
+ aria-label={this.props.intl.formatMessage(
444
+ messages.removeWorkingCopy,
445
+ )}
446
+ onClick={() => {
447
+ this.props
448
+ .removeWorkingCopy(path)
449
+ .then((response) => {
450
+ this.props.history.push(
451
+ flattenToAppURL(
452
+ this.props.content.working_copy_of['@id'],
453
+ ),
454
+ );
455
+ this.props.closeMenu();
456
+ toast.info(
457
+ <Toast
458
+ info
459
+ title={intl.formatMessage(
460
+ messages.workingCopyRemovedTitle,
461
+ )}
462
+ />,
463
+ {
464
+ toastId: 'workingcopyremovednotice',
465
+ autoClose: 10000,
466
+ },
467
+ );
468
+ });
469
+ }}
470
+ >
471
+ {this.props.intl.formatMessage(
472
+ messages.removeWorkingCopy,
473
+ )}
474
+
475
+ <Icon
476
+ name={removeSVG}
477
+ size="24px"
478
+ color="#e40166"
479
+ title={this.props.intl.formatMessage(
480
+ messages.removeWorkingCopy,
481
+ )}
482
+ />
483
+ </button>
484
+ </li>
485
+ </Plug>
486
+ )}
487
+ {this.props.content.working_copy &&
488
+ !this.props.content.working_copy_of && (
489
+ <Plug
490
+ pluggable="toolbar-more-manage-content"
491
+ id="workingcopy"
492
+ >
493
+ <li>
494
+ <Link
495
+ to={flattenToAppURL(
496
+ this.props.content.working_copy['@id'],
497
+ )}
498
+ onClick={() => this.props.closeMenu()}
499
+ >
500
+ {this.props.intl.formatMessage(
501
+ messages.viewWorkingCopy,
502
+ )}
503
+ <Icon name={rightArrowSVG} size="24px" />
504
+ </Link>
505
+ </li>
506
+ </Plug>
507
+ )}
508
+ </>
509
+ )}
510
+ {editAction &&
511
+ config.settings.isMultilingual &&
512
+ this.props.content['@type'] !== 'ims_indicator' && (
513
+ <Plug pluggable="toolbar-more-manage-content" id="multilingual">
514
+ <li>
515
+ <Link to={`${path}/manage-translations`}>
516
+ {this.props.intl.formatMessage(messages.ManageTranslations)}
517
+
518
+ <Icon name={rightArrowSVG} size="24px" />
519
+ </Link>
520
+ </li>
521
+ </Plug>
522
+ )}
523
+ </div>
524
+ );
525
+ }
526
+ }
527
+
528
+ export default compose(
529
+ injectIntl,
530
+ withRouter,
531
+ connect(
532
+ (state, props) => ({
533
+ actions: state.actions.actions,
534
+ pathname: props.pathname,
535
+ content: state.content.data,
536
+ lang: state.intl.locale,
537
+ workingCopy: state.workingCopy,
538
+ }),
539
+ { applyWorkingCopy, createWorkingCopy, removeWorkingCopy },
540
+ ),
541
+ )(More);
@@ -74,7 +74,9 @@ const UniversalLink = ({
74
74
 
75
75
  const checkedURL = URLUtils.checkAndNormalizeUrl(url);
76
76
 
77
- url = checkedURL.url;
77
+ // we can receive an item with a linkWithHash property set from ObjectBrowserWidget
78
+ // if so, we use that instead of the url prop
79
+ url = (item && item['linkWithHash']) || checkedURL.url;
78
80
  let tag = (
79
81
  <Link
80
82
  to={flattenToAppURL(url)}
@@ -105,7 +105,8 @@ export class ObjectBrowserWidgetComponent extends Component {
105
105
  this.placeholderRef = React.createRef();
106
106
  }
107
107
  renderLabel(item) {
108
- const href = item['@id'];
108
+ // show linkWithHash if available, otherwise @id
109
+ const href = item['linkWithHash'] || item['@id'];
109
110
  return (
110
111
  <Popup
111
112
  key={flattenToAppURL(href)}
@@ -163,6 +164,7 @@ export class ObjectBrowserWidgetComponent extends Component {
163
164
  }
164
165
  let exists = false;
165
166
  let index = -1;
167
+
166
168
  value.forEach((_item, _index) => {
167
169
  if (flattenToAppURL(_item['@id']) === flattenToAppURL(item['@id'])) {
168
170
  exists = true;
@@ -181,6 +183,7 @@ export class ObjectBrowserWidgetComponent extends Component {
181
183
  ...this.props.selectedItemAttrs,
182
184
  // Add the required attributes for the widget to work
183
185
  '@id',
186
+ 'linkWithHash', // add linkWithHash to the allowed attributes
184
187
  'title',
185
188
  ];
186
189
  resultantItem = Object.keys(item)
@@ -222,16 +225,28 @@ export class ObjectBrowserWidgetComponent extends Component {
222
225
  }
223
226
  };
224
227
 
228
+ /**
229
+ * Splits a URL into its link and hash components.
230
+ * @param {string} url - The URL to split.
231
+ * @returns {[string, string]} - An array containing the link and hash components of the URL.
232
+ */
233
+ getHashAndLinkFromUrl = (url) => {
234
+ return url.split('#');
235
+ };
236
+
225
237
  onSubmitManualLink = () => {
226
238
  if (this.validateManualLink(this.state.manualLinkInput)) {
227
239
  if (isInternalURL(this.state.manualLinkInput)) {
228
- const link = this.state.manualLinkInput;
240
+ const [link, hash] = this.getHashAndLinkFromUrl(
241
+ this.state.manualLinkInput,
242
+ );
243
+ const relative_link = flattenToAppURL(link);
229
244
  // convert it into an internal on if possible
230
245
  this.props
231
246
  .searchContent(
232
247
  '/',
233
248
  {
234
- 'path.query': flattenToAppURL(this.state.manualLinkInput),
249
+ 'path.query': relative_link,
235
250
  'path.depth': '0',
236
251
  sort_on: 'getObjPositionInParent',
237
252
  metadata_fields: '_all',
@@ -241,6 +256,10 @@ export class ObjectBrowserWidgetComponent extends Component {
241
256
  )
242
257
  .then((resp) => {
243
258
  if (resp.items?.length > 0) {
259
+ // if there is a hash within the url, add it to the item as linkWithHash
260
+ if (hash) {
261
+ resp.items[0]['linkWithHash'] = `${relative_link}#${hash}`;
262
+ }
244
263
  this.onChange(resp.items[0]);
245
264
  } else {
246
265
  this.props.onChange(this.props.id, [
@@ -312,15 +331,8 @@ export class ObjectBrowserWidgetComponent extends Component {
312
331
  * @returns {string} Markup for the component.
313
332
  */
314
333
  render() {
315
- const {
316
- id,
317
- description,
318
- fieldSet,
319
- value,
320
- mode,
321
- onChange,
322
- isDisabled,
323
- } = this.props;
334
+ const { id, description, fieldSet, value, mode, onChange, isDisabled } =
335
+ this.props;
324
336
 
325
337
  let items = compact(!isArray(value) && value ? [value] : value || []);
326
338
 
@@ -379,7 +391,6 @@ export class ObjectBrowserWidgetComponent extends Component {
379
391
  <Button.Group>
380
392
  <Button
381
393
  basic
382
- icon
383
394
  className="cancel"
384
395
  onClick={(e) => {
385
396
  e.stopPropagation();
@@ -390,7 +401,6 @@ export class ObjectBrowserWidgetComponent extends Component {
390
401
  </Button>
391
402
  <Button
392
403
  basic
393
- icon
394
404
  primary
395
405
  disabled={!this.state.validURL}
396
406
  onClick={(e) => {
@@ -0,0 +1 @@
1
+ Customized ObjectBrowserWidget to preserve anchor links in the manually pasted internal URL.
@@ -0,0 +1 @@
1
+ Workflow.jsx - copied from @plone/volto 16.22.2 - refs #256563