From 2064bdbfb343d1d5dd4608f83ec2fe56125edc38 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 29 May 2020 13:28:34 -0400 Subject: [PATCH] abstracts sensitive data from public data within config and re-introduces functional conditions --- demo/collections/AllFields.js | 7 ++ demo/collections/Conditions.js | 61 ++-------- demo/payload.private.config.js | 7 ++ ...oad.config.js => payload.public.config.js} | 4 - demo/server.js | 15 ++- src/auth/baseFields.js | 6 +- src/client/api.js | 3 +- src/client/components/Routes.js | 3 +- src/client/components/data/User.js | 3 +- .../components/elements/Eyebrow/index.scss | 3 +- .../components/elements/Localizer/index.js | 5 +- src/client/components/elements/Nav/index.js | 3 +- .../forms/field-types/Checkbox/index.js | 4 +- .../forms/field-types/DateTime/index.js | 4 +- .../forms/field-types/Email/index.js | 4 +- .../forms/field-types/Flexible/index.js | 4 +- .../forms/field-types/Group/index.js | 4 +- .../forms/field-types/HiddenInput/index.js | 4 +- .../forms/field-types/Number/index.js | 4 +- .../forms/field-types/Password/index.js | 4 +- .../forms/field-types/Relationship/index.js | 8 +- .../forms/field-types/Repeater/index.js | 4 +- .../forms/field-types/Select/index.js | 4 +- .../forms/field-types/Text/index.js | 4 +- .../forms/field-types/Textarea/index.js | 4 +- .../forms/field-types/Upload/index.js | 4 +- .../components/forms/withCondition/index.js | 54 +++++++++ .../components/forms/withConditions/index.js | 112 ------------------ src/client/components/index.js | 2 +- .../components/modals/StayLoggedIn/index.js | 3 +- .../components/utilities/Locale/index.js | 3 +- .../components/views/CreateFirstUser/index.js | 3 +- .../components/views/Dashboard/index.js | 3 +- .../components/views/ForgotPassword/index.js | 3 +- src/client/components/views/Global/index.js | 3 +- src/client/components/views/Login/index.js | 3 +- src/client/components/views/Logout/index.js | 3 +- src/client/components/views/NotFound/index.js | 3 +- .../components/views/ResetPassword/index.js | 3 +- .../components/views/Unauthorized/index.js | 3 +- .../views/collections/Edit/Default.js | 3 +- .../views/collections/Edit/index.js | 3 +- .../views/collections/Edit/index.scss | 14 ++- .../components/views/collections/List/Cell.js | 3 +- .../views/collections/List/Default.js | 3 +- src/client/scss/vars.scss | 5 + src/fields/schemaMap.js | 2 +- src/index.js | 7 +- src/utilities/getConfig.js | 18 +++ src/webpack/getWebpackDevConfig.js | 6 +- 50 files changed, 211 insertions(+), 234 deletions(-) create mode 100644 demo/payload.private.config.js rename demo/{payload.config.js => payload.public.config.js} (97%) create mode 100644 src/client/components/forms/withCondition/index.js delete mode 100644 src/client/components/forms/withConditions/index.js create mode 100644 src/utilities/getConfig.js diff --git a/demo/collections/AllFields.js b/demo/collections/AllFields.js index 3a3a44a110..3f1c17186e 100644 --- a/demo/collections/AllFields.js +++ b/demo/collections/AllFields.js @@ -4,6 +4,13 @@ const AllFields = { singular: 'All Fields', plural: 'All Fields', }, + preview: (doc, token) => { + if (doc.text) { + return `http://localhost:3000/previewable-posts/${doc.text.value}?preview=true&token=${token}`; + } + + return null; + }, fields: [ { name: 'text', diff --git a/demo/collections/Conditions.js b/demo/collections/Conditions.js index d9dfd5dad2..b4fecf35ea 100644 --- a/demo/collections/Conditions.js +++ b/demo/collections/Conditions.js @@ -26,68 +26,31 @@ const Conditions = { type: 'text', label: 'Enable Test is checked', required: true, - conditions: { - 'enable-test': { - equals: true, - }, - }, + condition: (_, siblings) => siblings['enable-test'] && siblings['enable-test'].value === true, }, { name: 'or-condition', type: 'text', label: 'Number is greater than 20 OR enable-test is checked', required: true, - conditions: { - or: [ - { - number: { - greater_than: 20, - }, - }, - { - 'enable-test': { - equals: true, - }, - }, - ], + condition: (_, siblings) => { + if (siblings.number && siblings['enable-test']) { + return siblings.number.value > 20 || siblings['enable-test'].value === true; + } + return false; }, }, { name: 'nested-conditions', type: 'text', label: 'Number is either greater than 20 AND enable-test is checked, OR number is less than 20 and enable-test is NOT checked', - conditions: { - or: [ - { - and: [ - { - number: { - greater_than: 20, - }, - }, - { - 'enable-test': { - equals: true, - }, - }, - ], - }, - { - and: [ - { - number: { - less_than: 20, - }, - }, - { - 'enable-test': { - equals: false, - }, - }, - ], - }, - ], + condition: (_, siblings) => { + if (siblings.number && siblings['enable-test']) { + return (siblings.number.value > 20 && siblings['enable-test'].value === true) || (siblings.number.value < 20 && siblings['enable-test'].value === false); + } + + return false; }, }, ], diff --git a/demo/payload.private.config.js b/demo/payload.private.config.js new file mode 100644 index 0000000000..76dc985664 --- /dev/null +++ b/demo/payload.private.config.js @@ -0,0 +1,7 @@ +module.exports = { + email: { + provider: 'mock', + }, + secret: 'SECRET_KEY', + mongoURL: 'mongodb://localhost/payload', +}; diff --git a/demo/payload.config.js b/demo/payload.public.config.js similarity index 97% rename from demo/payload.config.js rename to demo/payload.public.config.js index a0116719dc..b91d59f78b 100644 --- a/demo/payload.config.js +++ b/demo/payload.public.config.js @@ -22,7 +22,6 @@ const NavigationRepeater = require('./globals/NavigationRepeater'); const GlobalWithPolicies = require('./globals/GlobalWithPolicies'); module.exports = { - secret: 'SECRET_KEY', admin: { user: 'admins', disable: false, @@ -75,9 +74,6 @@ module.exports = { fallback: true, }, productionGraphQLPlayground: false, - email: { - provider: 'mock', - }, components: { layout: { // Sidebar: path.resolve(__dirname, 'client/components/layout/Sidebar/index.js'), diff --git a/demo/server.js b/demo/server.js index b0f38a0e70..82f55e09b6 100644 --- a/demo/server.js +++ b/demo/server.js @@ -1,22 +1,27 @@ +const path = require('path'); const express = require('express'); const Payload = require('../src'); -const config = require('./payload.config'); +const publicConfig = require('./payload.public.config'); +const privateConfig = require('./payload.private.config'); const expressApp = express(); const payload = new Payload({ - config, + config: { + public: path.resolve(__dirname, 'payload.public.config.js'), + private: path.resolve(__dirname, 'payload.private.config.js'), + }, express: expressApp, }); exports.payload = payload; exports.start = (cb) => { - const server = expressApp.listen(config.port, async () => { - console.log(`listening on ${config.port}...`); + const server = expressApp.listen(publicConfig.port, async () => { + console.log(`listening on ${publicConfig.port}...`); if (cb) cb(); - if (config.email.provider === 'mock') { + if (privateConfig.email.provider === 'mock') { const creds = await payload.getMockEmailCredentials(); console.log(`Mock email account username: ${creds.user}`); console.log(`Mock email account password: ${creds.pass}`); diff --git a/src/auth/baseFields.js b/src/auth/baseFields.js index ae1dc4e837..e68d801b08 100644 --- a/src/auth/baseFields.js +++ b/src/auth/baseFields.js @@ -24,10 +24,8 @@ module.exports = [ name: 'apiKey', type: 'text', label: 'User API Key', - conditions: { - enableAPIKey: { - equals: true, - }, + condition: (_, siblings) => { + return siblings.enableAPIKey && siblings.enableAPIKey.value; }, }, ]; diff --git a/src/client/api.js b/src/client/api.js index 9331fb1d3d..68379924bf 100644 --- a/src/client/api.js +++ b/src/client/api.js @@ -1,7 +1,8 @@ import Cookies from 'universal-cookie'; import qs from 'qs'; +import config from 'payload/config'; -const { cookiePrefix } = PAYLOAD_CONFIG; +const { cookiePrefix } = config; const cookieTokenName = `${cookiePrefix}-token`; export const getJWTHeader = () => { diff --git a/src/client/components/Routes.js b/src/client/components/Routes.js index 0252bce0ba..ad2c852f16 100644 --- a/src/client/components/Routes.js +++ b/src/client/components/Routes.js @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import { Route, Switch, withRouter, Redirect, } from 'react-router-dom'; +import config from 'payload/config'; import List from './views/collections/List'; import { useUser } from './data/User'; import DefaultTemplate from './templates/Default'; @@ -21,7 +22,7 @@ import Loading from './elements/Loading'; const { admin: { user: userSlug }, routes, collections, globals, -} = PAYLOAD_CONFIG; +} = config; const Routes = () => { const [initialized, setInitialized] = useState(null); diff --git a/src/client/components/data/User.js b/src/client/components/data/User.js index 9193dc7f82..b8348a8561 100644 --- a/src/client/components/data/User.js +++ b/src/client/components/data/User.js @@ -5,6 +5,7 @@ import { useLocation, useHistory } from 'react-router-dom'; import jwt from 'jsonwebtoken'; import PropTypes from 'prop-types'; import Cookies from 'universal-cookie'; +import config from 'payload/config'; import { useModal } from '@trbl/react-modal'; import { requests } from '../../api'; import StayLoggedInModal from '../modals/StayLoggedIn'; @@ -20,7 +21,7 @@ const { admin, api, }, -} = PAYLOAD_CONFIG; +} = config; const cookieTokenName = `${cookiePrefix}-token`; diff --git a/src/client/components/elements/Eyebrow/index.scss b/src/client/components/elements/Eyebrow/index.scss index 3c0c85cdd4..8a803ea0c7 100644 --- a/src/client/components/elements/Eyebrow/index.scss +++ b/src/client/components/elements/Eyebrow/index.scss @@ -1,6 +1,7 @@ @import '../../../scss/styles.scss'; .eyebrow { + @include blur-bg; position: sticky; top: 0; z-index: 2; @@ -8,8 +9,6 @@ display: flex; align-items: center; justify-content: space-between; - background: rgba($color-background-gray, .8); - backdrop-filter: saturate(180%) blur(5px); @include mid-break { padding: 0 0 $baseline; diff --git a/src/client/components/elements/Localizer/index.js b/src/client/components/elements/Localizer/index.js index 950fd1f805..4328a8d8ad 100644 --- a/src/client/components/elements/Localizer/index.js +++ b/src/client/components/elements/Localizer/index.js @@ -1,6 +1,7 @@ -import React, { useState } from 'react'; +import React from 'react'; import { Link } from 'react-router-dom'; import qs from 'qs'; +import config from 'payload/config'; import { useLocale } from '../../utilities/Locale'; import { useSearchParams } from '../../utilities/SearchParams'; import Popup from '../Popup'; @@ -9,7 +10,7 @@ import './index.scss'; const baseClass = 'localizer'; -const { localization } = PAYLOAD_CONFIG; +const { localization } = config; const Localizer = () => { const locale = useLocale(); diff --git a/src/client/components/elements/Nav/index.js b/src/client/components/elements/Nav/index.js index 309bd5614d..ca21f1bd96 100644 --- a/src/client/components/elements/Nav/index.js +++ b/src/client/components/elements/Nav/index.js @@ -1,5 +1,6 @@ import React, { useState } from 'react'; import { NavLink, Link } from 'react-router-dom'; +import config from 'payload/config'; import { useUser } from '../../data/User'; import Chevron from '../../icons/Chevron'; import LogOut from '../../icons/LogOut'; @@ -19,7 +20,7 @@ const { routes: { admin, }, -} = PAYLOAD_CONFIG; +} = config; const Nav = () => { const { permissions } = useUser(); diff --git a/src/client/components/forms/field-types/Checkbox/index.js b/src/client/components/forms/field-types/Checkbox/index.js index 0d920f00ac..5f96fcaaf1 100644 --- a/src/client/components/forms/field-types/Checkbox/index.js +++ b/src/client/components/forms/field-types/Checkbox/index.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import useFieldType from '../../useFieldType'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; import Error from '../../Error'; import StyledCheckbox from './StyledCheckbox'; @@ -87,4 +87,4 @@ Checkbox.propTypes = { label: PropTypes.string, }; -export default withConditions(Checkbox); +export default withCondition(Checkbox); diff --git a/src/client/components/forms/field-types/DateTime/index.js b/src/client/components/forms/field-types/DateTime/index.js index bd8f7980df..36090500c5 100644 --- a/src/client/components/forms/field-types/DateTime/index.js +++ b/src/client/components/forms/field-types/DateTime/index.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import DatePicker from '../../../elements/DatePicker'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; import useFieldType from '../../useFieldType'; import Label from '../../Label'; import Error from '../../Error'; @@ -95,4 +95,4 @@ DateTime.propTypes = { style: PropTypes.shape({}), }; -export default withConditions(DateTime); +export default withCondition(DateTime); diff --git a/src/client/components/forms/field-types/Email/index.js b/src/client/components/forms/field-types/Email/index.js index 2bb7dc4803..64b9c3682a 100644 --- a/src/client/components/forms/field-types/Email/index.js +++ b/src/client/components/forms/field-types/Email/index.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; import useFieldType from '../../useFieldType'; import Label from '../../Label'; import Error from '../../Error'; @@ -108,4 +108,4 @@ Email.propTypes = { autoComplete: PropTypes.string, }; -export default withConditions(Email); +export default withCondition(Email); diff --git a/src/client/components/forms/field-types/Flexible/index.js b/src/client/components/forms/field-types/Flexible/index.js index 6199150036..eb0ce584f9 100644 --- a/src/client/components/forms/field-types/Flexible/index.js +++ b/src/client/components/forms/field-types/Flexible/index.js @@ -6,7 +6,7 @@ import { DragDropContext, Droppable } from 'react-beautiful-dnd'; import { useModal } from '@trbl/react-modal'; import { RowModifiedProvider, useRowModified } from '../../Form/RowModified'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; import Button from '../../../elements/Button'; import FormContext from '../../Form/Context'; import AddRowModal from './AddRowModal'; @@ -210,4 +210,4 @@ Flexible.propTypes = { fieldTypes: PropTypes.shape({}).isRequired, }; -export default withConditions(Flexible); +export default withCondition(Flexible); diff --git a/src/client/components/forms/field-types/Group/index.js b/src/client/components/forms/field-types/Group/index.js index 0cf440e1be..1ecdab2957 100644 --- a/src/client/components/forms/field-types/Group/index.js +++ b/src/client/components/forms/field-types/Group/index.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import RenderFields from '../../RenderFields'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; import './index.scss'; @@ -41,4 +41,4 @@ Group.propTypes = { fieldTypes: PropTypes.shape({}).isRequired, }; -export default withConditions(Group); +export default withCondition(Group); diff --git a/src/client/components/forms/field-types/HiddenInput/index.js b/src/client/components/forms/field-types/HiddenInput/index.js index 2911dcd033..b96ce31f64 100644 --- a/src/client/components/forms/field-types/HiddenInput/index.js +++ b/src/client/components/forms/field-types/HiddenInput/index.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import useFieldType from '../../useFieldType'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; const HiddenInput = (props) => { const { @@ -38,4 +38,4 @@ HiddenInput.propTypes = { defaultValue: PropTypes.string, }; -export default withConditions(HiddenInput); +export default withCondition(HiddenInput); diff --git a/src/client/components/forms/field-types/Number/index.js b/src/client/components/forms/field-types/Number/index.js index 9606e96bd5..1dd59eecb5 100644 --- a/src/client/components/forms/field-types/Number/index.js +++ b/src/client/components/forms/field-types/Number/index.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import useFieldType from '../../useFieldType'; import Label from '../../Label'; import Error from '../../Error'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; import './index.scss'; @@ -96,4 +96,4 @@ NumberField.propTypes = { label: PropTypes.string, }; -export default withConditions(NumberField); +export default withCondition(NumberField); diff --git a/src/client/components/forms/field-types/Password/index.js b/src/client/components/forms/field-types/Password/index.js index 5ac4b37acb..9a739eda00 100644 --- a/src/client/components/forms/field-types/Password/index.js +++ b/src/client/components/forms/field-types/Password/index.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import useFieldType from '../../useFieldType'; import Label from '../../Label'; import Error from '../../Error'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; import './index.scss'; @@ -92,4 +92,4 @@ Password.propTypes = { validate: PropTypes.func, }; -export default withConditions(Password); +export default withCondition(Password); diff --git a/src/client/components/forms/field-types/Relationship/index.js b/src/client/components/forms/field-types/Relationship/index.js index 5609338a4a..bd1a60b381 100644 --- a/src/client/components/forms/field-types/Relationship/index.js +++ b/src/client/components/forms/field-types/Relationship/index.js @@ -2,7 +2,8 @@ import React, { Component, useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import Cookies from 'universal-cookie'; import some from 'async-some'; -import withConditions from '../../withConditions'; +import config from 'payload/config'; +import withCondition from '../../withCondition'; import ReactSelect from '../../../elements/ReactSelect'; import useFieldType from '../../useFieldType'; import Label from '../../Label'; @@ -14,7 +15,8 @@ const cookies = new Cookies(); const { cookiePrefix, serverURL, routes: { api }, collections, -} = PAYLOAD_CONFIG; +} = config; + const cookieTokenName = `${cookiePrefix}-token`; const defaultError = 'Please make a selection.'; @@ -338,4 +340,4 @@ const RelationshipFieldType = (props) => { ); }; -export default withConditions(RelationshipFieldType); +export default withCondition(RelationshipFieldType); diff --git a/src/client/components/forms/field-types/Repeater/index.js b/src/client/components/forms/field-types/Repeater/index.js index 5667d48aac..5526b12834 100644 --- a/src/client/components/forms/field-types/Repeater/index.js +++ b/src/client/components/forms/field-types/Repeater/index.js @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; import { DragDropContext, Droppable } from 'react-beautiful-dnd'; import { RowModifiedProvider, useRowModified } from '../../Form/RowModified'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; import Button from '../../../elements/Button'; import FormContext from '../../Form/Context'; import DraggableSection from '../../DraggableSection'; @@ -162,4 +162,4 @@ Repeater.propTypes = { fieldTypes: PropTypes.shape({}).isRequired, }; -export default withConditions(Repeater); +export default withCondition(Repeater); diff --git a/src/client/components/forms/field-types/Select/index.js b/src/client/components/forms/field-types/Select/index.js index f9264ede34..e2de794599 100644 --- a/src/client/components/forms/field-types/Select/index.js +++ b/src/client/components/forms/field-types/Select/index.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; import ReactSelect from '../../../elements/ReactSelect'; import useFieldType from '../../useFieldType'; import Label from '../../Label'; @@ -153,4 +153,4 @@ Select.propTypes = { hasMany: PropTypes.bool, }; -export default withConditions(Select); +export default withCondition(Select); diff --git a/src/client/components/forms/field-types/Text/index.js b/src/client/components/forms/field-types/Text/index.js index 6f024a2e47..c56f3e239b 100644 --- a/src/client/components/forms/field-types/Text/index.js +++ b/src/client/components/forms/field-types/Text/index.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import useFieldType from '../../useFieldType'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; import Label from '../../Label'; import Error from '../../Error'; @@ -96,4 +96,4 @@ Text.propTypes = { label: PropTypes.string, }; -export default withConditions(Text); +export default withCondition(Text); diff --git a/src/client/components/forms/field-types/Textarea/index.js b/src/client/components/forms/field-types/Textarea/index.js index 487c523c5f..9d17a868a7 100644 --- a/src/client/components/forms/field-types/Textarea/index.js +++ b/src/client/components/forms/field-types/Textarea/index.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import useFieldType from '../../useFieldType'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; import Label from '../../Label'; import Error from '../../Error'; @@ -95,4 +95,4 @@ Textarea.propTypes = { placeholder: PropTypes.string, }; -export default withConditions(Textarea); +export default withCondition(Textarea); diff --git a/src/client/components/forms/field-types/Upload/index.js b/src/client/components/forms/field-types/Upload/index.js index 2b32d9f217..ccd08d0f2f 100644 --- a/src/client/components/forms/field-types/Upload/index.js +++ b/src/client/components/forms/field-types/Upload/index.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import useFieldType from '../../useFieldType'; -import withConditions from '../../withConditions'; +import withCondition from '../../withCondition'; import UploadMedia from '../../../modules/UploadMedia'; import './index.scss'; @@ -60,4 +60,4 @@ class Media extends Component { } } -export default withConditions(fieldType(Media, 'media', validate, error)); +export default withCondition(fieldType(Media, 'media', validate, error)); diff --git a/src/client/components/forms/withCondition/index.js b/src/client/components/forms/withCondition/index.js new file mode 100644 index 0000000000..798c77899f --- /dev/null +++ b/src/client/components/forms/withCondition/index.js @@ -0,0 +1,54 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import useForm from '../Form/useForm'; + +const withCondition = (Field) => { + const WithCondition = (props) => { + const { condition, name } = props; + const { fields } = useForm(); + + if (condition) { + let siblingFields = fields; + + // If this field is nested + // We can provide a list of sibling fields + if (name.indexOf('.') > 0) { + const parentFieldPath = name.substring(0, name.lastIndexOf('.') + 1); + siblingFields = Object.keys(fields).reduce((siblings, fieldKey) => { + if (fieldKey.indexOf(parentFieldPath) === 0) { + return { + ...siblings, + [fieldKey.replace(parentFieldPath, '')]: fields[fieldKey], + }; + } + + return siblings; + }, {}); + } + + const passesCondition = condition ? condition(fields, siblingFields) : true; + + if (passesCondition) { + return ; + } + + return null; + } + + return ; + }; + + WithCondition.defaultProps = { + condition: null, + }; + + WithCondition.propTypes = { + condition: PropTypes.func, + name: PropTypes.string.isRequired, + }; + + return WithCondition; +}; + +export default withCondition; diff --git a/src/client/components/forms/withConditions/index.js b/src/client/components/forms/withConditions/index.js deleted file mode 100644 index 24990d553d..0000000000 --- a/src/client/components/forms/withConditions/index.js +++ /dev/null @@ -1,112 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import useForm from '../Form/useForm'; - -const recursiveCheckCondition = (object, siblingFields) => Object.keys(object).every((key) => { - if (key.toLowerCase() === 'and') { - return object[key].every((andCondition) => { - const result = recursiveCheckCondition(andCondition, siblingFields); - return result; - }); - } - - if (key.toLowerCase() === 'or') { - return object[key].some((orCondition) => { - const result = recursiveCheckCondition(orCondition, siblingFields); - return result; - }); - } - - if (typeof object[key] === 'object') { - return Object.keys(object[key]).every((operator) => { - const value = siblingFields?.[key]?.value; - const valueToCompare = object[key][operator]; - - let result = false; - - if (operator === 'equals') { - result = value === valueToCompare; - } - - if (operator === 'not_equals') { - result = value !== valueToCompare; - } - - if (typeof value === 'number') { - if (operator === 'greater_than') { - result = value > valueToCompare; - } - - if (operator === 'greater_than_equals') { - result = value >= valueToCompare; - } - - if (operator === 'less_than') { - result = value < valueToCompare; - } - - if (operator === 'less_than_equals') { - result = value <= valueToCompare; - } - } - - return result; - }); - } - - return false; -}); - -const withConditions = (Field) => { - const WithConditions = (props) => { - const { conditions, name } = props; - const { fields } = useForm(); - - if (conditions) { - let siblingFields = fields; - - // If this field is nested - // We can provide a list of sibling fields - if (name.indexOf('.') > 0) { - const parentFieldPath = name.substring(0, name.lastIndexOf('.') + 1); - siblingFields = Object.keys(fields).reduce((siblings, fieldKey) => { - if (fieldKey.indexOf(parentFieldPath) === 0) { - return { - ...siblings, - [fieldKey.replace(parentFieldPath, '')]: fields[fieldKey], - }; - } - - return siblings; - }, {}); - } - - const passesConditions = recursiveCheckCondition(conditions, siblingFields); - - if (passesConditions) { - return ; - } - - return null; - } - - return ; - }; - - WithConditions.defaultProps = { - conditions: null, - }; - - WithConditions.propTypes = { - conditions: PropTypes.oneOfType([ - PropTypes.shape({}), - PropTypes.array, - ]), - name: PropTypes.string.isRequired, - }; - - return WithConditions; -}; - -export default withConditions; diff --git a/src/client/components/index.js b/src/client/components/index.js index 3db78019cd..04edf77f7c 100644 --- a/src/client/components/index.js +++ b/src/client/components/index.js @@ -4,6 +4,7 @@ import { BrowserRouter as Router } from 'react-router-dom'; import { ScrollInfoProvider } from '@trbl/react-scroll-info'; import { WindowInfoProvider } from '@trbl/react-window-info'; import { ModalProvider, ModalContainer } from '@trbl/react-modal'; +import config from 'payload/config'; import Loading from './elements/Loading'; import { SearchParamsProvider } from './utilities/SearchParams'; import { LocaleProvider } from './utilities/Locale'; @@ -14,7 +15,6 @@ import getCSSVariable from '../../utilities/getCSSVariable'; import '../scss/app.scss'; - const Index = () => { const windowInfoProps = {}; diff --git a/src/client/components/modals/StayLoggedIn/index.js b/src/client/components/modals/StayLoggedIn/index.js index 5d7a2047ec..b36054b5f2 100644 --- a/src/client/components/modals/StayLoggedIn/index.js +++ b/src/client/components/modals/StayLoggedIn/index.js @@ -2,13 +2,14 @@ import React from 'react'; import PropTypes from 'prop-types'; import { useHistory } from 'react-router-dom'; import { asModal } from '@trbl/react-modal'; +import config from 'payload/config'; import Button from '../../elements/Button'; import './index.scss'; const baseClass = 'stay-logged-in'; -const { routes: { admin } } = PAYLOAD_CONFIG; +const { routes: { admin } } = config; const StayLoggedInModal = (props) => { const { diff --git a/src/client/components/utilities/Locale/index.js b/src/client/components/utilities/Locale/index.js index 89d1287a3d..3723cc13a6 100644 --- a/src/client/components/utilities/Locale/index.js +++ b/src/client/components/utilities/Locale/index.js @@ -2,9 +2,10 @@ import React, { createContext, useContext, useState, useEffect, } from 'react'; import PropTypes from 'prop-types'; +import config from 'payload/config'; import { useSearchParams } from '../SearchParams'; -const { localization } = PAYLOAD_CONFIG; +const { localization } = config; const defaultLocale = (localization && localization.defaultLocale) ? localization.defaultLocale : 'en'; const Context = createContext({}); diff --git a/src/client/components/views/CreateFirstUser/index.js b/src/client/components/views/CreateFirstUser/index.js index 45600780bb..96e566d4a2 100644 --- a/src/client/components/views/CreateFirstUser/index.js +++ b/src/client/components/views/CreateFirstUser/index.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { useHistory } from 'react-router-dom'; +import config from 'payload/config'; import MinimalTemplate from '../../templates/Minimal'; import StatusList, { useStatusList } from '../../elements/Status'; import Form from '../../forms/Form'; @@ -13,7 +14,7 @@ import './index.scss'; const { admin: { user: userSlug }, collections, serverURL, routes: { admin, api }, -} = PAYLOAD_CONFIG; +} = config; const userConfig = collections.find(collection => collection.slug === userSlug); diff --git a/src/client/components/views/Dashboard/index.js b/src/client/components/views/Dashboard/index.js index 882223503a..39de0f83d2 100644 --- a/src/client/components/views/Dashboard/index.js +++ b/src/client/components/views/Dashboard/index.js @@ -1,5 +1,6 @@ import React, { useEffect } from 'react'; import { Link } from 'react-router-dom'; +import config from 'payload/config'; import { useStepNav } from '../../elements/StepNav'; import Eyebrow from '../../elements/Eyebrow'; @@ -9,7 +10,7 @@ const { routes: { admin, }, -} = PAYLOAD_CONFIG; +} = config; const baseClass = 'dashboard'; diff --git a/src/client/components/views/ForgotPassword/index.js b/src/client/components/views/ForgotPassword/index.js index f6cb70c743..d1064cc3bc 100644 --- a/src/client/components/views/ForgotPassword/index.js +++ b/src/client/components/views/ForgotPassword/index.js @@ -1,5 +1,6 @@ import React, { useState } from 'react'; import { Link } from 'react-router-dom'; +import config from 'payload/config'; import MinimalTemplate from '../../templates/Minimal'; import StatusList, { useStatusList } from '../../elements/Status'; import Form from '../../forms/Form'; @@ -19,7 +20,7 @@ const { admin, api, }, -} = PAYLOAD_CONFIG; +} = config; const ForgotPassword = () => { const { addStatus } = useStatusList(); diff --git a/src/client/components/views/Global/index.js b/src/client/components/views/Global/index.js index 591bf91fb1..5c372647dc 100644 --- a/src/client/components/views/Global/index.js +++ b/src/client/components/views/Global/index.js @@ -1,5 +1,6 @@ import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; +import config from 'payload/config'; import { useStepNav } from '../../elements/StepNav'; import usePayloadAPI from '../../../hooks/usePayloadAPI'; import Form from '../../forms/Form'; @@ -12,7 +13,7 @@ const { admin, api, }, -} = PAYLOAD_CONFIG; +} = config; const baseClass = 'global-edit'; diff --git a/src/client/components/views/Login/index.js b/src/client/components/views/Login/index.js index 0d5feb2063..fcab4d1e64 100644 --- a/src/client/components/views/Login/index.js +++ b/src/client/components/views/Login/index.js @@ -1,5 +1,6 @@ import React from 'react'; import { Link, useHistory } from 'react-router-dom'; +import config from 'payload/config'; import Logo from '../../graphics/Logo'; import MinimalTemplate from '../../templates/Minimal'; import StatusList, { useStatusList } from '../../elements/Status'; @@ -14,7 +15,7 @@ import './index.scss'; const baseClass = 'login'; -const { admin: { user: userSlug }, serverURL, routes: { admin, api } } = PAYLOAD_CONFIG; +const { admin: { user: userSlug }, serverURL, routes: { admin, api } } = config; const Login = () => { const { addStatus } = useStatusList(); diff --git a/src/client/components/views/Logout/index.js b/src/client/components/views/Logout/index.js index e51c368842..e95445109d 100644 --- a/src/client/components/views/Logout/index.js +++ b/src/client/components/views/Logout/index.js @@ -1,11 +1,12 @@ import React, { useEffect } from 'react'; +import config from 'payload/config'; import { useUser } from '../../data/User'; import Minimal from '../../templates/Minimal'; import Button from '../../elements/Button'; import './index.scss'; -const { routes: { admin } } = PAYLOAD_CONFIG; +const { routes: { admin } } = config; const baseClass = 'logout'; diff --git a/src/client/components/views/NotFound/index.js b/src/client/components/views/NotFound/index.js index e919ef5b4c..b6700cc661 100644 --- a/src/client/components/views/NotFound/index.js +++ b/src/client/components/views/NotFound/index.js @@ -1,9 +1,10 @@ import React, { useEffect } from 'react'; +import config from 'payload/config'; import Eyebrow from '../../elements/Eyebrow'; import { useStepNav } from '../../elements/StepNav'; import Button from '../../elements/Button'; -const { routes: { admin } } = PAYLOAD_CONFIG; +const { routes: { admin } } = config; const NotFound = () => { const { setStepNav } = useStepNav(); diff --git a/src/client/components/views/ResetPassword/index.js b/src/client/components/views/ResetPassword/index.js index 63623a5212..9b467060f2 100644 --- a/src/client/components/views/ResetPassword/index.js +++ b/src/client/components/views/ResetPassword/index.js @@ -1,5 +1,6 @@ import React from 'react'; import { Link, useHistory, useParams } from 'react-router-dom'; +import config from 'payload/config'; import StatusList from '../../elements/Status'; import Form from '../../forms/Form'; import Password from '../../forms/field-types/Password'; @@ -12,7 +13,7 @@ import HiddenInput from '../../forms/field-types/HiddenInput'; const baseClass = 'reset-password'; -const { admin: { user: userSlug }, serverURL, routes: { admin, api } } = PAYLOAD_CONFIG; +const { admin: { user: userSlug }, serverURL, routes: { admin, api } } = config; const ResetPassword = () => { const { token } = useParams(); diff --git a/src/client/components/views/Unauthorized/index.js b/src/client/components/views/Unauthorized/index.js index 330d184bd3..c521c68258 100644 --- a/src/client/components/views/Unauthorized/index.js +++ b/src/client/components/views/Unauthorized/index.js @@ -1,8 +1,9 @@ import React from 'react'; +import config from 'payload/config'; import Button from '../../elements/Button'; import MinimalTemplate from '../../templates/Minimal'; -const { routes: { admin } } = PAYLOAD_CONFIG; +const { routes: { admin } } = config; const Unauthorized = () => { return ( diff --git a/src/client/components/views/collections/Edit/Default.js b/src/client/components/views/collections/Edit/Default.js index bab04e7e0f..2b4bd0bfb8 100644 --- a/src/client/components/views/collections/Edit/Default.js +++ b/src/client/components/views/collections/Edit/Default.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { useRouteMatch } from 'react-router-dom'; import moment from 'moment'; +import config from 'payload/config'; import Eyebrow from '../../../elements/Eyebrow'; import Form from '../../../forms/Form'; import PreviewButton from '../../../elements/PreviewButton'; @@ -11,7 +12,7 @@ import * as fieldTypes from '../../../forms/field-types'; import './index.scss'; -const { serverURL, routes: { api } } = PAYLOAD_CONFIG; +const { serverURL, routes: { api } } = config; const baseClass = 'collection-edit'; diff --git a/src/client/components/views/collections/Edit/index.js b/src/client/components/views/collections/Edit/index.js index 98e8c82430..8f23c7441d 100644 --- a/src/client/components/views/collections/Edit/index.js +++ b/src/client/components/views/collections/Edit/index.js @@ -1,13 +1,14 @@ import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; import { useRouteMatch, useHistory } from 'react-router-dom'; +import config from 'payload/config'; import { useStepNav } from '../../../elements/StepNav'; import usePayloadAPI from '../../../../hooks/usePayloadAPI'; import RenderCustomComponent from '../../../utilities/RenderCustomComponent'; import DefaultEdit from './Default'; -const { serverURL, routes: { admin, api } } = PAYLOAD_CONFIG; +const { serverURL, routes: { admin, api } } = config; const EditView = (props) => { const { params: { id } = {} } = useRouteMatch(); diff --git a/src/client/components/views/collections/Edit/index.scss b/src/client/components/views/collections/Edit/index.scss index 2336ce5eec..fbe7bb4bc0 100644 --- a/src/client/components/views/collections/Edit/index.scss +++ b/src/client/components/views/collections/Edit/index.scss @@ -5,6 +5,7 @@ &__form { display: flex; + align-items: flex-start; } &__main { @@ -40,17 +41,26 @@ } &__sidebar { - padding-left: base(2); padding-bottom: base(1); width: base(16); display: flex; flex-direction: column; } + &__document-actions, + &__meta { + padding-left: base(2); + } + + &__document-actions { + @include blur-bg; + position: sticky; + top: 0; + } + &__meta { margin: auto 0 0 0; list-style: none; - padding: 0; li { margin-bottom: base(.5); diff --git a/src/client/components/views/collections/List/Cell.js b/src/client/components/views/collections/List/Cell.js index 29be45e9d2..d9c025ff01 100644 --- a/src/client/components/views/collections/List/Cell.js +++ b/src/client/components/views/collections/List/Cell.js @@ -2,9 +2,10 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from 'react-router-dom'; import moment from 'moment'; +import config from 'payload/config'; import RenderCustomComponent from '../../../utilities/RenderCustomComponent'; -const { routes: { admin } } = PAYLOAD_CONFIG; +const { routes: { admin } } = config; const DefaultCell = (props) => { const { diff --git a/src/client/components/views/collections/List/Default.js b/src/client/components/views/collections/List/Default.js index 581848ca9f..1e915d32a4 100644 --- a/src/client/components/views/collections/List/Default.js +++ b/src/client/components/views/collections/List/Default.js @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; import queryString from 'qs'; import PropTypes from 'prop-types'; +import config from 'payload/config'; import usePayloadAPI from '../../../../hooks/usePayloadAPI'; import Eyebrow from '../../../elements/Eyebrow'; import Paginator from '../../../elements/Paginator'; @@ -14,7 +15,7 @@ import Cell from './Cell'; import './index.scss'; -const { serverURL, routes: { api, admin } } = PAYLOAD_CONFIG; +const { serverURL, routes: { api, admin } } = config; const baseClass = 'collection-list'; diff --git a/src/client/scss/vars.scss b/src/client/scss/vars.scss index 40fdc81525..338b6178d3 100644 --- a/src/client/scss/vars.scss +++ b/src/client/scss/vars.scss @@ -85,6 +85,11 @@ $style-stroke-width-m : 2px; } } +@mixin blur-bg { + background: rgba($color-background-gray, .8); + backdrop-filter: saturate(180%) blur(5px); +} + ////////////////////////////// // IMPORT OVERRIDES ////////////////////////////// diff --git a/src/fields/schemaMap.js b/src/fields/schemaMap.js index fb74b669c1..b6ecec1393 100644 --- a/src/fields/schemaMap.js +++ b/src/fields/schemaMap.js @@ -5,7 +5,7 @@ const formatBaseSchema = (field) => { hide: field.hidden === 'api' || field.hidden === true, localized: field.localized || false, unique: field.unique || false, - required: (field.required && !field.localized && !field.hidden && !field.conditions) || false, + required: (field.required && !field.localized && !field.hidden && !field.condition) || false, default: field.defaultValue || undefined, }; }; diff --git a/src/index.js b/src/index.js index 0732846acb..30b0e72077 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ require('isomorphic-fetch'); const express = require('express'); const graphQLPlayground = require('graphql-playground-middleware-express').default; +const getConfig = require('./utilities/getConfig'); const authenticate = require('./express/middleware/authenticate'); const connectMongoose = require('./mongoose/connect'); const expressMiddleware = require('./express/middleware'); @@ -19,7 +20,11 @@ const errorHandler = require('./express/middleware/errorHandler'); class Payload { constructor(options) { - this.config = sanitizeConfig(options.config); + const config = getConfig(options); + + this.config = sanitizeConfig(config); + this.config.paths.publicConfig = options.config.public; + this.express = options.express; this.router = express.Router(); this.collections = {}; diff --git a/src/utilities/getConfig.js b/src/utilities/getConfig.js new file mode 100644 index 0000000000..f12bf297af --- /dev/null +++ b/src/utilities/getConfig.js @@ -0,0 +1,18 @@ +/* eslint-disable import/no-dynamic-require */ +/* eslint-disable global-require */ + +const getConfig = (options) => { + if (!options.config || !options.config.public || !options.config.private) { + throw new Error('Error: missing config paths. Please specify where to find your configuration files while initializing Payload.'); + } + + const publicConfig = require(options.config.public); + const privateConfig = require(options.config.private); + + return { + ...publicConfig, + ...privateConfig, + }; +}; + +module.exports = getConfig; diff --git a/src/webpack/getWebpackDevConfig.js b/src/webpack/getWebpackDevConfig.js index be699d5430..95b83e1c1b 100644 --- a/src/webpack/getWebpackDevConfig.js +++ b/src/webpack/getWebpackDevConfig.js @@ -4,6 +4,8 @@ const webpack = require('webpack'); const getStyleLoaders = require('./getStyleLoaders'); const secureConfig = require('../utilities/secureConfig'); +const dependency = 'fuck'; + module.exports = (config) => { return { entry: { @@ -106,9 +108,6 @@ module.exports = (config) => { ], }, plugins: [ - new webpack.DefinePlugin({ - PAYLOAD_CONFIG: JSON.stringify(secureConfig(config)), - }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, '../client/index.html'), filename: './index.html', @@ -118,6 +117,7 @@ module.exports = (config) => { resolve: { modules: ['node_modules', path.resolve(__dirname, '../../node_modules')], alias: { + 'payload/config': config.paths.publicConfig, 'payload-scss-overrides': config.paths.scss, }, },