@plone/volto 14.2.0 → 14.3.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/CHANGELOG.md +37 -0
- package/README.md +25 -4
- package/package.json +4 -4
- package/src/components/manage/Contents/Contents.jsx +225 -220
- package/src/components/manage/Widgets/DatetimeWidget.jsx +2 -2
- package/src/components/theme/Anontools/Anontools.jsx +1 -1
- package/src/components/theme/Navigation/NavItem.jsx +3 -1
- package/src/express-middleware/files.js +2 -3
- package/src/express-middleware/images.js +2 -4
- package/src/helpers/Api/APIResourceWithAuth.js +1 -7
- package/src/helpers/FormValidation/FormValidation.js +1 -1
- package/src/server.jsx +28 -0
- package/theme/themes/pastanaga/extras/contents.less +12 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 14.3.0 (2022-01-20)
|
|
4
|
+
|
|
5
|
+
### Feature
|
|
6
|
+
|
|
7
|
+
- Bump semantic-ui-react to v2.0.3 @nileshgulia1
|
|
8
|
+
|
|
9
|
+
## 14.2.3 (2022-01-20)
|
|
10
|
+
|
|
11
|
+
### Bugfix
|
|
12
|
+
|
|
13
|
+
- Fix overflow table in Content view @giuliaghisini
|
|
14
|
+
- Fixed url validation in FormValidation to admit ip addresses. @giuliaghisini
|
|
15
|
+
|
|
16
|
+
## 14.2.2 (2022-01-13)
|
|
17
|
+
|
|
18
|
+
### Bugfix
|
|
19
|
+
|
|
20
|
+
- Fix home URL item in Navigation, which was evaluating as non-internal @sneridagh
|
|
21
|
+
- Improve the request handling in `getAPIResourceWithAuth` and in `Api` helper. This fixes the "Cannot set headers once the content has being sent" @sneridagh
|
|
22
|
+
- Fix when you remove the time from DatetimeWidget @iRohitSingh
|
|
23
|
+
|
|
24
|
+
### Internal
|
|
25
|
+
|
|
26
|
+
- Fix URL for Climate-Energy, a Volto website @tiberiuichim
|
|
27
|
+
- Fix quirky Cypress test in "DX control panel schema" (see https://github.com/plone/volto/runs/4803206906?check_suite_focus=true) @sneridagh
|
|
28
|
+
|
|
29
|
+
## 14.2.1 (2022-01-12)
|
|
30
|
+
|
|
31
|
+
### Bugfix
|
|
32
|
+
|
|
33
|
+
- Fix home URL item in Navigation, which was evaluating as non-internal
|
|
34
|
+
|
|
35
|
+
### Internal
|
|
36
|
+
|
|
37
|
+
- Use plone-backend docker images for Cypress tests @sneridagh
|
|
38
|
+
- Upgrade `query-string` library so it supports Plone `:list` qs marker @sneridagh
|
|
39
|
+
|
|
3
40
|
## 14.2.0 (2022-01-04)
|
|
4
41
|
|
|
5
42
|
### Feature
|
package/README.md
CHANGED
|
@@ -76,20 +76,41 @@ follow the prompts questions, provide `myvoltoproject` as project name then, whe
|
|
|
76
76
|
|
|
77
77
|
We recommend Plone as backend of choice for Volto.
|
|
78
78
|
|
|
79
|
-
You can bootstrap a ready Docker Plone container with all the dependencies and ready for Volto use:
|
|
79
|
+
You can bootstrap a ready Docker Plone container with all the dependencies and ready for Volto use. We recommend to use the Plone docker builds based in `pip` [plone/plone-backend](https://github.com/plone/plone-backend) image:
|
|
80
80
|
|
|
81
81
|
```shell
|
|
82
|
-
docker run -it --rm --name=plone -p 8080:8080 -e SITE=Plone -e
|
|
82
|
+
docker run -it --rm --name=plone -p 8080:8080 -e SITE=Plone -e ADDONS="plone.restapi==8.18.0 plone.app.iterate==4.0.2 plone.rest==2.0.0a1 plone.app.vocabularies==4.3.0 plone.volto==3.1.0a7" -e PROFILES="plone.volto:default-homepage" plone/plone-backend
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
or as an alternative if you have experience with Plone and you have all the
|
|
86
|
-
dependencies installed on your system, you can use the supplied buildout in the
|
|
86
|
+
dependencies installed on your system, you can use the supplied convenience buildout in the
|
|
87
87
|
`api` folder by issuing the command:
|
|
88
88
|
|
|
89
89
|
```shell
|
|
90
90
|
make build-backend
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
#### Recommended Plone version
|
|
94
|
+
|
|
95
|
+
Volto is Plone 6 default UI, so it will work for all Plone 6 released versions.
|
|
96
|
+
|
|
97
|
+
For the Plone 5 series latest released version (with Python 3) and above is recommended (at the time of writing 5.2.6).
|
|
98
|
+
|
|
99
|
+
The following KGS (or above) are also recommended, for any Plone version used.
|
|
100
|
+
|
|
101
|
+
#### KGS (known good versions) for backend packages
|
|
102
|
+
|
|
103
|
+
Volto always works best with latest versions of the "Frontend stack" or at least the recommended ones (in parenthesis) which are:
|
|
104
|
+
|
|
105
|
+
- plone.restapi (8.18.0)
|
|
106
|
+
- plone.rest (2.0.0a1)
|
|
107
|
+
- plone.volto (3.1.0a7)
|
|
108
|
+
|
|
109
|
+
and the following core packages since some features require up to date versions:
|
|
110
|
+
|
|
111
|
+
- plone.app.iterate (4.0.2)
|
|
112
|
+
- plone.app.vocabularies (4.3.0)
|
|
113
|
+
|
|
93
114
|
### Start Volto
|
|
94
115
|
|
|
95
116
|
```shell
|
|
@@ -109,7 +130,7 @@ Volto is actively developed since 2017 and used in production since 2018 on the
|
|
|
109
130
|
- [Excellence at Humboldt-Universität zu Berlin](https://www.alles-beginnt-mit-einer-frage.de) (Website for the excellence initiative of the [Humboldt University Berlin](https://hu-berlin.de), developed by [kitconcept GmbH](https://kitconcept.com), 2019)
|
|
110
131
|
- [Forest Information System for Europe](https://forest.eea.europa.eu) (Thematic website focusing on European forests, developed by [Eau de Web](https://www.eaudeweb.ro), 2019)
|
|
111
132
|
- [Industrial Emissions portal for Europe](https://industry.eea.europa.eu) (Thematic website focusing on European industrial emissions, developed by [Eau de Web](https://www.eaudeweb.ro), 2020)
|
|
112
|
-
- [Energy Climate Union portal for Europe](https://
|
|
133
|
+
- [Energy Climate Union portal for Europe](https://climate-energy.eea.europa.eu/) (Thematic website focusing on European strides towards mitigating climate change, developed by [Eau de Web](https://www.eaudeweb.ro), 2020)
|
|
113
134
|
- [Talke Carrer Website](https://karriere.talke.com/) (Carrer website for [Talke](https://www.talke.com), one of the leading a chemical and petrochemical logistics companies in Germany, developed by [kitconcept GmbH](https://kitconcept.com), 2020)
|
|
114
135
|
- [Stradanove](http://www.stradanove.it/) (Website of the Department of Youth Policies of the Municipality of Modena, developed by [RedTurtle](https://redturtle.it), 2020)
|
|
115
136
|
- [VisitModena](https://www.visitmodena.it/) (Tourist website of the Municipality of Modena, developed by [RedTurtle](https://redturtle.it), 2020)
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
}
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"version": "14.
|
|
12
|
+
"version": "14.3.0",
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
15
|
"url": "git@github.com:plone/volto.git"
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"start-spa": "razzle start --type=spa",
|
|
35
35
|
"build": "razzle build",
|
|
36
36
|
"build-spa": "razzle build --type=spa",
|
|
37
|
-
"test": "razzle test --env=jest-environment-jsdom-sixteen",
|
|
37
|
+
"test": "razzle test --env=jest-environment-jsdom-sixteen --maxWorkers=50%",
|
|
38
38
|
"test:ci": "CI=true razzle test --env=jest-environment-jsdom-sixteen",
|
|
39
39
|
"test:husky": "CI=true yarn test --bail --findRelatedTests",
|
|
40
40
|
"test:debug": "node --inspect node_modules/.bin/jest --runInBand",
|
|
@@ -342,7 +342,7 @@
|
|
|
342
342
|
"prismjs": "1.25.0",
|
|
343
343
|
"promise-file-reader": "1.0.2",
|
|
344
344
|
"prop-types": "15.7.2",
|
|
345
|
-
"query-string": "7.
|
|
345
|
+
"query-string": "7.1.0",
|
|
346
346
|
"razzle": "3.4.5",
|
|
347
347
|
"razzle-plugin-bundle-analyzer": "1.2.0",
|
|
348
348
|
"rc-time-picker": "3.7.3",
|
|
@@ -387,7 +387,7 @@
|
|
|
387
387
|
"release-it": "14.2.1",
|
|
388
388
|
"rrule": "2.6.4",
|
|
389
389
|
"semantic-ui-less": "2.4.1",
|
|
390
|
-
"semantic-ui-react": "0.
|
|
390
|
+
"semantic-ui-react": "2.0.3",
|
|
391
391
|
"semver": "5.6.0",
|
|
392
392
|
"serialize-javascript": "3.1.0",
|
|
393
393
|
"start-server-and-test": "1.10.6",
|
|
@@ -1491,236 +1491,241 @@ class Contents extends Component {
|
|
|
1491
1491
|
</Dropdown.Menu>
|
|
1492
1492
|
</Dropdown>
|
|
1493
1493
|
</Segment>
|
|
1494
|
-
<
|
|
1495
|
-
<Table
|
|
1496
|
-
<Table.
|
|
1497
|
-
<Table.
|
|
1498
|
-
<
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
icon={
|
|
1506
|
-
<Icon
|
|
1507
|
-
name={configurationSVG}
|
|
1508
|
-
size="24px"
|
|
1509
|
-
color="#826a6a"
|
|
1510
|
-
className="configuration-svg"
|
|
1511
|
-
/>
|
|
1512
|
-
}
|
|
1513
|
-
>
|
|
1514
|
-
<Dropdown.Menu>
|
|
1515
|
-
<Dropdown.Header
|
|
1516
|
-
content={this.props.intl.formatMessage(
|
|
1517
|
-
messages.rearrangeBy,
|
|
1518
|
-
)}
|
|
1519
|
-
/>
|
|
1520
|
-
{map(
|
|
1521
|
-
[
|
|
1522
|
-
'id',
|
|
1523
|
-
'sortable_title',
|
|
1524
|
-
'EffectiveDate',
|
|
1525
|
-
'CreationDate',
|
|
1526
|
-
'ModificationDate',
|
|
1527
|
-
'portal_type',
|
|
1528
|
-
],
|
|
1529
|
-
(index) => (
|
|
1530
|
-
<Dropdown.Item
|
|
1531
|
-
key={index}
|
|
1532
|
-
className={`sort_${index} icon-align`}
|
|
1533
|
-
>
|
|
1534
|
-
<Icon name={downKeySVG} size="24px" />
|
|
1535
|
-
<FormattedMessage
|
|
1536
|
-
id={Indexes[index].label}
|
|
1537
|
-
/>
|
|
1538
|
-
<Dropdown.Menu>
|
|
1539
|
-
<Dropdown.Item
|
|
1540
|
-
onClick={this.onSortItems}
|
|
1541
|
-
value={`${Indexes[index].sort_on}|ascending`}
|
|
1542
|
-
className={`sort_${Indexes[index].sort_on}_ascending icon-align`}
|
|
1543
|
-
>
|
|
1544
|
-
<Icon
|
|
1545
|
-
name={sortDownSVG}
|
|
1546
|
-
size="24px"
|
|
1547
|
-
/>{' '}
|
|
1548
|
-
<FormattedMessage
|
|
1549
|
-
id="Ascending"
|
|
1550
|
-
defaultMessage="Ascending"
|
|
1551
|
-
/>
|
|
1552
|
-
</Dropdown.Item>
|
|
1553
|
-
<Dropdown.Item
|
|
1554
|
-
onClick={this.onSortItems}
|
|
1555
|
-
value={`${Indexes[index].sort_on}|descending`}
|
|
1556
|
-
className={`sort_${Indexes[index].sort_on}_descending icon-align`}
|
|
1557
|
-
>
|
|
1558
|
-
<Icon
|
|
1559
|
-
name={sortUpSVG}
|
|
1560
|
-
size="24px"
|
|
1561
|
-
/>{' '}
|
|
1562
|
-
<FormattedMessage
|
|
1563
|
-
id="Descending"
|
|
1564
|
-
defaultMessage="Descending"
|
|
1565
|
-
/>
|
|
1566
|
-
</Dropdown.Item>
|
|
1567
|
-
</Dropdown.Menu>
|
|
1568
|
-
</Dropdown.Item>
|
|
1569
|
-
),
|
|
1494
|
+
<div className="contents-table-wrapper">
|
|
1495
|
+
<Table selectable compact singleLine attached>
|
|
1496
|
+
<Table.Header>
|
|
1497
|
+
<Table.Row>
|
|
1498
|
+
<Table.HeaderCell>
|
|
1499
|
+
<Dropdown
|
|
1500
|
+
item
|
|
1501
|
+
upward={false}
|
|
1502
|
+
className="sort-icon"
|
|
1503
|
+
aria-label={this.props.intl.formatMessage(
|
|
1504
|
+
messages.sort,
|
|
1570
1505
|
)}
|
|
1571
|
-
|
|
1572
|
-
</Dropdown>
|
|
1573
|
-
</Table.HeaderCell>
|
|
1574
|
-
<Table.HeaderCell>
|
|
1575
|
-
<Dropdown
|
|
1576
|
-
upward={false}
|
|
1577
|
-
trigger={
|
|
1578
|
-
<Icon
|
|
1579
|
-
name={
|
|
1580
|
-
this.state.selected.length === 0
|
|
1581
|
-
? checkboxUncheckedSVG
|
|
1582
|
-
: this.state.selected.length ===
|
|
1583
|
-
this.state.items.length
|
|
1584
|
-
? checkboxCheckedSVG
|
|
1585
|
-
: checkboxIndeterminateSVG
|
|
1586
|
-
}
|
|
1587
|
-
color={
|
|
1588
|
-
this.state.selected.length > 0
|
|
1589
|
-
? '#007eb1'
|
|
1590
|
-
: '#826a6a'
|
|
1591
|
-
}
|
|
1592
|
-
size="24px"
|
|
1593
|
-
/>
|
|
1594
|
-
}
|
|
1595
|
-
icon={null}
|
|
1596
|
-
>
|
|
1597
|
-
<Dropdown.Menu>
|
|
1598
|
-
<Dropdown.Header
|
|
1599
|
-
content={this.props.intl.formatMessage(
|
|
1600
|
-
messages.select,
|
|
1601
|
-
)}
|
|
1602
|
-
/>
|
|
1603
|
-
<Dropdown.Item onClick={this.onSelectAll}>
|
|
1506
|
+
icon={
|
|
1604
1507
|
<Icon
|
|
1605
|
-
name={
|
|
1606
|
-
color="#007eb1"
|
|
1508
|
+
name={configurationSVG}
|
|
1607
1509
|
size="24px"
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
id="All"
|
|
1611
|
-
defaultMessage="All"
|
|
1510
|
+
color="#826a6a"
|
|
1511
|
+
className="configuration-svg"
|
|
1612
1512
|
/>
|
|
1613
|
-
|
|
1614
|
-
|
|
1513
|
+
}
|
|
1514
|
+
>
|
|
1515
|
+
<Dropdown.Menu>
|
|
1516
|
+
<Dropdown.Header
|
|
1517
|
+
content={this.props.intl.formatMessage(
|
|
1518
|
+
messages.rearrangeBy,
|
|
1519
|
+
)}
|
|
1520
|
+
/>
|
|
1521
|
+
{map(
|
|
1522
|
+
[
|
|
1523
|
+
'id',
|
|
1524
|
+
'sortable_title',
|
|
1525
|
+
'EffectiveDate',
|
|
1526
|
+
'CreationDate',
|
|
1527
|
+
'ModificationDate',
|
|
1528
|
+
'portal_type',
|
|
1529
|
+
],
|
|
1530
|
+
(index) => (
|
|
1531
|
+
<Dropdown.Item
|
|
1532
|
+
key={index}
|
|
1533
|
+
className={`sort_${index} icon-align`}
|
|
1534
|
+
>
|
|
1535
|
+
<Icon name={downKeySVG} size="24px" />
|
|
1536
|
+
<FormattedMessage
|
|
1537
|
+
id={Indexes[index].label}
|
|
1538
|
+
/>
|
|
1539
|
+
<Dropdown.Menu>
|
|
1540
|
+
<Dropdown.Item
|
|
1541
|
+
onClick={this.onSortItems}
|
|
1542
|
+
value={`${Indexes[index].sort_on}|ascending`}
|
|
1543
|
+
className={`sort_${Indexes[index].sort_on}_ascending icon-align`}
|
|
1544
|
+
>
|
|
1545
|
+
<Icon
|
|
1546
|
+
name={sortDownSVG}
|
|
1547
|
+
size="24px"
|
|
1548
|
+
/>{' '}
|
|
1549
|
+
<FormattedMessage
|
|
1550
|
+
id="Ascending"
|
|
1551
|
+
defaultMessage="Ascending"
|
|
1552
|
+
/>
|
|
1553
|
+
</Dropdown.Item>
|
|
1554
|
+
<Dropdown.Item
|
|
1555
|
+
onClick={this.onSortItems}
|
|
1556
|
+
value={`${Indexes[index].sort_on}|descending`}
|
|
1557
|
+
className={`sort_${Indexes[index].sort_on}_descending icon-align`}
|
|
1558
|
+
>
|
|
1559
|
+
<Icon
|
|
1560
|
+
name={sortUpSVG}
|
|
1561
|
+
size="24px"
|
|
1562
|
+
/>{' '}
|
|
1563
|
+
<FormattedMessage
|
|
1564
|
+
id="Descending"
|
|
1565
|
+
defaultMessage="Descending"
|
|
1566
|
+
/>
|
|
1567
|
+
</Dropdown.Item>
|
|
1568
|
+
</Dropdown.Menu>
|
|
1569
|
+
</Dropdown.Item>
|
|
1570
|
+
),
|
|
1571
|
+
)}
|
|
1572
|
+
</Dropdown.Menu>
|
|
1573
|
+
</Dropdown>
|
|
1574
|
+
</Table.HeaderCell>
|
|
1575
|
+
<Table.HeaderCell>
|
|
1576
|
+
<Dropdown
|
|
1577
|
+
upward={false}
|
|
1578
|
+
trigger={
|
|
1615
1579
|
<Icon
|
|
1616
|
-
name={
|
|
1580
|
+
name={
|
|
1581
|
+
this.state.selected.length === 0
|
|
1582
|
+
? checkboxUncheckedSVG
|
|
1583
|
+
: this.state.selected.length ===
|
|
1584
|
+
this.state.items.length
|
|
1585
|
+
? checkboxCheckedSVG
|
|
1586
|
+
: checkboxIndeterminateSVG
|
|
1587
|
+
}
|
|
1588
|
+
color={
|
|
1589
|
+
this.state.selected.length > 0
|
|
1590
|
+
? '#007eb1'
|
|
1591
|
+
: '#826a6a'
|
|
1592
|
+
}
|
|
1617
1593
|
size="24px"
|
|
1618
|
-
/>{' '}
|
|
1619
|
-
<FormattedMessage
|
|
1620
|
-
id="None"
|
|
1621
|
-
defaultMessage="None"
|
|
1622
1594
|
/>
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
{
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1595
|
+
}
|
|
1596
|
+
icon={null}
|
|
1597
|
+
>
|
|
1598
|
+
<Dropdown.Menu>
|
|
1599
|
+
<Dropdown.Header
|
|
1600
|
+
content={this.props.intl.formatMessage(
|
|
1601
|
+
messages.select,
|
|
1602
|
+
)}
|
|
1603
|
+
/>
|
|
1604
|
+
<Dropdown.Item onClick={this.onSelectAll}>
|
|
1605
|
+
<Icon
|
|
1606
|
+
name={checkboxCheckedSVG}
|
|
1607
|
+
color="#007eb1"
|
|
1608
|
+
size="24px"
|
|
1609
|
+
/>{' '}
|
|
1610
|
+
<FormattedMessage
|
|
1611
|
+
id="All"
|
|
1612
|
+
defaultMessage="All"
|
|
1613
|
+
/>
|
|
1614
|
+
</Dropdown.Item>
|
|
1615
|
+
<Dropdown.Item onClick={this.onSelectNone}>
|
|
1616
|
+
<Icon
|
|
1617
|
+
name={checkboxUncheckedSVG}
|
|
1618
|
+
size="24px"
|
|
1619
|
+
/>{' '}
|
|
1620
|
+
<FormattedMessage
|
|
1621
|
+
id="None"
|
|
1622
|
+
defaultMessage="None"
|
|
1623
|
+
/>
|
|
1624
|
+
</Dropdown.Item>
|
|
1625
|
+
<Dropdown.Divider />
|
|
1626
|
+
<Dropdown.Header
|
|
1627
|
+
content={this.props.intl.formatMessage(
|
|
1628
|
+
messages.selected,
|
|
1629
|
+
{ count: this.state.selected.length },
|
|
1630
|
+
)}
|
|
1631
|
+
/>
|
|
1632
|
+
<Input
|
|
1633
|
+
icon={<Icon name={zoomSVG} size="24px" />}
|
|
1634
|
+
iconPosition="left"
|
|
1635
|
+
className="search"
|
|
1636
|
+
placeholder={this.props.intl.formatMessage(
|
|
1637
|
+
messages.filter,
|
|
1638
|
+
)}
|
|
1639
|
+
onChange={this.onChangeSelected}
|
|
1640
|
+
onClick={(e) => {
|
|
1641
|
+
e.preventDefault();
|
|
1642
|
+
e.stopPropagation();
|
|
1643
|
+
}}
|
|
1644
|
+
/>
|
|
1645
|
+
<Dropdown.Menu scrolling>
|
|
1646
|
+
{map(filteredItems, (item) => (
|
|
1647
|
+
<Dropdown.Item
|
|
1648
|
+
key={item}
|
|
1649
|
+
value={item}
|
|
1650
|
+
onClick={this.onDeselect}
|
|
1651
|
+
>
|
|
1652
|
+
<Icon
|
|
1653
|
+
name={deleteSVG}
|
|
1654
|
+
color="#e40166"
|
|
1655
|
+
size="24px"
|
|
1656
|
+
/>{' '}
|
|
1657
|
+
{this.getFieldById(item, 'title')}
|
|
1658
|
+
</Dropdown.Item>
|
|
1659
|
+
))}
|
|
1660
|
+
</Dropdown.Menu>
|
|
1659
1661
|
</Dropdown.Menu>
|
|
1660
|
-
</Dropdown
|
|
1661
|
-
</
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1662
|
+
</Dropdown>
|
|
1663
|
+
</Table.HeaderCell>
|
|
1664
|
+
<Table.HeaderCell
|
|
1665
|
+
width={Math.ceil(
|
|
1666
|
+
16 / this.state.index.selectedCount,
|
|
1667
|
+
)}
|
|
1668
|
+
>
|
|
1669
|
+
<FormattedMessage
|
|
1670
|
+
id="Title"
|
|
1671
|
+
defaultMessage="Title"
|
|
1672
|
+
/>
|
|
1673
|
+
</Table.HeaderCell>
|
|
1674
|
+
{map(
|
|
1675
|
+
this.state.index.order,
|
|
1676
|
+
(index, order) =>
|
|
1677
|
+
this.state.index.values[index].selected && (
|
|
1678
|
+
<ContentsIndexHeader
|
|
1679
|
+
key={index}
|
|
1680
|
+
width={Math.ceil(
|
|
1681
|
+
16 / this.state.index.selectedCount,
|
|
1682
|
+
)}
|
|
1683
|
+
label={
|
|
1684
|
+
this.state.index.values[index].label
|
|
1685
|
+
}
|
|
1686
|
+
order={order}
|
|
1687
|
+
onOrderIndex={this.onOrderIndex}
|
|
1688
|
+
/>
|
|
1689
|
+
),
|
|
1666
1690
|
)}
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1691
|
+
<Table.HeaderCell textAlign="right">
|
|
1692
|
+
<FormattedMessage
|
|
1693
|
+
id="Actions"
|
|
1694
|
+
defaultMessage="Actions"
|
|
1695
|
+
/>
|
|
1696
|
+
</Table.HeaderCell>
|
|
1697
|
+
</Table.Row>
|
|
1698
|
+
</Table.Header>
|
|
1699
|
+
<Table.Body>
|
|
1700
|
+
{this.state.items.map((item, order) => (
|
|
1701
|
+
<ContentsItem
|
|
1702
|
+
key={item['@id']}
|
|
1703
|
+
item={item}
|
|
1704
|
+
order={order}
|
|
1705
|
+
selected={
|
|
1706
|
+
indexOf(this.state.selected, item['@id']) !==
|
|
1707
|
+
-1
|
|
1708
|
+
}
|
|
1709
|
+
onClick={this.onSelect}
|
|
1710
|
+
indexes={filter(
|
|
1711
|
+
map(this.state.index.order, (index) => ({
|
|
1712
|
+
id: index,
|
|
1713
|
+
type: this.state.index.values[index].type,
|
|
1714
|
+
})),
|
|
1715
|
+
(index) =>
|
|
1716
|
+
this.state.index.values[index.id].selected,
|
|
1717
|
+
)}
|
|
1718
|
+
onCut={this.cut}
|
|
1719
|
+
onCopy={this.copy}
|
|
1720
|
+
onDelete={this.delete}
|
|
1721
|
+
onOrderItem={this.onOrderItem}
|
|
1722
|
+
onMoveToTop={this.onMoveToTop}
|
|
1723
|
+
onMoveToBottom={this.onMoveToBottom}
|
|
1692
1724
|
/>
|
|
1693
|
-
|
|
1694
|
-
</Table.
|
|
1695
|
-
</Table
|
|
1696
|
-
|
|
1697
|
-
{this.state.items.map((item, order) => (
|
|
1698
|
-
<ContentsItem
|
|
1699
|
-
key={item['@id']}
|
|
1700
|
-
item={item}
|
|
1701
|
-
order={order}
|
|
1702
|
-
selected={
|
|
1703
|
-
indexOf(this.state.selected, item['@id']) !== -1
|
|
1704
|
-
}
|
|
1705
|
-
onClick={this.onSelect}
|
|
1706
|
-
indexes={filter(
|
|
1707
|
-
map(this.state.index.order, (index) => ({
|
|
1708
|
-
id: index,
|
|
1709
|
-
type: this.state.index.values[index].type,
|
|
1710
|
-
})),
|
|
1711
|
-
(index) =>
|
|
1712
|
-
this.state.index.values[index.id].selected,
|
|
1713
|
-
)}
|
|
1714
|
-
onCut={this.cut}
|
|
1715
|
-
onCopy={this.copy}
|
|
1716
|
-
onDelete={this.delete}
|
|
1717
|
-
onOrderItem={this.onOrderItem}
|
|
1718
|
-
onMoveToTop={this.onMoveToTop}
|
|
1719
|
-
onMoveToBottom={this.onMoveToBottom}
|
|
1720
|
-
/>
|
|
1721
|
-
))}
|
|
1722
|
-
</Table.Body>
|
|
1723
|
-
</Table>
|
|
1725
|
+
))}
|
|
1726
|
+
</Table.Body>
|
|
1727
|
+
</Table>
|
|
1728
|
+
</div>
|
|
1724
1729
|
|
|
1725
1730
|
<div className="contents-pagination">
|
|
1726
1731
|
<Pagination
|
|
@@ -148,8 +148,8 @@ export class DatetimeWidgetComponent extends Component {
|
|
|
148
148
|
const moment = this.props.moment.default;
|
|
149
149
|
if (time) {
|
|
150
150
|
const base = (this.getInternalValue() || moment()).set({
|
|
151
|
-
hours: time
|
|
152
|
-
minutes: time
|
|
151
|
+
hours: time?.hours() ?? 0,
|
|
152
|
+
minutes: time?.minutes() ?? 0,
|
|
153
153
|
seconds: 0,
|
|
154
154
|
});
|
|
155
155
|
const dateValue = base.toISOString();
|
|
@@ -5,7 +5,9 @@ import config from '@plone/volto/registry';
|
|
|
5
5
|
|
|
6
6
|
const NavItem = ({ item, lang }) => {
|
|
7
7
|
const { settings } = config;
|
|
8
|
-
|
|
8
|
+
// The item.url in the root is ''
|
|
9
|
+
// TODO: make more consistent it everywhere (eg. reducers to return '/' instead of '')
|
|
10
|
+
if (isInternalURL(item.url) || item.url === '') {
|
|
9
11
|
return (
|
|
10
12
|
<NavLink
|
|
11
13
|
to={item.url === '' ? '/' : item.url}
|
|
@@ -4,7 +4,6 @@ import { getAPIResourceWithAuth } from '@plone/volto/helpers';
|
|
|
4
4
|
const HEADERS = ['content-type', 'content-disposition', 'cache-control'];
|
|
5
5
|
|
|
6
6
|
function fileMiddleware(req, res, next) {
|
|
7
|
-
const { errorHandler } = req.app.locals;
|
|
8
7
|
getAPIResourceWithAuth(req)
|
|
9
8
|
.then((resource) => {
|
|
10
9
|
// Just forward the headers that we need
|
|
@@ -14,8 +13,8 @@ function fileMiddleware(req, res, next) {
|
|
|
14
13
|
}
|
|
15
14
|
});
|
|
16
15
|
res.send(resource.body);
|
|
17
|
-
}
|
|
18
|
-
.catch(
|
|
16
|
+
})
|
|
17
|
+
.catch(next);
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
export default function () {
|
|
@@ -4,7 +4,6 @@ import { getAPIResourceWithAuth } from '@plone/volto/helpers';
|
|
|
4
4
|
const HEADERS = ['content-type', 'content-disposition', 'cache-control'];
|
|
5
5
|
|
|
6
6
|
function imageMiddleware(req, res, next) {
|
|
7
|
-
const { errorHandler } = req.app.locals;
|
|
8
7
|
getAPIResourceWithAuth(req)
|
|
9
8
|
.then((resource) => {
|
|
10
9
|
// Just forward the headers that we need
|
|
@@ -13,10 +12,9 @@ function imageMiddleware(req, res, next) {
|
|
|
13
12
|
res.set(header, resource.headers[header]);
|
|
14
13
|
}
|
|
15
14
|
});
|
|
16
|
-
|
|
17
15
|
res.send(resource.body);
|
|
18
|
-
}
|
|
19
|
-
.catch(
|
|
16
|
+
})
|
|
17
|
+
.catch(next);
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
export default function () {
|
|
@@ -34,11 +34,5 @@ export const getAPIResourceWithAuth = (req) =>
|
|
|
34
34
|
if (authToken) {
|
|
35
35
|
request.set('Authorization', `Bearer ${authToken}`);
|
|
36
36
|
}
|
|
37
|
-
request.
|
|
38
|
-
if (error) {
|
|
39
|
-
reject(error);
|
|
40
|
-
} else {
|
|
41
|
-
resolve(res);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
37
|
+
request.then(resolve).catch(reject);
|
|
44
38
|
});
|
|
@@ -61,7 +61,7 @@ const widgetValidation = {
|
|
|
61
61
|
},
|
|
62
62
|
url: {
|
|
63
63
|
isValidURL: (urlValue, urlObj, intlFunc) => {
|
|
64
|
-
const urlRegex = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)
|
|
64
|
+
const urlRegex = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?|^((http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/gm;
|
|
65
65
|
const isValid = urlRegex.test(urlValue);
|
|
66
66
|
return !isValid ? intlFunc(messages.isValidURL) : null;
|
|
67
67
|
},
|
package/src/server.jsx
CHANGED
|
@@ -72,6 +72,34 @@ const middleware = (config.settings.expressMiddleware || []).filter((m) => m);
|
|
|
72
72
|
server.all('*', setupServer);
|
|
73
73
|
if (middleware.length) server.use('/', middleware);
|
|
74
74
|
|
|
75
|
+
server.use(function (err, req, res, next) {
|
|
76
|
+
if (err) {
|
|
77
|
+
const { store } = req.app.locals;
|
|
78
|
+
const errorPage = (
|
|
79
|
+
<Provider store={store} onError={reactIntlErrorHandler}>
|
|
80
|
+
<StaticRouter context={{}} location={req.url}>
|
|
81
|
+
<ErrorPage message={err.message} />
|
|
82
|
+
</StaticRouter>
|
|
83
|
+
</Provider>
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
res.set({
|
|
87
|
+
'Cache-Control': 'public, max-age=60, no-transform',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/* Displays error in console
|
|
91
|
+
* TODO:
|
|
92
|
+
* - get ignored codes from Plone error_log
|
|
93
|
+
*/
|
|
94
|
+
const ignoredErrors = [301, 302, 401, 404];
|
|
95
|
+
if (!ignoredErrors.includes(err.status)) console.error(err);
|
|
96
|
+
|
|
97
|
+
res
|
|
98
|
+
.status(err.status || 500) // If error happens in Volto code itself error status is undefined
|
|
99
|
+
.send(`<!doctype html> ${renderToString(errorPage)}`);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
75
103
|
function setupServer(req, res, next) {
|
|
76
104
|
const api = new Api(req);
|
|
77
105
|
|
|
@@ -78,6 +78,18 @@
|
|
|
78
78
|
top: 3px;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
.contents-table-wrapper {
|
|
82
|
+
width: 100%;
|
|
83
|
+
overflow-x: auto;
|
|
84
|
+
|
|
85
|
+
.ui.attached.table {
|
|
86
|
+
width: 100%;
|
|
87
|
+
max-width: 100%;
|
|
88
|
+
|
|
89
|
+
border: none;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
81
93
|
.ui.table .icon-align,
|
|
82
94
|
.ui.dropdown .menu > .item.icon-align {
|
|
83
95
|
display: flex;
|