@eeacms/volto-eea-website-theme 1.34.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +7 -2
- package/CHANGELOG.md +44 -20
- package/docker-compose.yml +1 -1
- package/jest-addon.config.js +3 -0
- package/package.json +2 -1
- package/src/components/manage/Blocks/LayoutSettings/index.js +3 -1
- package/src/components/manage/Blocks/Title/index.js +3 -1
- package/src/components/manage/Blocks/Title/schema.js +3 -1
- package/src/components/theme/Banner/View.jsx +12 -5
- package/src/components/theme/DraftBackground/DraftBackground.jsx +1 -0
- package/src/components/theme/DraftBackground/DraftBackground.test.jsx +85 -0
- package/src/config.js +2 -0
- package/src/customizations/@plone/volto-slate/blocks/Text/TextBlockView.jsx +32 -0
- package/src/customizations/@plone/volto-slate/editor/render.jsx +75 -0
- package/src/customizations/@plone/volto-slate/elementEditor/utils.js +76 -75
- package/src/customizations/volto/components/manage/Blocks/Grid/Edit.jsx +70 -0
- package/src/customizations/volto/components/manage/Blocks/Grid/View.jsx +61 -0
- package/src/customizations/volto/components/manage/Blocks/Grid/readme.md +1 -0
- package/src/customizations/volto/components/manage/Blocks/Image/Edit.jsx +82 -23
- package/src/customizations/volto/components/manage/Blocks/Image/Edit.test.jsx +10 -3
- package/src/customizations/volto/components/manage/Blocks/Image/View.jsx +110 -111
- package/src/customizations/volto/components/manage/Blocks/Image/schema.js +17 -2
- package/src/customizations/volto/components/manage/Blocks/LeadImage/Edit.jsx +35 -14
- package/src/customizations/volto/components/manage/Blocks/LeadImage/View.jsx +65 -79
- package/src/customizations/volto/components/manage/Display/Display.jsx +306 -0
- package/src/customizations/volto/components/manage/Display/Readme.md +1 -0
- package/src/customizations/volto/components/manage/Sidebar/SidebarPopup copy.jsx +82 -0
- package/src/customizations/volto/components/manage/Toolbar/More.jsx +541 -0
- package/src/customizations/volto/components/manage/UniversalLink/UniversalLink.jsx +3 -1
- package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.jsx +24 -14
- package/src/customizations/volto/components/manage/Widgets/README.md +1 -0
- package/src/customizations/volto/components/manage/Workflow/README.txt +1 -0
- package/src/customizations/volto/components/manage/Workflow/Workflow.jsx +324 -0
- package/src/customizations/volto/components/manage/Workflow/Workflow.test.jsx +81 -0
- package/src/customizations/volto/components/theme/Comments/Comments.jsx +1 -2
- package/src/customizations/volto/components/theme/ContactForm/ContactForm.jsx +1 -1
- package/src/customizations/volto/components/theme/EventDetails/EventDetails.jsx +1 -0
- package/src/index.js +21 -16
- package/src/middleware/ok.js +4 -2
- package/src/middleware/voltoCustom.js +4 -2
- package/src/slate.js +10 -8
- package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/README.txt +0 -1
- package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/StyleMenu.jsx +0 -157
- package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/utils.js +0 -168
- package/src/customizations/volto/components/manage/Add/Add.jsx +0 -498
- package/src/customizations/volto/components/manage/Add/readme.md +0 -1
- package/src/customizations/volto/components/manage/Contents/ContentsPropertiesModal.jsx +0 -232
- package/src/customizations/volto/components/manage/Form/Form.jsx +0 -810
- package/src/customizations/volto/components/manage/Form/Form.test.jsx +0 -1124
- package/src/customizations/volto/components/manage/Form/ModalForm.jsx +0 -326
- package/src/customizations/volto/components/manage/Sharing/Sharing.jsx +0 -528
- package/src/customizations/volto/components/manage/Sharing/Sharing.test.jsx +0 -72
- package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.test.jsx +0 -193
- 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
|
-
|
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
|
-
|
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.
|
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':
|
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
|
-
|
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
|