@eeacms/volto-eea-website-theme 1.0.0 → 1.2.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.
@@ -1,7 +1,9 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
 
4
- const projectRootPath = fs.realpathSync('./project'); // __dirname
4
+ const projectRootPath = fs.existsSync('./project')
5
+ ? fs.realpathSync('./project')
6
+ : fs.realpathSync('./../../../');
5
7
  const packageJson = require(path.join(projectRootPath, 'package.json'));
6
8
  const jsConfig = require(path.join(projectRootPath, 'jsconfig.json')).compilerOptions;
7
9
 
package/CHANGELOG.md CHANGED
@@ -4,7 +4,41 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
- ### [1.0.0](https://github.com/eea/volto-eea-website-theme/compare/0.7.7...1.0.0) - 28 October 2022
7
+ ### [1.2.0](https://github.com/eea/volto-eea-website-theme/compare/1.1.0...1.2.0) - 16 November 2022
8
+
9
+ #### :rocket: New Features
10
+
11
+ - feat(dependencies): volto-subsites [Alin Voinea - [`7e196b3`](https://github.com/eea/volto-eea-website-theme/commit/7e196b3ba0ffdae32d15fc1a9a020a7f760461ac)]
12
+ - feat(header): customization of logo + check if multilingual [Miu Razvan - [`cc5bb39`](https://github.com/eea/volto-eea-website-theme/commit/cc5bb3927b79b87f269c8a2a77b1913785892658)]
13
+
14
+ #### :bug: Bug Fixes
15
+
16
+ - fix(eslint): remove .project.eslintrc.js [Miu Razvan - [`5cf763f`](https://github.com/eea/volto-eea-website-theme/commit/5cf763f4451dab6b63705b8346912145e6b8cc73)]
17
+
18
+ #### :nail_care: Enhancements
19
+
20
+ - change(theme): modified volto-columns-block padding and margin values [David Ichim - [`830b571`](https://github.com/eea/volto-eea-website-theme/commit/830b5711c89c3bcceda01a5cf9372ae4118dfa9d)]
21
+ - change(theme): modified path for site theme [David Ichim - [`3996f88`](https://github.com/eea/volto-eea-website-theme/commit/3996f8836741242fd75517a5a5a9c5180be75aad)]
22
+ - change(website-theme): removed dependency on slate now that plone has slate builtin [David Ichim - [`5840c03`](https://github.com/eea/volto-eea-website-theme/commit/5840c03a8f2c89e44014c93b2b2198467d111b84)]
23
+
24
+ #### :hammer_and_wrench: Others
25
+
26
+ - Release 1.2.0 [Alin Voinea - [`567f1b1`](https://github.com/eea/volto-eea-website-theme/commit/567f1b12d6915f960b73016de3ac53e00a47e9f7)]
27
+ - Add volto-subsite dependency [kreafox - [`7f2751f`](https://github.com/eea/volto-eea-website-theme/commit/7f2751f3da169138971e3c06b932bbe6fb909abf)]
28
+ - Remove console.log [kreafox - [`098f028`](https://github.com/eea/volto-eea-website-theme/commit/098f0282f8694e164b1724c214706c566943e340)]
29
+ - Fix undefined error [kreafox - [`461d693`](https://github.com/eea/volto-eea-website-theme/commit/461d6938630d0c110e9c9f369846bfaac314ea2c)]
30
+ - Improve subsite [kreafox - [`108b03a`](https://github.com/eea/volto-eea-website-theme/commit/108b03a8d31bf642509f0ab9c624aa0309b5faec)]
31
+ - Add .project.eslintrc.js [Alin Voinea - [`b7be5f5`](https://github.com/eea/volto-eea-website-theme/commit/b7be5f5628f555100293e06e9fd7a25b8b7ec16b)]
32
+ - Add subsite class to body [Tiberiu Ichim - [`74d700f`](https://github.com/eea/volto-eea-website-theme/commit/74d700fbfd6249a8604762a7e4e49cce857db0f3)]
33
+ - Add subsite info to header [Tiberiu Ichim - [`47daf8b`](https://github.com/eea/volto-eea-website-theme/commit/47daf8bb6374a1222040626b19d4154df7ba1b83)]
34
+ - fix eslint [Miu Razvan - [`eb8d0a7`](https://github.com/eea/volto-eea-website-theme/commit/eb8d0a790bc70c0aae256c6ff35f63c4885f338e)]
35
+ ### [1.1.0](https://github.com/eea/volto-eea-website-theme/compare/1.0.0...1.1.0) - 28 October 2022
36
+
37
+ #### :nail_care: Enhancements
38
+
39
+ - refactor(customizations): Upgrade to volto 16.alpha.45 [Alin Voinea - [`930b77f`](https://github.com/eea/volto-eea-website-theme/commit/930b77fa6113d423a82704883a349bc1f919bb85)]
40
+
41
+ ## [1.0.0](https://github.com/eea/volto-eea-website-theme/compare/0.7.7...1.0.0) - 28 October 2022
8
42
 
9
43
  #### :nail_care: Enhancements
10
44
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-eea-website-theme",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "@eeacms/volto-eea-website-theme: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -14,7 +14,8 @@
14
14
  "react"
15
15
  ],
16
16
  "addons": [
17
- "@eeacms/volto-eea-design-system"
17
+ "@eeacms/volto-eea-design-system",
18
+ "volto-subsites"
18
19
  ],
19
20
  "repository": {
20
21
  "type": "git",
@@ -22,8 +23,7 @@
22
23
  },
23
24
  "dependencies": {
24
25
  "@eeacms/volto-eea-design-system": "*",
25
- "slate": "^0.71.0",
26
- "slate-react": "^0.71.0"
26
+ "volto-subsites": "*"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@plone/scripts": "*",
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import { useSelector } from 'react-redux';
4
+ import { useLocation } from 'react-router-dom';
5
+
6
+ import { BodyClass } from '@plone/volto/helpers';
7
+ import { isSubsiteRoot } from 'volto-subsites/utils';
8
+
9
+ const SubsiteClass = () => {
10
+ const subsite = useSelector(
11
+ (state) => state.content?.data?.['@components']?.subsite || {},
12
+ );
13
+ const location = useLocation();
14
+
15
+ return (
16
+ <BodyClass
17
+ className={cx('subsite', `subsite-${subsite.subsite_css_class?.token}`, {
18
+ 'subsite-root': isSubsiteRoot(location.pathname, subsite),
19
+ })}
20
+ />
21
+ );
22
+ };
23
+ export default SubsiteClass;
package/src/config.js CHANGED
@@ -8,7 +8,8 @@ import copernicusLogo from '@eeacms/volto-eea-design-system/../theme/themes/eea/
8
8
  import industryLogo from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/logo/industry.svg';
9
9
  import marineLogo from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/logo/marine.svg';
10
10
  import eionetLogo from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/logo/eionet.svg';
11
- import eeaLogo from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/logo/eea-white.svg';
11
+ import eeaLogo from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/eea-logo.svg';
12
+ import eeaWhiteLogo from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/logo/eea-white.svg';
12
13
  import climateLogo from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/logo/climate-health.svg';
13
14
 
14
15
  // TODO: to be consolidated with headerLinks
@@ -92,7 +93,7 @@ export const footerOpts = {
92
93
  managedBy: [
93
94
  {
94
95
  link: 'https://www.eea.europa.eu/',
95
- src: eeaLogo,
96
+ src: eeaWhiteLogo,
96
97
  alt: 'EEA Logo',
97
98
  className: 'site logo',
98
99
  columnSize: {
@@ -175,42 +176,47 @@ export const footerOpts = {
175
176
  address: 'Kongens Nytorv 6 1050 Copenhagen K (+45) 33 36 71 00',
176
177
  };
177
178
 
178
- export const globalHeaderPartnerLinks = {
179
- title: 'Environmental information systems',
180
- links: [
181
- {
182
- title: 'Biodiversity Information System for Europe',
183
- href: 'https://biodiversity.europa.eu/',
184
- },
185
- {
186
- title: 'Climate Adaptation Platform',
187
- href: 'https://climate-adapt.eea.europa.eu/',
188
- },
189
- {
190
- title: 'Copernicus in situ component',
191
- href: 'https://insitu.copernicus.eu/',
192
- },
193
- {
194
- title: 'European Industrial Emissions Portal',
195
- href: 'https://industry.eea.europa.eu/',
196
- },
197
- {
198
- title: 'Forest Information System for Europe',
199
- href: 'https://forest.eea.europa.eu/',
200
- },
201
- {
202
- title: 'Information Platform for Chemical Monitoring',
203
- href: 'https://ipchem.jrc.ec.europa.eu/RDSIdiscovery/ipchem/index.html',
204
- },
205
- {
206
- title: 'Marine Water Information System for Europe',
207
- href: 'https://water.europa.eu/marine',
208
- },
209
- {
210
- title: 'Fresh Water Information System for Europe',
211
- href: 'https://water.europa.eu/freshwater',
212
- },
213
- ],
179
+ // Header.jsx config options
180
+ export const headerOpts = {
181
+ logo: eeaLogo,
182
+ logoWhite: eeaWhiteLogo,
183
+ partnerLinks: {
184
+ title: 'Environmental information systems',
185
+ links: [
186
+ {
187
+ title: 'Biodiversity Information System for Europe',
188
+ href: 'https://biodiversity.europa.eu/',
189
+ },
190
+ {
191
+ title: 'Climate Adaptation Platform',
192
+ href: 'https://climate-adapt.eea.europa.eu/',
193
+ },
194
+ {
195
+ title: 'Copernicus in situ component',
196
+ href: 'https://insitu.copernicus.eu/',
197
+ },
198
+ {
199
+ title: 'European Industrial Emissions Portal',
200
+ href: 'https://industry.eea.europa.eu/',
201
+ },
202
+ {
203
+ title: 'Forest Information System for Europe',
204
+ href: 'https://forest.eea.europa.eu/',
205
+ },
206
+ {
207
+ title: 'Information Platform for Chemical Monitoring',
208
+ href: 'https://ipchem.jrc.ec.europa.eu/RDSIdiscovery/ipchem/index.html',
209
+ },
210
+ {
211
+ title: 'Marine Water Information System for Europe',
212
+ href: 'https://water.europa.eu/marine',
213
+ },
214
+ {
215
+ title: 'Fresh Water Information System for Europe',
216
+ href: 'https://water.europa.eu/freshwater',
217
+ },
218
+ ],
219
+ },
214
220
  };
215
221
 
216
222
  export const languages = [
@@ -74,6 +74,7 @@ class Form extends Component {
74
74
  onCancel: PropTypes.func,
75
75
  submitLabel: PropTypes.string,
76
76
  resetAfterSubmit: PropTypes.bool,
77
+ resetOnCancel: PropTypes.bool,
77
78
  isEditForm: PropTypes.bool,
78
79
  isAdminForm: PropTypes.bool,
79
80
  title: PropTypes.string,
@@ -105,6 +106,7 @@ class Form extends Component {
105
106
  onCancel: null,
106
107
  submitLabel: null,
107
108
  resetAfterSubmit: false,
109
+ resetOnCancel: false,
108
110
  isEditForm: false,
109
111
  isAdminForm: false,
110
112
  title: null,
@@ -399,7 +401,7 @@ class Form extends Component {
399
401
  if (event) {
400
402
  event.preventDefault();
401
403
  }
402
- if (this.props.resetAfterSubmit) {
404
+ if (this.props.resetOnCancel || this.props.resetAfterSubmit) {
403
405
  this.setState({
404
406
  formData: this.props.formData,
405
407
  });
@@ -418,11 +420,13 @@ class Form extends Component {
418
420
  event.preventDefault();
419
421
  }
420
422
 
421
- const errors = FormValidation.validateFieldsPerFieldset({
422
- schema: this.props.schema,
423
- formData: this.state.formData,
424
- formatMessage: this.props.intl.formatMessage,
425
- });
423
+ const errors = this.props.schema
424
+ ? FormValidation.validateFieldsPerFieldset({
425
+ schema: this.props.schema,
426
+ formData: this.state.formData,
427
+ formatMessage: this.props.intl.formatMessage,
428
+ })
429
+ : {};
426
430
 
427
431
  if (keys(errors).length > 0) {
428
432
  const activeIndex = FormValidation.showFirstTabWithErrors({
@@ -653,6 +657,11 @@ class Form extends Component {
653
657
  {this.props.title}
654
658
  </Segment>
655
659
  ),
660
+ item.description && (
661
+ <Message attached="bottom">
662
+ {item.description}
663
+ </Message>
664
+ ),
656
665
  ...map(item.fields, (field, index) => (
657
666
  <Field
658
667
  {...schema.properties[field]}
@@ -94,6 +94,7 @@ class ModalForm extends Component {
94
94
  loadingMessage: null,
95
95
  submitError: null,
96
96
  className: null,
97
+ dimmer: null,
97
98
  };
98
99
 
99
100
  /**
@@ -224,7 +225,11 @@ class ModalForm extends Component {
224
225
 
225
226
  const state_errors = keys(this.state.errors).length > 0;
226
227
  return (
227
- <Modal open={this.props.open} className={this.props.className}>
228
+ <Modal
229
+ dimmer={this.props.dimmer}
230
+ open={this.props.open}
231
+ className={this.props.className}
232
+ >
228
233
  <Header>{this.props.title}</Header>
229
234
  <Dimmer active={this.props.loading}>
230
235
  <Loader>
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import React, { Component } from 'react';
6
6
  import PropTypes from 'prop-types';
7
+ import { Plug, Pluggable } from '@plone/volto/components/manage/Pluggable';
7
8
  import { Helmet } from '@plone/volto/helpers';
8
9
  import { connect } from 'react-redux';
9
10
  import { compose } from 'redux';
@@ -291,146 +292,162 @@ class SharingComponent extends Component {
291
292
  <Container id="page-sharing">
292
293
  <Helmet title={this.props.intl.formatMessage(messages.sharing)} />
293
294
  <Segment.Group raised>
294
- <Segment className="primary">
295
- <FormattedMessage
296
- id="Sharing for {title}"
297
- defaultMessage="Sharing for {title}"
298
- values={{ title: <q>{this.props.title}</q> }}
299
- />
300
- </Segment>
301
- <Segment secondary>
302
- <FormattedMessage
303
- id="You can control who can view and edit your item using the list below."
304
- defaultMessage="You can control who can view and edit your item using the list below."
305
- />
306
- </Segment>
307
- <Segment>
308
- <Form onSubmit={this.onSearch}>
309
- <Form.Field>
310
- <Input
311
- name="SearchableText"
312
- action={{ icon: 'search' }}
313
- placeholder={this.props.intl.formatMessage(
314
- messages.searchForUserOrGroup,
315
- )}
316
- onChange={this.onChangeSearch}
317
- />
318
- </Form.Field>
319
- </Form>
320
- </Segment>
321
- <Form onSubmit={this.onSubmit}>
322
- <Table celled padded striped attached>
323
- <Table.Header>
324
- <Table.Row>
325
- <Table.HeaderCell>
326
- <FormattedMessage id="Name" defaultMessage="Name" />
327
- </Table.HeaderCell>
328
- {this.props.available_roles?.map((role) => (
329
- <Table.HeaderCell key={role.id}>
330
- {role.title}
295
+ <Pluggable name="sharing-component" />
296
+ <Plug pluggable="sharing-component" id="sharing-component-title">
297
+ <Segment className="primary">
298
+ <FormattedMessage
299
+ id="Sharing for {title}"
300
+ defaultMessage="Sharing for {title}"
301
+ values={{ title: <q>{this.props.title}</q> }}
302
+ />
303
+ </Segment>
304
+ </Plug>
305
+ <Plug
306
+ pluggable="sharing-component"
307
+ id="sharing-component-description"
308
+ >
309
+ <Segment secondary>
310
+ <FormattedMessage
311
+ id="You can control who can view and edit your item using the list below."
312
+ defaultMessage="You can control who can view and edit your item using the list below."
313
+ />
314
+ </Segment>
315
+ </Plug>
316
+ <Plug pluggable="sharing-component" id="sharing-component-search">
317
+ <Segment>
318
+ <Form onSubmit={this.onSearch}>
319
+ <Form.Field>
320
+ <Input
321
+ name="SearchableText"
322
+ action={{ icon: 'search' }}
323
+ placeholder={this.props.intl.formatMessage(
324
+ messages.searchForUserOrGroup,
325
+ )}
326
+ onChange={this.onChangeSearch}
327
+ />
328
+ </Form.Field>
329
+ </Form>
330
+ </Segment>
331
+ </Plug>
332
+ <Plug
333
+ pluggable="sharing-component"
334
+ id="sharing-component-form"
335
+ dependencies={[this.state.entries, this.props.available_roles]}
336
+ >
337
+ <Form onSubmit={this.onSubmit}>
338
+ <Table celled padded striped attached>
339
+ <Table.Header>
340
+ <Table.Row>
341
+ <Table.HeaderCell>
342
+ <FormattedMessage id="Name" defaultMessage="Name" />
331
343
  </Table.HeaderCell>
332
- ))}
333
- </Table.Row>
334
- </Table.Header>
335
- <Table.Body>
336
- {this.state.entries?.map((entry) => (
337
- <Table.Row key={entry.id}>
338
- <Table.Cell>
339
- <IconOld
340
- name={entry.type === 'user' ? 'user' : 'users'}
341
- title={
342
- entry.type === 'user'
343
- ? this.props.intl.formatMessage(messages.user)
344
- : this.props.intl.formatMessage(messages.group)
345
- }
346
- />{' '}
347
- {entry.title}
348
- {entry.login && ` (${entry.login})`}
349
- </Table.Cell>
350
344
  {this.props.available_roles?.map((role) => (
351
- <Table.Cell key={role.id}>
352
- {entry.roles[role.id] === 'global' && (
353
- <IconOld
354
- name="check circle outline"
355
- title={this.props.intl.formatMessage(
356
- messages.globalRole,
357
- )}
358
- color="blue"
359
- />
360
- )}
361
- {entry.roles[role.id] === 'acquired' && (
362
- <IconOld
363
- name="check circle outline"
364
- color="green"
365
- title={this.props.intl.formatMessage(
366
- messages.inheritedValue,
367
- )}
368
- />
369
- )}
370
- {typeof entry.roles[role.id] === 'boolean' && (
371
- <Checkbox
372
- onChange={this.onChange}
373
- value={`${entry.id}:${role.id}`}
374
- checked={entry.roles[role.id]}
375
- disabled={entry.login === this.props.login}
376
- />
377
- )}
378
- </Table.Cell>
345
+ <Table.HeaderCell key={role.id}>
346
+ {role.title}
347
+ </Table.HeaderCell>
379
348
  ))}
380
349
  </Table.Row>
381
- ))}
382
- </Table.Body>
383
- </Table>
384
- <Segment attached>
385
- <Form.Field>
386
- <Checkbox
387
- checked={this.state.inherit}
388
- onChange={this.onToggleInherit}
389
- label={this.props.intl.formatMessage(messages.inherit)}
390
- />
391
- </Form.Field>
392
- <p className="help">
393
- <FormattedMessage
394
- id="By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
395
- defaultMessage="By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
396
- values={{
397
- inherited: (
398
- <IconOld name="check circle outline" color="green" />
399
- ),
400
- global: (
401
- <IconOld name="check circle outline" color="blue" />
402
- ),
403
- }}
404
- />
405
- </p>
406
- </Segment>
407
- <Segment className="actions" attached clearing>
408
- <Button
409
- basic
410
- icon
411
- primary
412
- floated="right"
413
- type="submit"
414
- aria-label={this.props.intl.formatMessage(messages.save)}
415
- title={this.props.intl.formatMessage(messages.save)}
416
- loading={this.props.updateRequest.loading}
417
- onClick={this.onSubmit}
418
- >
419
- <Icon className="circled" name={aheadSVG} size="30px" />
420
- </Button>
421
- <Button
422
- basic
423
- icon
424
- secondary
425
- aria-label={this.props.intl.formatMessage(messages.cancel)}
426
- title={this.props.intl.formatMessage(messages.cancel)}
427
- floated="right"
428
- onClick={this.onCancel}
429
- >
430
- <Icon className="circled" name={clearSVG} size="30px" />
431
- </Button>
432
- </Segment>
433
- </Form>
350
+ </Table.Header>
351
+ <Table.Body>
352
+ {this.state.entries?.map((entry) => (
353
+ <Table.Row key={entry.id}>
354
+ <Table.Cell>
355
+ <IconOld
356
+ name={entry.type === 'user' ? 'user' : 'users'}
357
+ title={
358
+ entry.type === 'user'
359
+ ? this.props.intl.formatMessage(messages.user)
360
+ : this.props.intl.formatMessage(messages.group)
361
+ }
362
+ />{' '}
363
+ {entry.title}
364
+ {entry.login && ` (${entry.login})`}
365
+ </Table.Cell>
366
+ {this.props.available_roles?.map((role) => (
367
+ <Table.Cell key={role.id}>
368
+ {entry.roles[role.id] === 'global' && (
369
+ <IconOld
370
+ name="check circle outline"
371
+ title={this.props.intl.formatMessage(
372
+ messages.globalRole,
373
+ )}
374
+ color="blue"
375
+ />
376
+ )}
377
+ {entry.roles[role.id] === 'acquired' && (
378
+ <IconOld
379
+ name="check circle outline"
380
+ color="green"
381
+ title={this.props.intl.formatMessage(
382
+ messages.inheritedValue,
383
+ )}
384
+ />
385
+ )}
386
+ {typeof entry.roles[role.id] === 'boolean' && (
387
+ <Checkbox
388
+ onChange={this.onChange}
389
+ value={`${entry.id}:${role.id}`}
390
+ checked={entry.roles[role.id]}
391
+ disabled={entry.login === this.props.login}
392
+ />
393
+ )}
394
+ </Table.Cell>
395
+ ))}
396
+ </Table.Row>
397
+ ))}
398
+ </Table.Body>
399
+ </Table>
400
+ <Segment attached>
401
+ <Form.Field>
402
+ <Checkbox
403
+ checked={this.state.inherit}
404
+ onChange={this.onToggleInherit}
405
+ label={this.props.intl.formatMessage(messages.inherit)}
406
+ />
407
+ </Form.Field>
408
+ <p className="help">
409
+ <FormattedMessage
410
+ id="By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
411
+ defaultMessage="By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator."
412
+ values={{
413
+ inherited: (
414
+ <IconOld name="check circle outline" color="green" />
415
+ ),
416
+ global: (
417
+ <IconOld name="check circle outline" color="blue" />
418
+ ),
419
+ }}
420
+ />
421
+ </p>
422
+ </Segment>
423
+ <Segment className="actions" attached clearing>
424
+ <Button
425
+ basic
426
+ icon
427
+ primary
428
+ floated="right"
429
+ type="submit"
430
+ aria-label={this.props.intl.formatMessage(messages.save)}
431
+ title={this.props.intl.formatMessage(messages.save)}
432
+ loading={this.props.updateRequest.loading}
433
+ onClick={this.onSubmit}
434
+ >
435
+ <Icon className="circled" name={aheadSVG} size="30px" />
436
+ </Button>
437
+ <Button
438
+ basic
439
+ icon
440
+ secondary
441
+ aria-label={this.props.intl.formatMessage(messages.cancel)}
442
+ title={this.props.intl.formatMessage(messages.cancel)}
443
+ floated="right"
444
+ onClick={this.onCancel}
445
+ >
446
+ <Icon className="circled" name={clearSVG} size="30px" />
447
+ </Button>
448
+ </Segment>
449
+ </Form>
450
+ </Plug>
434
451
  </Segment.Group>
435
452
  {this.state.isClient && (
436
453
  <Portal node={document.getElementById('toolbar')}>
@@ -65,6 +65,7 @@ export class ObjectBrowserWidgetComponent extends Component {
65
65
  description: PropTypes.string,
66
66
  mode: PropTypes.string, // link, image, multiple
67
67
  return: PropTypes.string, // single, multiple
68
+ initialPath: PropTypes.string,
68
69
  required: PropTypes.bool,
69
70
  error: PropTypes.arrayOf(PropTypes.string),
70
71
  value: PropTypes.oneOfType([
@@ -74,6 +75,7 @@ export class ObjectBrowserWidgetComponent extends Component {
74
75
  onChange: PropTypes.func.isRequired,
75
76
  openObjectBrowser: PropTypes.func.isRequired,
76
77
  allowExternals: PropTypes.bool,
78
+ placeholder: PropTypes.string,
77
79
  };
78
80
 
79
81
  /**
@@ -88,6 +90,7 @@ export class ObjectBrowserWidgetComponent extends Component {
88
90
  value: [],
89
91
  mode: 'multiple',
90
92
  return: 'multiple',
93
+ initialPath: '',
91
94
  allowExternals: false,
92
95
  };
93
96
 
@@ -148,7 +151,10 @@ export class ObjectBrowserWidgetComponent extends Component {
148
151
  };
149
152
 
150
153
  onChange = (item) => {
151
- let value = this.props.mode === 'multiple' ? [...this.props.value] : [];
154
+ let value =
155
+ this.props.mode === 'multiple' && this.props.value
156
+ ? [...this.props.value]
157
+ : [];
152
158
  value = value.filter((item) => item != null);
153
159
  const maxSize =
154
160
  this.props.widgetOptions?.pattern_options?.maximumSelectionSize || -1;
@@ -239,7 +245,7 @@ export class ObjectBrowserWidgetComponent extends Component {
239
245
  } else {
240
246
  this.props.onChange(this.props.id, [
241
247
  {
242
- '@id': normalizeUrl(link),
248
+ '@id': flattenToAppURL(link),
243
249
  title: removeProtocol(link),
244
250
  },
245
251
  ]);
@@ -273,15 +279,17 @@ export class ObjectBrowserWidgetComponent extends Component {
273
279
  ev.preventDefault();
274
280
  this.props.openObjectBrowser({
275
281
  mode: this.props.mode,
276
- currentPath: this.props.location.pathname,
282
+ currentPath: this.props.initialPath || this.props.location.pathname,
277
283
  propDataName: 'value',
278
284
  onSelectItem: (url, item) => {
279
285
  this.onChange(item);
280
286
  },
281
- selectableTypes: this.props.widgetOptions?.pattern_options
282
- ?.selectableTypes,
283
- maximumSelectionSize: this.props.widgetOptions?.pattern_options
284
- ?.maximumSelectionSize,
287
+ selectableTypes:
288
+ this.props.widgetOptions?.pattern_options?.selectableTypes ||
289
+ this.props.selectableTypes,
290
+ maximumSelectionSize:
291
+ this.props.widgetOptions?.pattern_options?.maximumSelectionSize ||
292
+ this.props.maximumSelectionSize,
285
293
  });
286
294
  };
287
295
 
@@ -349,7 +357,8 @@ export class ObjectBrowserWidgetComponent extends Component {
349
357
 
350
358
  {items.length === 0 && this.props.mode === 'multiple' && (
351
359
  <div className="placeholder" ref={this.placeholderRef}>
352
- {this.props.intl.formatMessage(messages.placeholder)}
360
+ {this.props.placeholder ??
361
+ this.props.intl.formatMessage(messages.placeholder)}
353
362
  </div>
354
363
  )}
355
364
  {this.props.allowExternals &&
@@ -359,9 +368,10 @@ export class ObjectBrowserWidgetComponent extends Component {
359
368
  onKeyDown={this.onKeyDownManualLink}
360
369
  onChange={this.onManualLinkInput}
361
370
  value={this.state.manualLinkInput}
362
- placeholder={this.props.intl.formatMessage(
363
- messages.placeholder,
364
- )}
371
+ placeholder={
372
+ this.props.placeholder ??
373
+ this.props.intl.formatMessage(messages.placeholder)
374
+ }
365
375
  />
366
376
  )}
367
377
  </div>
@@ -18,8 +18,6 @@ import { getNavigation } from '@plone/volto/actions';
18
18
  import { Header, Logo } from '@eeacms/volto-eea-design-system/ui';
19
19
  import { usePrevious } from '@eeacms/volto-eea-design-system/helpers';
20
20
  import { find } from 'lodash';
21
- import WhiteLogoImage from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/logo/eea-white.svg';
22
- import LogoImage from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/eea-logo.svg';
23
21
  import globeIcon from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/global-line.svg';
24
22
  import eeaFlag from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/eea.png';
25
23
 
@@ -29,22 +27,30 @@ import { BodyClass } from '@plone/volto/helpers';
29
27
 
30
28
  import cx from 'classnames';
31
29
 
30
+ function removeTrailingSlash(path) {
31
+ return path.replace(/\/+$/, '');
32
+ }
33
+
32
34
  /**
33
35
  * EEA Specific Header component.
34
36
  */
35
- const EEAHeader = ({ pathname, token, items, history }) => {
37
+ const EEAHeader = ({ pathname, token, items, history, subsite }) => {
36
38
  const currentLang = useSelector((state) => state.intl.locale);
37
39
  const translations = useSelector(
38
40
  (state) => state.content.data?.['@components']?.translations?.items,
39
41
  );
40
42
 
41
43
  const router_pathname = useSelector((state) => {
42
- return state.router?.location?.pathname || '';
44
+ return removeTrailingSlash(state.router?.location?.pathname) || '';
43
45
  });
44
46
 
47
+ const isSubsite = subsite?.['@type'] === 'Subsite';
48
+
45
49
  const isHomePageInverse = useSelector((state) => {
46
50
  const layout = state.content?.data?.layout;
47
- const has_home_layout = layout === 'homepage_inverse_view';
51
+ const has_home_layout =
52
+ layout === 'homepage_inverse_view' ||
53
+ (__CLIENT__ && document.body.classList.contains('homepage-inverse'));
48
54
  return (
49
55
  has_home_layout &&
50
56
  (pathname === router_pathname || router_pathname.endsWith('/edit'))
@@ -52,6 +58,8 @@ const EEAHeader = ({ pathname, token, items, history }) => {
52
58
  });
53
59
 
54
60
  const { eea } = config.settings;
61
+ const headerOpts = eea.headerOpts || {};
62
+ const { logo, logoWhite } = headerOpts || {};
55
63
  const width = useSelector((state) => state.screen?.width);
56
64
  const dispatch = useDispatch();
57
65
  const previousToken = usePrevious(token);
@@ -116,82 +124,94 @@ const EEAHeader = ({ pathname, token, items, history }) => {
116
124
  </Header.TopDropdownMenu>
117
125
  </Header.TopItem>
118
126
 
119
- <Header.TopItem>
127
+ {!!headerOpts.partnerLinks && (
128
+ <Header.TopItem>
129
+ <Header.TopDropdownMenu
130
+ id="theme-sites"
131
+ text={headerOpts.partnerLinks.title}
132
+ viewportWidth={width}
133
+ >
134
+ <div className="wrapper">
135
+ {headerOpts.partnerLinks.links.map((item, index) => (
136
+ <Dropdown.Item key={index}>
137
+ <a
138
+ href={item.href}
139
+ className="site"
140
+ target="_blank"
141
+ rel="noreferrer"
142
+ >
143
+ {item.title}
144
+ </a>
145
+ </Dropdown.Item>
146
+ ))}
147
+ </div>
148
+ </Header.TopDropdownMenu>
149
+ </Header.TopItem>
150
+ )}
151
+
152
+ {config.settings.isMultilingual && (
120
153
  <Header.TopDropdownMenu
121
- id="theme-sites"
122
- text={eea.globalHeaderPartnerLinks.title}
154
+ id="language-switcher"
155
+ className="item"
156
+ text={`${language.toUpperCase()}`}
157
+ mobileText={`${language.toUpperCase()}`}
158
+ icon={
159
+ <Image src={globeIcon} alt="language dropdown globe icon"></Image>
160
+ }
123
161
  viewportWidth={width}
124
162
  >
125
- <div className="wrapper">
126
- {eea.globalHeaderPartnerLinks.links.map((item, index) => (
127
- <Dropdown.Item key={index}>
128
- <a
129
- href={item.href}
130
- className="site"
131
- target="_blank"
132
- rel="noreferrer"
133
- >
134
- {item.title}
135
- </a>
136
- </Dropdown.Item>
163
+ <ul
164
+ className="wrapper language-list"
165
+ role="listbox"
166
+ aria-label="language switcher"
167
+ >
168
+ {eea.languages.map((item, index) => (
169
+ <Dropdown.Item
170
+ as="li"
171
+ key={index}
172
+ text={
173
+ <span>
174
+ {item.name}
175
+ <span className="country-code">
176
+ {item.code.toUpperCase()}
177
+ </span>
178
+ </span>
179
+ }
180
+ onClick={() => {
181
+ const translation = find(translations, {
182
+ language: item.code,
183
+ });
184
+ const to = translation
185
+ ? flattenToAppURL(translation['@id'])
186
+ : `/${item.code}`;
187
+ setLanguage(item.code);
188
+ history.push(to);
189
+ }}
190
+ ></Dropdown.Item>
137
191
  ))}
138
- </div>
192
+ </ul>
139
193
  </Header.TopDropdownMenu>
140
- </Header.TopItem>
141
-
142
- <Header.TopDropdownMenu
143
- id="language-switcher"
144
- className="item"
145
- text={`${language.toUpperCase()}`}
146
- mobileText={`${language.toUpperCase()}`}
147
- icon={
148
- <Image src={globeIcon} alt="language dropdown globe icon"></Image>
149
- }
150
- viewportWidth={width}
151
- >
152
- <ul
153
- className="wrapper language-list"
154
- role="listbox"
155
- aria-label="language switcher"
156
- >
157
- {eea.languages.map((item, index) => (
158
- <Dropdown.Item
159
- as="li"
160
- key={index}
161
- text={
162
- <span>
163
- {item.name}
164
- <span className="country-code">
165
- {item.code.toUpperCase()}
166
- </span>
167
- </span>
168
- }
169
- onClick={() => {
170
- const translation = find(translations, {
171
- language: item.code,
172
- });
173
- const to = translation
174
- ? flattenToAppURL(translation['@id'])
175
- : `/${item.code}`;
176
- setLanguage(item.code);
177
- history.push(to);
178
- }}
179
- ></Dropdown.Item>
180
- ))}
181
- </ul>
182
- </Header.TopDropdownMenu>
194
+ )}
183
195
  </Header.TopHeader>
184
196
  <Header.Main
185
197
  pathname={pathname}
186
198
  inverted={isHomePageInverse ? true : false}
187
199
  transparency={isHomePageInverse ? true : false}
188
200
  logo={
189
- <Logo
190
- src={isHomePageInverse ? WhiteLogoImage : LogoImage}
191
- title={eea.websiteTitle}
192
- alt={eea.organisationName}
193
- url={eea.logoTargetUrl}
194
- />
201
+ <div {...(isSubsite ? { className: 'logo-wrapper' } : {})}>
202
+ <Logo
203
+ src={isHomePageInverse ? logoWhite : logo}
204
+ title={eea.websiteTitle}
205
+ alt={eea.organisationName}
206
+ url={eea.logoTargetUrl}
207
+ />
208
+
209
+ {!!subsite && subsite.title && (
210
+ <UniversalLink item={subsite} className="subsite-logo">
211
+ {subsite.title}
212
+ </UniversalLink>
213
+ )}
214
+ </div>
195
215
  }
196
216
  menuItems={items}
197
217
  renderGlobalMenuItem={(item, { onClick }) => (
@@ -231,6 +251,7 @@ export default compose(
231
251
  (state) => ({
232
252
  token: state.userSession.token,
233
253
  items: state.navigation.items,
254
+ subsite: state.content.data?.['@components']?.subsite,
234
255
  }),
235
256
  { getNavigation },
236
257
  ),
package/src/index.js CHANGED
@@ -4,6 +4,7 @@ import installCustomTitle from '@eeacms/volto-eea-website-theme/components/manag
4
4
  import CustomCSS from '@eeacms/volto-eea-website-theme/components/theme/CustomCSS/CustomCSS';
5
5
  import DraftBackground from '@eeacms/volto-eea-website-theme/components/theme/DraftBackground/DraftBackground';
6
6
  import { TokenWidget } from '@eeacms/volto-eea-website-theme/components/theme/Widgets/TokenWidget';
7
+ import SubsiteClass from './components/theme/SubsiteClass';
7
8
  import HomePageView from '@eeacms/volto-eea-website-theme/components/theme/Homepage/HomePageView';
8
9
  import HomePageInverseView from '@eeacms/volto-eea-website-theme/components/theme/Homepage/HomePageInverseView';
9
10
  import { Icon } from '@plone/volto/components';
@@ -81,6 +82,10 @@ const applyConfig = (config) => {
81
82
  match: '',
82
83
  component: DraftBackground,
83
84
  },
85
+ {
86
+ match: '',
87
+ component: SubsiteClass,
88
+ },
84
89
  ];
85
90
 
86
91
  if (config.settings.slate) {
@@ -0,0 +1,7 @@
1
+ @columnBlocksWrapperPaddingTop: 0;
2
+ @columnBlocksWrapperPaddingBottom: 0;
3
+
4
+
5
+ // Column grid
6
+ @columnGridMarginTop: 1rem;
7
+ @columnGridMarginBottom: 1rem;
@@ -0,0 +1,2 @@
1
+ ### Site theme
2
+ - Add here variables and overrides
@@ -119,7 +119,7 @@
119
119
  @themesFolder: '~volto-themes';
120
120
 
121
121
  /* Path to site override folder */
122
- @siteFolder: '~@eeacms/volto-eea-website-theme/../theme';
122
+ @siteFolder: '~@eeacms/volto-eea-website-theme/../theme/site';
123
123
 
124
124
  /*******************************
125
125
  Import Theme