diff --git a/CHANGELOG.md b/CHANGELOG.md index d38949a775..d3a5d7f9fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 0.5.0 (unreleased) +### Added +- Added pastanaga theme package @jaroel, @robgietema +- Added registry based controlpanels @robgietema + ## 0.4.0 (2017-05-03) ### Added - Adding tiles @robgietema diff --git a/locales/de/LC_MESSAGES/plone-react.po b/locales/de/LC_MESSAGES/plone-react.po index 7e4e48c58f..1ae9687688 100644 --- a/locales/de/LC_MESSAGES/plone-react.po +++ b/locales/de/LC_MESSAGES/plone-react.po @@ -68,7 +68,6 @@ msgstr "Standardmäßig werden die Berechtigungen von einem Ordner auf die in ih #: components/manage/Contents/ContentsUploadModal #: components/manage/Delete/Delete -#: components/manage/Form/Form #: components/manage/Form/ModalForm #: components/manage/Sharing/Sharing msgid "Cancel" @@ -93,10 +92,18 @@ msgstr "Statusänderung" msgid "Changes saved" msgstr "Änderungen gespeichert" +#: components/manage/Controlpanels/Controlpanel +msgid "Changes saved." +msgstr "" + #: components/manage/Diff/Diff msgid "Compare" msgstr "Vergleichen" +#: components/manage/Controlpanels/Controlpanels +msgid "Configuration area for Plone and add-on Products." +msgstr "" + #: components/manage/Preferences/ChangePassword msgid "Confirm password" msgstr "Passwort bestätigen" @@ -446,7 +453,6 @@ msgstr "Durch diese Version ersetzen" msgid "Rights" msgstr "Rechte" -#: components/manage/Form/Form #: components/manage/Form/ModalForm #: components/manage/Sharing/Sharing msgid "Save" @@ -505,6 +511,11 @@ msgstr "Website-Administrator" msgid "Site Map" msgstr "Übersicht" +#: components/manage/Controlpanels/Controlpanels +#: components/manage/Toolbar/Toolbar +msgid "Site Setup" +msgstr "" + #: components/manage/Contents/Contents #: components/theme/View/TabularView msgid "State" diff --git a/locales/en/LC_MESSAGES/plone-react.po b/locales/en/LC_MESSAGES/plone-react.po index b67784440d..28a6d5091d 100644 --- a/locales/en/LC_MESSAGES/plone-react.po +++ b/locales/en/LC_MESSAGES/plone-react.po @@ -53,7 +53,6 @@ msgstr "" #: components/manage/Contents/ContentsUploadModal #: components/manage/Delete/Delete -#: components/manage/Form/Form #: components/manage/Form/ModalForm #: components/manage/Sharing/Sharing msgid "Cancel" @@ -78,10 +77,18 @@ msgstr "" msgid "Changes saved" msgstr "" +#: components/manage/Controlpanels/Controlpanel +msgid "Changes saved." +msgstr "" + #: components/manage/Diff/Diff msgid "Compare" msgstr "" +#: components/manage/Controlpanels/Controlpanels +msgid "Configuration area for Plone and add-on Products." +msgstr "" + #: components/manage/Preferences/ChangePassword msgid "Confirm password" msgstr "" @@ -431,7 +438,6 @@ msgstr "" msgid "Rights" msgstr "" -#: components/manage/Form/Form #: components/manage/Form/ModalForm #: components/manage/Sharing/Sharing msgid "Save" @@ -490,6 +496,11 @@ msgstr "" msgid "Site Map" msgstr "" +#: components/manage/Controlpanels/Controlpanels +#: components/manage/Toolbar/Toolbar +msgid "Site Setup" +msgstr "" + #: components/manage/Contents/Contents #: components/theme/View/TabularView msgid "State" diff --git a/locales/nl/LC_MESSAGES/plone-react.po b/locales/nl/LC_MESSAGES/plone-react.po index 4d354f8c9a..e234079391 100644 --- a/locales/nl/LC_MESSAGES/plone-react.po +++ b/locales/nl/LC_MESSAGES/plone-react.po @@ -72,7 +72,6 @@ msgstr "Standaard worden de rechten overgenomen van de bovenliggende map. Als u #: components/manage/Contents/ContentsUploadModal #: components/manage/Delete/Delete -#: components/manage/Form/Form #: components/manage/Form/ModalForm #: components/manage/Sharing/Sharing msgid "Cancel" @@ -97,10 +96,18 @@ msgstr "Status wijzigen" msgid "Changes saved" msgstr "Wijzigingen zijn opgeslagen" +#: components/manage/Controlpanels/Controlpanel +msgid "Changes saved." +msgstr "" + #: components/manage/Diff/Diff msgid "Compare" msgstr "Vergelijken" +#: components/manage/Controlpanels/Controlpanels +msgid "Configuration area for Plone and add-on Products." +msgstr "" + #: components/manage/Preferences/ChangePassword msgid "Confirm password" msgstr "Wachtwoord bevestigen" @@ -450,7 +457,6 @@ msgstr "Deze rivisie herstellen" msgid "Rights" msgstr "Rechten" -#: components/manage/Form/Form #: components/manage/Form/ModalForm #: components/manage/Sharing/Sharing msgid "Save" @@ -509,6 +515,11 @@ msgstr "Sitebeheerder" msgid "Site Map" msgstr "Sitemap" +#: components/manage/Controlpanels/Controlpanels +#: components/manage/Toolbar/Toolbar +msgid "Site Setup" +msgstr "" + #: components/manage/Contents/Contents #: components/theme/View/TabularView msgid "State" diff --git a/locales/plone-react.pot b/locales/plone-react.pot index c5e13d93dc..628822ab7b 100644 --- a/locales/plone-react.pot +++ b/locales/plone-react.pot @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Plone\n" -"POT-Creation-Date: 2017-04-30T16:20:43.407Z\n" +"POT-Creation-Date: 2017-09-23T14:11:57.276Z\n" "Last-Translator: Plone i18n \n" "Language-Team: Plone i18n \n" "MIME-Version: 1.0\n" @@ -65,7 +65,6 @@ msgstr "" #: components/manage/Contents/ContentsUploadModal #: components/manage/Delete/Delete -#: components/manage/Form/Form #: components/manage/Form/ModalForm #: components/manage/Sharing/Sharing # defaultMessage: Cancel @@ -95,11 +94,21 @@ msgstr "" msgid "Changes saved" msgstr "" +#: components/manage/Controlpanels/Controlpanel +# defaultMessage: Changes saved. +msgid "Changes saved." +msgstr "" + #: components/manage/Diff/Diff # defaultMessage: Compare msgid "Compare" msgstr "" +#: components/manage/Controlpanels/Controlpanels +# defaultMessage: Configuration area for Plone and add-on Products. +msgid "Configuration area for Plone and add-on Products." +msgstr "" + #: components/manage/Preferences/ChangePassword # defaultMessage: Confirm password msgid "Confirm password" @@ -529,7 +538,6 @@ msgstr "" msgid "Rights" msgstr "" -#: components/manage/Form/Form #: components/manage/Form/ModalForm #: components/manage/Sharing/Sharing # defaultMessage: Save @@ -602,6 +610,12 @@ msgstr "" msgid "Site Map" msgstr "" +#: components/manage/Controlpanels/Controlpanels +#: components/manage/Toolbar/Toolbar +# defaultMessage: Site Setup +msgid "Site Setup" +msgstr "" + #: components/manage/Contents/Contents #: components/theme/View/TabularView # defaultMessage: State diff --git a/package.json b/package.json index 33a17b17bf..e028800524 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,7 @@ "babel-preset-stage-0": "6.24.1", "babel-register": "6.26.0", "babel-runtime": "6.26.0", - "body-parser": "1.18.1", + "body-parser": "1.18.2", "client-sessions": "0.8.0", "compression": "1.7.0", "copy-webpack-plugin": "4.0.1", @@ -226,7 +226,7 @@ "concurrently": "3.5.0", "css-loader": "0.28.7", "dirty-chai": "2.0.1", - "eslint": "4.7.1", + "eslint": "4.7.2", "eslint-config-airbnb": "15.1.0", "eslint-loader": "1.9.0", "eslint-plugin-import": "2.7.0", diff --git a/src/actions/controlpanel/controlpanel.js b/src/actions/controlpanel/controlpanel.js new file mode 100644 index 0000000000..a9aa8c3a8c --- /dev/null +++ b/src/actions/controlpanel/controlpanel.js @@ -0,0 +1,36 @@ +/** + * Controlpanel actions. + * @module actions/controlpanel/controlpanel + */ + +import { + EDIT_CONTROLPANEL, + GET_CONTROLPANEL, +} from '../../constants/ActionTypes'; + +/** + * Get controlpanel function. + * @function getControlpanel + * @param {id} id Controlpanel id. + * @returns {Object} Get controlpanel action. + */ +export function getControlpanel(id) { + return { + type: GET_CONTROLPANEL, + promise: api => api.get(`/@controlpanels/${id}`), + }; +} + +/** + * Edit controlpanel function. + * @function editControlpanel + * @param {string} url Controlpanel url. + * @param {Object} data Controlpanel data. + * @returns {Object} Edit controlpanel action. + */ +export function editControlpanel(url, data) { + return { + type: EDIT_CONTROLPANEL, + promise: api => api.patch(url, { data }), + }; +} diff --git a/src/actions/controlpanel/controlpanel.test.js b/src/actions/controlpanel/controlpanel.test.js new file mode 100644 index 0000000000..cc01737053 --- /dev/null +++ b/src/actions/controlpanel/controlpanel.test.js @@ -0,0 +1,40 @@ +import { editControlpanel, getControlpanel } from './controlpanel'; +import { + EDIT_CONTROLPANEL, + GET_CONTROLPANEL, +} from '../../constants/ActionTypes'; + +describe('Controlpanel action', () => { + describe('getControlpanel', () => { + it('should create an action to get the controlpanel', () => { + const url = 'my-url'; + const action = getControlpanel(url); + + expect(action.type).toEqual(GET_CONTROLPANEL); + + const apiMock = { + get: jest.fn(), + }; + action.promise(apiMock); + + expect(apiMock.get).toBeCalledWith(`/@controlpanels/${url}`); + }); + }); + + describe('editControlpanel', () => { + it('should create an action to edit a controlpanel', () => { + const url = 'http://localhost'; + const data = 'Hello World!'; + const action = editControlpanel(url, data); + + expect(action.type).toEqual(EDIT_CONTROLPANEL); + + const apiMock = { + patch: jest.fn(), + }; + action.promise(apiMock); + + expect(apiMock.patch).toBeCalledWith(url, { data }); + }); + }); +}); diff --git a/src/actions/controlpanels/controlpanels.js b/src/actions/controlpanels/controlpanels.js new file mode 100644 index 0000000000..4f80f94e7b --- /dev/null +++ b/src/actions/controlpanels/controlpanels.js @@ -0,0 +1,18 @@ +/** + * Controlpanels actions. + * @module actions/controlpanels/controlpanels + */ + +import { GET_CONTROLPANELS } from '../../constants/ActionTypes'; + +/** + * Get controlpanels function. + * @function getControlpanels + * @returns {Object} Get controlpanels action. + */ +export default function getControlpanels() { + return { + type: GET_CONTROLPANELS, + promise: api => api.get('/@controlpanels'), + }; +} diff --git a/src/actions/controlpanels/controlpanels.test.js b/src/actions/controlpanels/controlpanels.test.js new file mode 100644 index 0000000000..aad1c4b9aa --- /dev/null +++ b/src/actions/controlpanels/controlpanels.test.js @@ -0,0 +1,19 @@ +import getControlpanels from './controlpanels'; +import { GET_CONTROLPANELS } from '../../constants/ActionTypes'; + +describe('Controlpanels action', () => { + describe('getControlpanels', () => { + it('should create an action to get the controlpanels', () => { + const action = getControlpanels(); + + expect(action.type).toEqual(GET_CONTROLPANELS); + + const apiMock = { + get: jest.fn(), + }; + action.promise(apiMock); + + expect(apiMock.get).toBeCalledWith('/@controlpanels'); + }); + }); +}); diff --git a/src/actions/index.js b/src/actions/index.js index fb6e140d21..0cc61d0fea 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -14,6 +14,8 @@ export { orderContent, sortContent, } from './content/content'; +export { editControlpanel, getControlpanel } from './controlpanel/controlpanel'; +export getControlpanels from './controlpanels/controlpanels'; export getDiff from './diff/diff'; export { getHistory, revertHistory } from './history/history'; export { addMessage, removeMessage } from './messages/messages'; diff --git a/src/components/index.js b/src/components/index.js index e86424adc3..282083034b 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -42,6 +42,8 @@ export ContentsPropertiesModal from './manage/Contents/ContentsPropertiesModal'; export ContentsRenameModal from './manage/Contents/ContentsRenameModal'; export ContentsWorkflowModal from './manage/Contents/ContentsWorkflowModal'; export ContentsTagsModal from './manage/Contents/ContentsTagsModal'; +export Controlpanel from './manage/Controlpanels/Controlpanel'; +export Controlpanels from './manage/Controlpanels/Controlpanels'; export Delete from './manage/Delete/Delete'; export Diff from './manage/Diff/Diff'; export DiffField from './manage/Diff/DiffField'; diff --git a/src/components/manage/Controlpanels/Controlpanel.jsx b/src/components/manage/Controlpanels/Controlpanel.jsx new file mode 100644 index 0000000000..0101e83247 --- /dev/null +++ b/src/components/manage/Controlpanels/Controlpanel.jsx @@ -0,0 +1,178 @@ +/** + * Controlpanel component. + * @module components/manage/Controlpanels/Controlpanel + */ + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { browserHistory } from 'react-router'; +import { bindActionCreators } from 'redux'; +import Helmet from 'react-helmet'; +import { Link } from 'react-router'; +import { Icon, Button } from 'semantic-ui-react'; +import { + FormattedMessage, + defineMessages, + injectIntl, + intlShape, +} from 'react-intl'; + +import { Form } from '../../../components'; +import { + addMessage, + editControlpanel, + getControlpanel, +} from '../../../actions'; + +const messages = defineMessages({ + changesSaved: { + id: 'Changes saved.', + defaultMessage: 'Changes saved.', + }, +}); + +/** + * Controlpanel class. + * @class Controlpanel + * @extends Component + */ +@injectIntl +@connect( + (state, props) => ({ + controlpanel: state.controlpanel.controlpanel, + editRequest: state.controlpanel.edit, + id: props.params.id, + }), + dispatch => + bindActionCreators( + { addMessage, editControlpanel, getControlpanel }, + dispatch, + ), +) +export default class Controlpanel extends Component { + /** + * Property types. + * @property {Object} propTypes Property types. + * @static + */ + static propTypes = { + addMessage: PropTypes.func.isRequired, + editControlpanel: PropTypes.func.isRequired, + getControlpanel: PropTypes.func.isRequired, + id: PropTypes.string.isRequired, + editRequest: PropTypes.shape({ + loading: PropTypes.bool, + loaded: PropTypes.bool, + }).isRequired, + controlpanel: PropTypes.shape({ + '@id': PropTypes.string, + data: PropTypes.Object, + schema: PropTypes.Object, + title: PropTypes.string, + }), + intl: intlShape.isRequired, + }; + + /** + * Constructor + * @method constructor + * @param {Object} props Component properties + * @constructs Controlpanel + */ + constructor(props) { + super(props); + this.onCancel = this.onCancel.bind(this); + this.onSubmit = this.onSubmit.bind(this); + } + + /** + * Component will mount + * @method componentWillMount + * @returns {undefined} + */ + componentWillMount() { + this.props.getControlpanel(this.props.id); + } + + /** + * Component will receive props + * @method componentWillReceiveProps + * @param {Object} nextProps Next properties + * @returns {undefined} + */ + componentWillReceiveProps(nextProps) { + if (this.props.editRequest.loading && nextProps.editRequest.loaded) { + this.props.addMessage( + null, + this.props.intl.formatMessage(messages.changesSaved), + 'info', + ); + } + } + + /** + * Submit handler + * @method onSubmit + * @param {object} data Form data. + * @returns {undefined} + */ + onSubmit(data) { + this.props.editControlpanel(this.props.controlpanel['@id'], data); + } + + /** + * Cancel handler + * @method onCancel + * @returns {undefined} + */ + onCancel() { + browserHistory.goBack(); + } + + /** + * Site setup handler + * @method onSiteSetup + * @returns {undefined} + */ + onSiteSetup() { + browserHistory.push('/controlpanel'); + } + + /** + * Render method. + * @method render + * @returns {string} Markup for the component. + */ + render() { + if (this.props.controlpanel) { + return ( +
+ +
+ +
+
+

