@plone/volto 16.31.12 → 16.32.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.
package/.changelog.draft CHANGED
@@ -1,8 +1,7 @@
1
- ## 16.31.12 (2024-08-12)
1
+ ## 16.32.1 (2024-08-28)
2
2
 
3
3
  ### Bugfix
4
4
 
5
- - Return a 302 response for server-side rendering of the Link view for unauthenticated users. @davisagli [#6235](https://github.com/plone/volto/issues/6235)
6
- - In the URL Management control panel, allow external URLs as targets. @davisagli [#6247](https://github.com/plone/volto/issues/6247)
5
+ - Fix error in `SortOn` component when no sort is selected. @davisagli [#6273](https://github.com/plone/volto/issues/6273)
7
6
 
8
7
 
Binary file
package/CHANGELOG.md CHANGED
@@ -8,6 +8,19 @@
8
8
 
9
9
  <!-- towncrier release notes start -->
10
10
 
11
+ ## 16.32.1 (2024-08-28)
12
+
13
+ ### Bugfix
14
+
15
+ - Fix error in `SortOn` component when no sort is selected. @davisagli [#6273](https://github.com/plone/volto/issues/6273)
16
+
17
+ ## 16.32.0 (2024-08-16)
18
+
19
+ ### Feature
20
+
21
+ - The schema for the `ContentsPropertiesModal` can be enhanced using the `contentPropertiesSchemaEnhancer` setting.
22
+ Also, the properties form is now prepopulated with values if all selected items share the same value. @davisagli [#6248](https://github.com/plone/volto/issues/6248)
23
+
11
24
  ## 16.31.12 (2024-08-12)
12
25
 
13
26
  ### Bugfix
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "16.31.12",
12
+ "version": "16.32.1",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plone/volto-slate",
3
- "version": "16.31.12",
3
+ "version": "16.32.1",
4
4
  "description": "Slate.js integration with Volto",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -60,7 +60,7 @@ const SortOn = (props) => {
60
60
 
61
61
  const showSelectField = sortOnOptions.length > 1;
62
62
  if (!showSelectField && !activeSortOn) {
63
- return;
63
+ return null;
64
64
  }
65
65
  const value = {
66
66
  value: activeSortOn || intl.formatMessage(messages.noSelection),
@@ -1524,6 +1524,9 @@ class Contents extends Component {
1524
1524
  onCancel={this.onPropertiesCancel}
1525
1525
  onOk={this.onPropertiesOk}
1526
1526
  items={this.state.selected}
1527
+ values={map(this.state.selected, (id) =>
1528
+ find(this.state.items, { '@id': id }),
1529
+ ).filter((item) => item)}
1527
1530
  />
1528
1531
  {this.state.showWorkflow && (
1529
1532
  <ContentsWorkflowModal
@@ -3,15 +3,17 @@
3
3
  * @module components/manage/Contents/ContentsPropertiesModal
4
4
  */
5
5
 
6
- import React, { Component } from 'react';
6
+ import { Component } from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { connect } from 'react-redux';
9
9
  import { compose } from 'redux';
10
10
  import { isEmpty, map } from 'lodash';
11
11
  import { defineMessages, injectIntl } from 'react-intl';
12
12
 
13
+ import { cloneDeepSchema } from '@plone/volto/helpers/Utils/Utils';
13
14
  import { updateContent } from '@plone/volto/actions';
14
15
  import { ModalForm } from '@plone/volto/components';
16
+ import config from '@plone/volto/registry';
15
17
 
16
18
  const messages = defineMessages({
17
19
  properties: {
@@ -124,13 +126,19 @@ class ContentsPropertiesModal extends Component {
124
126
  * @param {Object} data Form data
125
127
  * @returns {undefined}
126
128
  */
127
- onSubmit(data) {
128
- if (isEmpty(data)) {
129
+ onSubmit(initialData, data) {
130
+ let changes = {};
131
+ for (const name of Object.keys(data)) {
132
+ if (data[name] !== initialData[name]) {
133
+ changes[name] = data[name];
134
+ }
135
+ }
136
+ if (isEmpty(changes)) {
129
137
  this.props.onOk();
130
138
  } else {
131
139
  this.props.updateContent(
132
140
  this.props.items,
133
- map(this.props.items, () => data),
141
+ map(this.props.items, () => changes),
134
142
  );
135
143
  }
136
144
  }
@@ -141,71 +149,96 @@ class ContentsPropertiesModal extends Component {
141
149
  * @returns {string} Markup for the component.
142
150
  */
143
151
  render() {
152
+ const baseSchema = {
153
+ fieldsets: [
154
+ {
155
+ id: 'default',
156
+ title: this.props.intl.formatMessage(messages.default),
157
+ fields: [
158
+ 'effective',
159
+ 'expires',
160
+ 'rights',
161
+ 'creators',
162
+ 'exclude_from_nav',
163
+ ],
164
+ },
165
+ ],
166
+ properties: {
167
+ effective: {
168
+ description: this.props.intl.formatMessage(
169
+ messages.effectiveDescription,
170
+ ),
171
+ title: this.props.intl.formatMessage(messages.effectiveTitle),
172
+ type: 'string',
173
+ widget: 'datetime',
174
+ },
175
+ expires: {
176
+ description: this.props.intl.formatMessage(
177
+ messages.expiresDescription,
178
+ ),
179
+ title: this.props.intl.formatMessage(messages.expiresTitle),
180
+ type: 'string',
181
+ widget: 'datetime',
182
+ },
183
+ rights: {
184
+ description: this.props.intl.formatMessage(
185
+ messages.rightsDescription,
186
+ ),
187
+ title: this.props.intl.formatMessage(messages.rightsTitle),
188
+ type: 'string',
189
+ widget: 'textarea',
190
+ },
191
+ creators: {
192
+ description: this.props.intl.formatMessage(
193
+ messages.creatorsDescription,
194
+ ),
195
+ title: this.props.intl.formatMessage(messages.creatorsTitle),
196
+ type: 'array',
197
+ },
198
+ exclude_from_nav: {
199
+ description: this.props.intl.formatMessage(
200
+ messages.excludeFromNavDescription,
201
+ ),
202
+ title: this.props.intl.formatMessage(messages.excludeFromNavTitle),
203
+ type: 'boolean',
204
+ },
205
+ },
206
+ required: [],
207
+ };
208
+ const schemaEnhancer = config.settings.contentPropertiesSchemaEnhancer;
209
+ let schema = schemaEnhancer
210
+ ? schemaEnhancer({
211
+ schema: cloneDeepSchema(baseSchema),
212
+ intl: this.props.intl,
213
+ })
214
+ : baseSchema;
215
+
216
+ const initialData = {};
217
+ if (this.props.values?.length) {
218
+ for (const name of Object.keys(schema.properties)) {
219
+ const firstValue = this.props.values[0][name];
220
+ // should not show floor or ceiling dates
221
+ if (
222
+ (name === 'effective' && firstValue && firstValue <= '1970') ||
223
+ (name === 'expires' && firstValue && firstValue >= '2499')
224
+ ) {
225
+ continue;
226
+ }
227
+ if (this.props.values.every((item) => item[name] === firstValue)) {
228
+ initialData[name] = firstValue;
229
+ }
230
+ }
231
+ }
232
+
144
233
  return (
145
234
  this.props.open && (
146
235
  <ModalForm
147
236
  open={this.props.open}
148
- onSubmit={this.onSubmit}
237
+ onSubmit={this.onSubmit.bind(this, initialData)}
149
238
  onCancel={this.props.onCancel}
150
239
  title={this.props.intl.formatMessage(messages.properties)}
151
- schema={{
152
- fieldsets: [
153
- {
154
- id: 'default',
155
- title: this.props.intl.formatMessage(messages.default),
156
- fields: [
157
- 'effective',
158
- 'expires',
159
- 'rights',
160
- 'creators',
161
- 'exclude_from_nav',
162
- ],
163
- },
164
- ],
165
- properties: {
166
- effective: {
167
- description: this.props.intl.formatMessage(
168
- messages.effectiveDescription,
169
- ),
170
- title: this.props.intl.formatMessage(messages.effectiveTitle),
171
- type: 'string',
172
- widget: 'datetime',
173
- },
174
- expires: {
175
- description: this.props.intl.formatMessage(
176
- messages.expiresDescription,
177
- ),
178
- title: this.props.intl.formatMessage(messages.expiresTitle),
179
- type: 'string',
180
- widget: 'datetime',
181
- },
182
- rights: {
183
- description: this.props.intl.formatMessage(
184
- messages.rightsDescription,
185
- ),
186
- title: this.props.intl.formatMessage(messages.rightsTitle),
187
- type: 'string',
188
- widget: 'textarea',
189
- },
190
- creators: {
191
- description: this.props.intl.formatMessage(
192
- messages.creatorsDescription,
193
- ),
194
- title: this.props.intl.formatMessage(messages.creatorsTitle),
195
- type: 'array',
196
- },
197
- exclude_from_nav: {
198
- description: this.props.intl.formatMessage(
199
- messages.excludeFromNavDescription,
200
- ),
201
- title: this.props.intl.formatMessage(
202
- messages.excludeFromNavTitle,
203
- ),
204
- type: 'boolean',
205
- },
206
- },
207
- required: [],
208
- }}
240
+ schema={schema}
241
+ formData={initialData}
209
242
  />
210
243
  )
211
244
  );
@@ -174,6 +174,7 @@ let config = {
174
174
  ],
175
175
  showSelfRegistration: false,
176
176
  contentMetadataTagsImageField: 'image',
177
+ contentPropertiesSchemaEnhancer: null,
177
178
  hasWorkingCopySupport: false,
178
179
  maxUndoLevels: 200, // undo history size for the main form
179
180
  addonsInfo: addonsInfo,