+ {this.props.controlpanel.title} +

+
+
+
+
+
+
+
+ ); + } + return
; + } +} diff --git a/src/components/manage/Controlpanels/Controlpanel.test.jsx b/src/components/manage/Controlpanels/Controlpanel.test.jsx new file mode 100644 index 0000000000..61a471192c --- /dev/null +++ b/src/components/manage/Controlpanels/Controlpanel.test.jsx @@ -0,0 +1,39 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import configureStore from 'redux-mock-store'; +import { Provider } from 'react-intl-redux'; + +import Controlpanel from './Controlpanel'; + +const mockStore = configureStore(); + +jest.mock('../Form/Form', () => jest.fn(() =>
)); + +describe('Controlpanel', () => { + it('renders a controlpanel component', () => { + const store = mockStore({ + controlpanel: { + controlpanel: { + '@id': 'http://localhost:8080/Plone/@controlpanels/date-and-time', + title: 'Date and Time', + schema: { + fieldsets: [], + properties: [], + }, + data: {}, + }, + }, + intl: { + locale: 'en', + messages: {}, + }, + }); + const component = renderer.create( + + + , + ); + const json = component.toJSON(); + expect(json).toMatchSnapshot(); + }); +}); diff --git a/src/components/manage/Controlpanels/Controlpanels.jsx b/src/components/manage/Controlpanels/Controlpanels.jsx new file mode 100644 index 0000000000..2c17ad1223 --- /dev/null +++ b/src/components/manage/Controlpanels/Controlpanels.jsx @@ -0,0 +1,119 @@ +/** + * Controlpanels component. + * @module components/manage/Controlpanels/Controlpanels + */ + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { Link } from 'react-router'; +import { bindActionCreators } from 'redux'; +import { filter, last, map, uniqBy } from 'lodash'; +import Helmet from 'react-helmet'; +import { Grid, Header, Icon, Segment } from 'semantic-ui-react'; +import Icons from '../../../constants/ControlpanelIcons'; +import { + FormattedMessage, + defineMessages, + injectIntl, + intlShape, +} from 'react-intl'; + +import { getControlpanels } from '../../../actions'; + +const messages = defineMessages({ + sitesetup: { + id: 'Site Setup', + defaultMessage: 'Site Setup', + }, +}); + +/** + * Controlpanels container class. + * @class Controlpanels + * @extends Component + */ +@injectIntl +@connect( + state => ({ + controlpanels: state.controlpanels.controlpanels, + }), + dispatch => bindActionCreators({ getControlpanels }, dispatch), +) +export default class Controlpanels extends Component { + /** + * Property types. + * @property {Object} propTypes Property types. + * @static + */ + static propTypes = { + getControlpanels: PropTypes.func.isRequired, + controlpanels: PropTypes.arrayOf( + PropTypes.shape({ + '@id': PropTypes.string, + group: PropTypes.string, + title: PropTypes.string, + }), + ).isRequired, + intl: intlShape.isRequired, + }; + + /** + * Component will mount + * @method componentWillMount + * @returns {undefined} + */ + componentWillMount() { + this.props.getControlpanels(); + } + + /** + * Render method. + * @method render + * @returns {string} Markup for the component. + */ + render() { + const groups = map(uniqBy(this.props.controlpanels, 'group'), 'group'); + const controlpanels = map(this.props.controlpanels, controlpanel => ({ + ...controlpanel, + id: last(controlpanel['@id'].split('/')), + })); + return ( +
+ +

+ +

+

+ +

+ {map(groups, group => ( +
+
+ {group} +
+ + + + {map(filter(controlpanels, { group }), controlpanel => ( + + +
+ + {controlpanel.title} +
+ +
+ ))} +
+
+
+
+ ))} +
+ ); + } +} diff --git a/src/components/manage/Controlpanels/Controlpanels.test.jsx b/src/components/manage/Controlpanels/Controlpanels.test.jsx new file mode 100644 index 0000000000..449ff057e4 --- /dev/null +++ b/src/components/manage/Controlpanels/Controlpanels.test.jsx @@ -0,0 +1,50 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import configureStore from 'redux-mock-store'; +import { Provider } from 'react-intl-redux'; + +import Controlpanels from './Controlpanels'; + +const mockStore = configureStore(); + +describe('Controlpanels', () => { + it('renders a controlpanels component', () => { + const store = mockStore({ + controlpanels: { + controlpanels: [ + { + '@id': 'http://localhost:8080/Plone/@controlpanels/date-and-time', + group: 'General', + title: 'Date and Time', + }, + { + '@id': 'http://localhost:8080/Plone/@controlpanels/lang', + group: 'General', + title: 'Language', + }, + { + '@id': 'http://localhost:8080/Plone/@controlpanels/editing', + group: 'Content', + title: 'Editing', + }, + { + '@id': 'http://localhost:8080/Plone/@controlpanels/security', + group: 'Security', + title: 'Security', + }, + ], + }, + intl: { + locale: 'en', + messages: {}, + }, + }); + const component = renderer.create( + + + , + ); + const json = component.toJSON(); + expect(json).toMatchSnapshot(); + }); +}); diff --git a/src/components/manage/Controlpanels/__snapshots__/Controlpanel.test.jsx.snap b/src/components/manage/Controlpanels/__snapshots__/Controlpanel.test.jsx.snap new file mode 100644 index 0000000000..916458b718 --- /dev/null +++ b/src/components/manage/Controlpanels/__snapshots__/Controlpanel.test.jsx.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Controlpanel renders a controlpanel component 1`] = ` +
+
+