From be86cc7cda460f42a6bb770d316261368e7cda00 Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Wed, 2 Dec 2020 15:40:48 +0900 Subject: [PATCH 1/4] Use Components.FormattedMessage to make FormattedMessage component replaceable --- .../vulcan-admin/lib/components/AdminHome.jsx | 25 ++- .../lib/modules/components/AccessControl.jsx | 6 +- .../lib/modules/components/Card/Card.jsx | 4 +- .../Datatable/DatatableContents.jsx | 9 +- .../components/Datatable/DatatableFilter.jsx | 17 +- .../Datatable/DatatableSubmitSelected.jsx | 3 +- .../lib/modules/components/DeleteButton.jsx | 5 +- .../lib/modules/components/EditButton.jsx | 4 +- .../lib/modules/components/Error404.jsx | 5 +- .../lib/modules/components/NewButton.jsx | 4 +- .../PaginatedList/PaginatedList.jsx | 7 +- .../lib/modules/containers/index.js | 1 + packages/vulcan-core/lib/modules/index.js | 1 + packages/vulcan-email/lib/server/email.js | 6 +- .../vulcan-embed/lib/components/EmbedURL.jsx | 8 +- packages/vulcan-forms-upload/lib/Upload.jsx | 5 +- .../lib/components/FormComponent.jsx | 4 +- .../lib/components/FormComponentLoader.jsx | 3 +- .../lib/components/FormDescription.jsx | 13 ++ .../vulcan-forms/lib/components/FormError.jsx | 5 +- .../lib/components/FormSubmit.jsx | 9 +- .../vulcan-forms/lib/modules/components.js | 1 + packages/vulcan-i18n/lib/modules/message.js | 9 +- .../vulcan-lib/lib/modules/collections.js | 2 +- packages/vulcan-lib/lib/modules/routes.ts | 168 ++++++++++++++++++ packages/vulcan-lib/lib/server/debug.js | 26 +-- .../lib/components/NewsletterSubscribe.jsx | 3 +- .../lib/components/SubscribeTo.jsx | 6 +- .../lib/components/forms/Date2.jsx | 7 +- .../components/forms/FormComponentInner.jsx | 4 +- .../lib/components/forms/FormItem.jsx | 18 +- .../lib/components/ui/Dropdown.jsx | 7 +- .../lib/components/bonus/LoadMore.jsx | 4 +- .../lib/components/forms/FormErrors.jsx | 1 - .../lib/components/forms/FormSubmit.jsx | 7 +- .../lib/components/upload/UploadImage.jsx | 1 - .../lib/components/upload/UploadInner.jsx | 5 +- 37 files changed, 293 insertions(+), 120 deletions(-) create mode 100644 packages/vulcan-core/lib/modules/containers/index.js create mode 100644 packages/vulcan-forms/lib/components/FormDescription.jsx create mode 100644 packages/vulcan-lib/lib/modules/routes.ts diff --git a/packages/vulcan-admin/lib/components/AdminHome.jsx b/packages/vulcan-admin/lib/components/AdminHome.jsx index 7aa61692a3..0ed6c6a7a2 100644 --- a/packages/vulcan-admin/lib/components/AdminHome.jsx +++ b/packages/vulcan-admin/lib/components/AdminHome.jsx @@ -1,26 +1,33 @@ import React from 'react'; import { Components, withCurrentUser, AdminColumns } from 'meteor/vulcan:core'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; import Users from 'meteor/vulcan:users'; import '../modules/columns.js'; -const AdminHome = ({ currentUser }) => +const AdminHome = ({ currentUser }) => (
-

}> - + +

+ }> +
-
; + +); export default withCurrentUser(AdminHome); diff --git a/packages/vulcan-core/lib/modules/components/AccessControl.jsx b/packages/vulcan-core/lib/modules/components/AccessControl.jsx index 8f9b190a54..634417574d 100644 --- a/packages/vulcan-core/lib/modules/components/AccessControl.jsx +++ b/packages/vulcan-core/lib/modules/components/AccessControl.jsx @@ -4,7 +4,7 @@ import { useCurrentUser } from '../containers/currentUser'; import Users from 'meteor/vulcan:users'; import { useHistory } from 'react-router-dom'; import withMessages from '../containers/withMessages'; -import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; +import { intlShape } from 'meteor/vulcan:i18n'; const AccessControl = ({ currentRoute, children, flash }, { intl }) => { const { loading, currentUser } = useCurrentUser(); @@ -56,7 +56,7 @@ const FailureComponent = props => { const DefaultLogInFailureComponent = () => ( - + ); @@ -64,7 +64,7 @@ registerComponent({ name: 'DefaultLogInFailureComponent', component: DefaultLogI const DefaultPermissionFailureComponent = () => ( - + ); diff --git a/packages/vulcan-core/lib/modules/components/Card/Card.jsx b/packages/vulcan-core/lib/modules/components/Card/Card.jsx index 928ef9c9a7..4460fa1259 100644 --- a/packages/vulcan-core/lib/modules/components/Card/Card.jsx +++ b/packages/vulcan-core/lib/modules/components/Card/Card.jsx @@ -1,5 +1,5 @@ import { registerComponent, Components, formatLabel } from 'meteor/vulcan:lib'; -import { intlShape, FormattedMessage } from 'meteor/vulcan:i18n'; +import { intlShape } from 'meteor/vulcan:i18n'; import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; @@ -49,7 +49,7 @@ const CardEdit = (props, context) => ( label={context.intl.formatMessage({ id: 'cards.edit' })} component={ - + }> diff --git a/packages/vulcan-core/lib/modules/components/Datatable/DatatableContents.jsx b/packages/vulcan-core/lib/modules/components/Datatable/DatatableContents.jsx index fc9b9a0180..2e2f5a4888 100644 --- a/packages/vulcan-core/lib/modules/components/Datatable/DatatableContents.jsx +++ b/packages/vulcan-core/lib/modules/components/Datatable/DatatableContents.jsx @@ -1,6 +1,5 @@ -import { registerComponent } from 'meteor/vulcan:lib'; +import { Components, registerComponent } from 'meteor/vulcan:lib'; import React, { memo } from 'react'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; import PropTypes from 'prop-types'; import _sortBy from 'lodash/sortBy'; @@ -91,12 +90,12 @@ const DatatableContents = props => { ))} {showEdit ? ( - + ) : null} {showDelete ? ( - + ) : null} @@ -195,7 +194,7 @@ const DatatableEmpty = () => (
- +
diff --git a/packages/vulcan-core/lib/modules/components/Datatable/DatatableFilter.jsx b/packages/vulcan-core/lib/modules/components/Datatable/DatatableFilter.jsx index 077b0ee9e7..99c094246b 100644 --- a/packages/vulcan-core/lib/modules/components/Datatable/DatatableFilter.jsx +++ b/packages/vulcan-core/lib/modules/components/Datatable/DatatableFilter.jsx @@ -2,7 +2,6 @@ import { Components, registerComponent, Utils } from 'meteor/vulcan:lib'; import React, { useState } from 'react'; import gql from 'graphql-tag'; import { useQuery } from '@apollo/client'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; import moment from 'moment'; const getCount = columnFilters => { @@ -46,7 +45,7 @@ const DatatableFilter = props => { return ( } + title={} size="small" trigger={}> {query ? : } @@ -117,7 +116,7 @@ const DatatableFilterContents = props => { default: contents = (

- +

); } @@ -133,7 +132,7 @@ const DatatableFilterContents = props => { onClick={() => { setFilters(undefined); }}> - + { onClick={() => { submitFilters({ name, filters }); }}> - + ); @@ -215,7 +214,7 @@ const DatatableFilterDates = ({ filters, setFilters }) => ( , + label: , layout: 'horizontal', }} inputProperties={{}} @@ -233,7 +232,7 @@ const DatatableFilterDates = ({ filters, setFilters }) => ( , + label: , layout: 'horizontal', }} inputProperties={{}} @@ -265,7 +264,7 @@ const DatatableFilterNumbers = ({ filters, setFilters }) => ( , + label: , layout: 'horizontal', }} inputProperties={{ @@ -285,7 +284,7 @@ const DatatableFilterNumbers = ({ filters, setFilters }) => ( , + label: , layout: 'horizontal', }} inputProperties={{ diff --git a/packages/vulcan-core/lib/modules/components/Datatable/DatatableSubmitSelected.jsx b/packages/vulcan-core/lib/modules/components/Datatable/DatatableSubmitSelected.jsx index f91aa8ce0b..07cef32218 100644 --- a/packages/vulcan-core/lib/modules/components/Datatable/DatatableSubmitSelected.jsx +++ b/packages/vulcan-core/lib/modules/components/Datatable/DatatableSubmitSelected.jsx @@ -1,6 +1,5 @@ import { registerComponent, Components } from 'meteor/vulcan:lib'; import React, { memo } from 'react'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; import PropTypes from 'prop-types'; /* @@ -15,7 +14,7 @@ const DatatableSubmitSelected = ({ selectedItems, onSubmitSelected }) => ( e.preventDefault(); onSubmitSelected(selectedItems); }}> - + ); diff --git a/packages/vulcan-core/lib/modules/components/DeleteButton.jsx b/packages/vulcan-core/lib/modules/components/DeleteButton.jsx index e0e63f2296..a9825e455d 100644 --- a/packages/vulcan-core/lib/modules/components/DeleteButton.jsx +++ b/packages/vulcan-core/lib/modules/components/DeleteButton.jsx @@ -1,10 +1,9 @@ import { Components, registerComponent } from 'meteor/vulcan:lib'; import React from 'react'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; import { useDelete2 } from '../containers/delete2'; const DeleteButton = (props) => { - const { label, collection, collectionName, fragment, fragmentName, documentId, mutationOptions, ...rest } = props; + const { label, collection, collectionName, fragment, fragmentName, documentId, mutationOptions, currentUser, ...rest } = props; const [deleteFunction, { loading }] = useDelete2({ collection, collectionName, @@ -19,7 +18,7 @@ const DeleteButton = (props) => { onClick={() => { deleteFunction({ input: { id: documentId } }); }} - label={label || } + label={label || } {...rest} /> ); diff --git a/packages/vulcan-core/lib/modules/components/EditButton.jsx b/packages/vulcan-core/lib/modules/components/EditButton.jsx index 0da866d96d..35ef401cc4 100644 --- a/packages/vulcan-core/lib/modules/components/EditButton.jsx +++ b/packages/vulcan-core/lib/modules/components/EditButton.jsx @@ -1,6 +1,6 @@ import { Components, registerComponent } from 'meteor/vulcan:lib'; import React from 'react'; -import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; +import { intlShape } from 'meteor/vulcan:i18n'; const EditButton = ({ style = 'primary', variant, label, size, showId, modalProps, formProps, component, ...props }, { intl }) => ( - {label || } + {label || } ) } diff --git a/packages/vulcan-core/lib/modules/components/Error404.jsx b/packages/vulcan-core/lib/modules/components/Error404.jsx index 0369b2e903..a583d47cbe 100644 --- a/packages/vulcan-core/lib/modules/components/Error404.jsx +++ b/packages/vulcan-core/lib/modules/components/Error404.jsx @@ -1,12 +1,11 @@ -import { registerComponent } from 'meteor/vulcan:lib'; +import { Components, registerComponent } from 'meteor/vulcan:lib'; import React from 'react'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; const Error404 = () => { return (

- +

); diff --git a/packages/vulcan-core/lib/modules/components/NewButton.jsx b/packages/vulcan-core/lib/modules/components/NewButton.jsx index ccddfd4f5a..b5d1683b84 100644 --- a/packages/vulcan-core/lib/modules/components/NewButton.jsx +++ b/packages/vulcan-core/lib/modules/components/NewButton.jsx @@ -1,6 +1,6 @@ import { Components, registerComponent } from 'meteor/vulcan:lib'; import React from 'react'; -import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; +import { intlShape } from 'meteor/vulcan:i18n'; const NewButton = ({ collection, size, label, style = 'primary', formProps, ...props }, { intl }) => ( - {label || } + {label || } } > diff --git a/packages/vulcan-core/lib/modules/components/PaginatedList/PaginatedList.jsx b/packages/vulcan-core/lib/modules/components/PaginatedList/PaginatedList.jsx index b6f137a639..3414833323 100644 --- a/packages/vulcan-core/lib/modules/components/PaginatedList/PaginatedList.jsx +++ b/packages/vulcan-core/lib/modules/components/PaginatedList/PaginatedList.jsx @@ -1,6 +1,5 @@ -import { registerComponent } from 'meteor/vulcan:lib'; +import { Components, registerComponent } from 'meteor/vulcan:lib'; import React from 'react'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; import withComponents from '../../containers/withComponents'; import { useMulti2 } from '../../containers/multi2'; @@ -101,7 +100,7 @@ registerComponent('PaginatedListItem', PaginatedListItem); const PaginatedListNoResults = () => (

- +

); @@ -118,7 +117,7 @@ const PaginatedListLoadMore = ({ Components, loadMore, count, totalCount }) => ( e.preventDefault(); loadMore(); }}> - +  {' '} ({count}/{totalCount}) diff --git a/packages/vulcan-core/lib/modules/containers/index.js b/packages/vulcan-core/lib/modules/containers/index.js new file mode 100644 index 0000000000..0aaab08b78 --- /dev/null +++ b/packages/vulcan-core/lib/modules/containers/index.js @@ -0,0 +1 @@ +export * from './localeData'; \ No newline at end of file diff --git a/packages/vulcan-core/lib/modules/index.js b/packages/vulcan-core/lib/modules/index.js index 8dad7d8423..70d1d26aa9 100644 --- a/packages/vulcan-core/lib/modules/index.js +++ b/packages/vulcan-core/lib/modules/index.js @@ -2,6 +2,7 @@ import './callbacks'; export * from 'meteor/vulcan:lib'; +export * from './containers'; export * from './components.js'; export { default as App } from './components/App.jsx'; diff --git a/packages/vulcan-email/lib/server/email.js b/packages/vulcan-email/lib/server/email.js index 056e3dcb11..dbcc5766a9 100644 --- a/packages/vulcan-email/lib/server/email.js +++ b/packages/vulcan-email/lib/server/email.js @@ -141,7 +141,7 @@ export const send = (to, subject, html, text, throwErrors, cc, bcc, replyTo, hea const shouldSendEmail = process.env.NODE_ENV === 'production' || getSetting('enableDevelopmentEmails', false); - console.log(`//////// sending email${shouldSendEmail ? '' : ' (simulation)'}…`); // eslint-disable-line + console.log(`✉️ sending email${shouldSendEmail ? '' : ' (simulation)'}…`); // eslint-disable-line console.log('from: ' + _from); // eslint-disable-line console.log('to: ' + to); // eslint-disable-line console.log('subject: ' + subject); // eslint-disable-line @@ -219,8 +219,8 @@ export const buildAndSend = async ({ from, }) => { try { - const email = await VulcanEmail.build({ to, emailName, variables, locale }); - return VulcanEmail.send({ to: email.to, cc, bcc, replyTo, subject: email.subject, html: email.html, headers, attachments, from }); + const email = await build({ to, emailName, variables, locale }); + return send({ to: email.to, cc, bcc, replyTo, subject: email.subject, html: email.html, headers, attachments, from }); } catch (error) { // eslint-disable-next-line no-console console.log(error); diff --git a/packages/vulcan-embed/lib/components/EmbedURL.jsx b/packages/vulcan-embed/lib/components/EmbedURL.jsx index fbfb195bcc..3bbffc62f3 100644 --- a/packages/vulcan-embed/lib/components/EmbedURL.jsx +++ b/packages/vulcan-embed/lib/components/EmbedURL.jsx @@ -2,7 +2,7 @@ import { Components, registerComponent, Utils, getSetting } from 'meteor/vulcan: import { withMutation } from 'meteor/vulcan:core'; import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; +import { intlShape } from 'meteor/vulcan:i18n'; class EmbedURL extends Component { constructor(props) { @@ -133,10 +133,10 @@ class EmbedURL extends Component { @@ -151,7 +151,7 @@ class EmbedURL extends Component { onClick={this.editThumbnail} className="embedly-thumbnail-placeholder"> - +
diff --git a/packages/vulcan-forms-upload/lib/Upload.jsx b/packages/vulcan-forms-upload/lib/Upload.jsx index 31fec2bbe3..77dd5272e3 100755 --- a/packages/vulcan-forms-upload/lib/Upload.jsx +++ b/packages/vulcan-forms-upload/lib/Upload.jsx @@ -16,7 +16,6 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import Dropzone from 'react-dropzone'; import 'cross-fetch/polyfill'; // patch for browser which don't have fetch implemented -import { FormattedMessage } from 'meteor/vulcan:i18n'; import set from 'lodash/set'; registerSetting('cloudinary.cloudName', null, 'Cloudinary cloud name (for image uploads)'); @@ -302,12 +301,12 @@ class Upload extends PureComponent {
- +
{uploading && (
- +
)} diff --git a/packages/vulcan-forms/lib/components/FormComponent.jsx b/packages/vulcan-forms/lib/components/FormComponent.jsx index c97202ce43..c1fb222bd6 100644 --- a/packages/vulcan-forms/lib/components/FormComponent.jsx +++ b/packages/vulcan-forms/lib/components/FormComponent.jsx @@ -31,8 +31,8 @@ class FormComponent extends Component { return null; } const path = getPath(props); - const value = get(document, path); - + const intlOrRegularValue = get(document, path); + const value = typeof intlOrRegularValue === 'object' ? intlOrRegularValue.value : intlOrRegularValue; return getCharacterCounts(value, max); } diff --git a/packages/vulcan-forms/lib/components/FormComponentLoader.jsx b/packages/vulcan-forms/lib/components/FormComponentLoader.jsx index a40642a2e5..f0a4d77b2d 100644 --- a/packages/vulcan-forms/lib/components/FormComponentLoader.jsx +++ b/packages/vulcan-forms/lib/components/FormComponentLoader.jsx @@ -29,12 +29,13 @@ const FormComponentLoader = props => { throw new Error(error); } - if (loading) + if (loading){ return (
); + } // pass newly loaded data (and options if needed) to child component const extraProps = { data, queryData: data, queryError: error, loading }; diff --git a/packages/vulcan-forms/lib/components/FormDescription.jsx b/packages/vulcan-forms/lib/components/FormDescription.jsx new file mode 100644 index 0000000000..85e0b5d794 --- /dev/null +++ b/packages/vulcan-forms/lib/components/FormDescription.jsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { registerComponent } from 'meteor/vulcan:core'; +import Form from 'react-bootstrap/Form'; + +const FormDescription = ({ description }) => { + return ( + +
+ + ); +}; + +registerComponent('FormDescription', FormDescription); diff --git a/packages/vulcan-forms/lib/components/FormError.jsx b/packages/vulcan-forms/lib/components/FormError.jsx index d1449a0d06..588ecf4c57 100644 --- a/packages/vulcan-forms/lib/components/FormError.jsx +++ b/packages/vulcan-forms/lib/components/FormError.jsx @@ -1,8 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import getContext from 'recompose/getContext'; -import { registerComponent } from 'meteor/vulcan:core'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; +import { Components, registerComponent } from 'meteor/vulcan:core'; import get from 'lodash/get'; const FormError = ({ error, errorContext, getLabel }) => { @@ -48,7 +47,7 @@ const FormError = ({ error, errorContext, getLabel }) => { values: exception.data, }; } - return ; + return ; }; FormError.defaultProps = { diff --git a/packages/vulcan-forms/lib/components/FormSubmit.jsx b/packages/vulcan-forms/lib/components/FormSubmit.jsx index 49137019cf..066ce6b6e5 100644 --- a/packages/vulcan-forms/lib/components/FormSubmit.jsx +++ b/packages/vulcan-forms/lib/components/FormSubmit.jsx @@ -2,7 +2,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Components } from 'meteor/vulcan:core'; import { registerComponent } from 'meteor/vulcan:core'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; const FormSubmit = ({ submitForm, @@ -21,7 +20,7 @@ const FormSubmit = ({ }) => (
- {submitLabel ? submitLabel : } + {submitLabel ? submitLabel : } {cancelCallback ? ( @@ -32,7 +31,7 @@ const FormSubmit = ({ cancelCallback(document); }} > - {cancelLabel ? cancelLabel : } + {cancelLabel ? cancelLabel : } ) : null} @@ -45,7 +44,7 @@ const FormSubmit = ({ revertCallback(document); }} > - {revertLabel ? revertLabel : } + {revertLabel ? revertLabel : } ) : null} @@ -53,7 +52,7 @@ const FormSubmit = ({

- +
) : null} diff --git a/packages/vulcan-forms/lib/modules/components.js b/packages/vulcan-forms/lib/modules/components.js index 70bae3a573..bf9e342cb5 100644 --- a/packages/vulcan-forms/lib/modules/components.js +++ b/packages/vulcan-forms/lib/modules/components.js @@ -16,3 +16,4 @@ import '../components/Form.jsx'; import '../components/FormLayout.jsx'; import '../components/FormComponentLoader.jsx'; import '../components/FormOptionLabel.jsx'; +import '../components/FormDescription.jsx'; diff --git a/packages/vulcan-i18n/lib/modules/message.js b/packages/vulcan-i18n/lib/modules/message.js index f6736bc2dc..ab4f446bf0 100644 --- a/packages/vulcan-i18n/lib/modules/message.js +++ b/packages/vulcan-i18n/lib/modules/message.js @@ -1,5 +1,6 @@ import React, { Component } from 'react'; import { intlShape } from './shape'; +import { registerComponent } from 'meteor/vulcan:lib'; const FormattedMessage = ({ id, values, defaultMessage = '', html = false, className = '' }, { intl }) => { let message = intl.formatMessage({ id, defaultMessage }, values); @@ -11,12 +12,14 @@ const FormattedMessage = ({ id, values, defaultMessage = '', html = false, class } return html ? - : - {message}; + : + {message}; }; FormattedMessage.contextTypes = { intl: intlShape }; -export default FormattedMessage; +registerComponent('FormattedMessage', FormattedMessage); + +export default FormattedMessage; \ No newline at end of file diff --git a/packages/vulcan-lib/lib/modules/collections.js b/packages/vulcan-lib/lib/modules/collections.js index 30f5eb2d76..a79d6cd558 100644 --- a/packages/vulcan-lib/lib/modules/collections.js +++ b/packages/vulcan-lib/lib/modules/collections.js @@ -85,7 +85,7 @@ Mongo.Collection.prototype.addField = function (fieldOrFieldArray) { }); // add field schema to collection schema - collection.attachSchema(createSchema(merge(collection.simpleSchema()._schema, fieldSchema))); + collection.attachSchema(createSchema(merge(collection.options.schema, fieldSchema))); }; /** diff --git a/packages/vulcan-lib/lib/modules/routes.ts b/packages/vulcan-lib/lib/modules/routes.ts new file mode 100644 index 0000000000..aeac7dc35d --- /dev/null +++ b/packages/vulcan-lib/lib/modules/routes.ts @@ -0,0 +1,168 @@ +import { Components, getComponent } from './components'; + +export type Route = { + name: string; + path: string; + componentName?: string, + layoutName?: string, +} + +export const Routes = new Map(); // will be populated on startup +export const RoutesTable = new Map(); // storage for infos about routes themselves + +/* + A route is defined in the list like: + RoutesTable.foobar = { + name: 'foobar', + path: '/xyz', + component: getComponent('FooBar') + componentName: 'FooBar' // optional + } + + if there there is value for parentRouteName it will look for the route and add the new route as a child of it + */ +export const addRoute = (routeOrRouteArray: Route|Array, parentRouteName?: string) => { + + // be sure to have an array of routes to manipulate + const addedRoutes = Array.isArray(routeOrRouteArray) ? routeOrRouteArray : [routeOrRouteArray]; + + // if there is a value for parentRouteName you are adding this route as new child + if (parentRouteName) { + + addAsChildRoute(parentRouteName, addedRoutes); + + } else { + + // modify the routes table with the new routes + addedRoutes.forEach(({ name, path, ...properties }) => { + + // check if there is already a route registered to this path + const routeWithSamePath = Object.values(RoutesTable).find(route => route.path === path); + + if (routeWithSamePath) { + // delete the route registered with same path + delete RoutesTable[routeWithSamePath.name]; + } + + // register the new route + RoutesTable[name] = { + name, + path, + ...properties + }; + + }); + } +}; + +export const extendRoute = (routeName, routeProps) => { + + const route = Object.values(RoutesTable).find(route => route.name === routeName); + + if (route) { + RoutesTable[route.name] = { + ...route, + ...routeProps + }; + } +}; + + +/** + A route is defined in the list like: (same as above) + RoutesTable.foobar = { + name: 'foobar', + path: '/xyz', + component: getComponent('FooBar') + componentName: 'FooBar' // optional + } + + NOTE: This is implemented on single level deep ONLY for now + **/ + + +export const addAsChildRoute = (parentRouteName, addedRoutes) => { + + // if the parentRouteName does not exist, error + if (!RoutesTable[parentRouteName]) { + throw new Error(`Route ${parentRouteName} doesn't exist`); + } + + // modify the routes table with the new routes + addedRoutes.map(({ name, path, ...properties }) => { + + // get the current child routes for this Route + const childRoutes = RoutesTable[parentRouteName]['childRoutes'] || []; + + // check if there is already a route registered to this path + const routeWithSamePath = childRoutes.find(route => route.path === path); + + if (routeWithSamePath) { + // delete the route registered with same path + delete childRoutes[routeWithSamePath.name]; + } + + // append to the child routes the new route + childRoutes.push({ + name, + path, + ...properties + }); + + // register the new child route (overwriting the current which is fine) + RoutesTable[parentRouteName]['childRoutes'] = childRoutes; + + }); +}; + + +export const getRoute = name => { + const routeDef = RoutesTable[name]; + + // components should be loaded by now (populateComponentsApp function), we can grab the component in the lookup table and assign it to the route + if (!routeDef.component && routeDef.componentName) { + routeDef.component = getComponent(routeDef.componentName); + } + + return routeDef; +}; + +export const getChildRoute = (name, index) => { + const routeDef = RoutesTable[name]['childRoutes'][index]; + + // components should be loaded by now (populateComponentsApp function), we can grab the component in the lookup table and assign it to the route + if (!routeDef.component && routeDef.componentName) { + routeDef.component = getComponent(routeDef.componentName); + } + + return routeDef; +}; + +/** + * Populate the lookup table for routes to be callable + * ℹ️ Called once on app startup + **/ +export const populateRoutesApp = () => { + // loop over each component in the list + Object.keys(RoutesTable).map(name => { + // loop over child routes if available + if (typeof RoutesTable[name]['childRoutes'] !== typeof undefined) { + RoutesTable[name]['childRoutes'].map((item, index) => { + RoutesTable[name]['childRoutes'][index] = getChildRoute(name, index); + }); + } + + // populate an entry in the lookup table + Routes[name] = getRoute(name); + + // uncomment for debug + // console.log('init route:', name); + }); +}; + +// Should be used only in tests +export const emptyRoutes = () => { + Object.keys(Routes).map((key) => { + delete Routes[key]; + }); +}; diff --git a/packages/vulcan-lib/lib/server/debug.js b/packages/vulcan-lib/lib/server/debug.js index 4f0ff89074..2d4916c103 100644 --- a/packages/vulcan-lib/lib/server/debug.js +++ b/packages/vulcan-lib/lib/server/debug.js @@ -1,7 +1,7 @@ import { GraphQLSchema } from './graphql/graphql.js'; import { generateTypeDefs } from './graphql/typedefs.js'; import Vulcan from '../modules/config.js'; -import fs from 'fs'; +import fs, { promises as fsAsync } from 'fs'; import { Collections } from '../modules/collections.js'; import { extractCollectionInfo, extractFragmentInfo } from '../modules/handleOptions'; @@ -35,7 +35,9 @@ export const getGraphQLSchema = fileName => { Vulcan.getGraphQLSchema = getGraphQLSchema; -export const logToFile = (fileName, object, options = {}) => { +const logsDirectory = '.logs'; + +export const logToFile = async (fileName, object, options = {}) => { const { mode = 'append', timestamp = false } = options; // the server path is of type "/Users/foo/bar/appName/.meteor/local/build/programs/server" // we remove the last five segments to get the app directory @@ -44,13 +46,17 @@ export const logToFile = (fileName, object, options = {}) => { .split('/') .slice(1, -5) .join('/'); - const fullPath = `/${path}/${fileName}`; + const logsDirPath = `/${path}/${logsDirectory}`; + if (!fs.existsSync(logsDirPath)) { + fs.mkdirSync(logsDirPath, { recursive: true }); + } + const fullPath = `${logsDirPath}/${fileName}`; const contents = typeof object === 'string' ? object : JSON.stringify(object, null, 2); const now = new Date(); const text = timestamp ? now.toString() + '\n---\n' + contents : contents; if (mode === 'append') { const stream = fs.createWriteStream(fullPath, { flags: 'a' }); - stream.write(text + '\n\n'); + stream.write(text + '\n'); stream.end(); } else { fs.writeFile(fullPath, text, error => { @@ -84,11 +90,11 @@ export const generateGraphQLQueries = fileName => { .slice(1, -5) .join('/'); const fullPath = `/${path}/${name}`; - fd = fs.openSync(fullPath, 'w'); + fd = fsAsync.openSync(fullPath, 'w'); - Object.keys(Fragments).forEach(fragment => fs.appendFileSync(fd, Fragments[fragment].fragmentText + '\n')); + Object.keys(Fragments).forEach(fragment => fsAsync.appendFileSync(fd, Fragments[fragment].fragmentText + '\n')); - fs.appendFileSync(fd, '\n'); + fsAsync.appendFileSync(fd, '\n'); Collections.forEach(collection => { const { collectionName } = extractCollectionInfo({ collection }); @@ -101,7 +107,7 @@ export const generateGraphQLQueries = fileName => { typeName, fragmentName, }); - fs.appendFileSync(fd, singleQueryString + '\n'); + fsAsync.appendFileSync(fd, singleQueryString + '\n'); } if (get(GraphQLSchema.resolvers, `Query.${Utils.camelCaseify(Utils.pluralize(typeName))}`)) { @@ -109,13 +115,13 @@ export const generateGraphQLQueries = fileName => { typeName, fragmentName, }); - fs.appendFileSync(fd, multiQueryString + '\n'); + fsAsync.appendFileSync(fd, multiQueryString + '\n'); } }); } catch (err) { console.log(err); } finally { - if (fd !== undefined) fs.closeSync(fd); + if (fd !== undefined) fsAsync.closeSync(fd); } }; diff --git a/packages/vulcan-newsletter/lib/components/NewsletterSubscribe.jsx b/packages/vulcan-newsletter/lib/components/NewsletterSubscribe.jsx index 994a90e5ee..d0b5e133ce 100644 --- a/packages/vulcan-newsletter/lib/components/NewsletterSubscribe.jsx +++ b/packages/vulcan-newsletter/lib/components/NewsletterSubscribe.jsx @@ -1,7 +1,6 @@ import { Components, registerComponent, withMutation, withMessages } from 'meteor/vulcan:core'; import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; // this component is used as a custom controller in user's account edit (cf. ./custom_fields.js) class NewsletterSubscribe extends PureComponent { @@ -44,7 +43,7 @@ class NewsletterSubscribe extends PureComponent { onClick={this.subscribeUnsubscribe} variant="primary" > - +
diff --git a/packages/vulcan-subscribe/lib/components/SubscribeTo.jsx b/packages/vulcan-subscribe/lib/components/SubscribeTo.jsx index 3f7654b1a0..5fc0b1b7bf 100644 --- a/packages/vulcan-subscribe/lib/components/SubscribeTo.jsx +++ b/packages/vulcan-subscribe/lib/components/SubscribeTo.jsx @@ -1,11 +1,11 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { intlShape, FormattedMessage } from 'meteor/vulcan:i18n'; +import { intlShape } from 'meteor/vulcan:i18n'; import { graphql } from '@apollo/client/react/hoc'; import compose from 'recompose/compose'; import gql from 'graphql-tag'; import Users from 'meteor/vulcan:users'; -import { withCurrentUser, withMessages, registerComponent, Utils } from 'meteor/vulcan:core'; +import { Components, withCurrentUser, withMessages, registerComponent, Utils } from 'meteor/vulcan:core'; // boolean -> unsubscribe || subscribe const getSubscribeAction = subscribed => subscribed ? 'unsubscribe' : 'subscribe'; @@ -61,7 +61,7 @@ class SubscribeToActionHandler extends PureComponent { const className = this.props.className ? this.props.className : ''; - return Users.canDo(currentUser, action) ? : null; + return Users.canDo(currentUser, action) ? : null; } } diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Date2.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Date2.jsx index 6db0661d84..27b14f26a4 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Date2.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Date2.jsx @@ -1,7 +1,6 @@ import React, { PureComponent } from 'react'; import { Components, registerComponent } from 'meteor/vulcan:core'; import moment from 'moment'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; const isEmptyValue = value => typeof value === 'undefined' || @@ -129,7 +128,7 @@ class DateComponent2 extends PureComponent {
diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/FormComponentInner.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/FormComponentInner.jsx index d649cf2333..4868c35984 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/FormComponentInner.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/FormComponentInner.jsx @@ -1,6 +1,6 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; +import { intlShape } from 'meteor/vulcan:i18n'; import { Components, registerComponent, instantiateComponent, whitelistInputProps } from 'meteor/vulcan:core'; import classNames from 'classnames'; @@ -17,7 +17,7 @@ class FormComponentInner extends PureComponent { }> - + ); } diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/FormItem.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/FormItem.jsx index e528737fed..87d60ac624 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/FormItem.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/FormItem.jsx @@ -34,11 +34,7 @@ const FormItem = props => { {beforeInput} {innerComponent} {afterInput} - {description && ( - -
- - )} + {description && } ); } else if (layout === 'vertical') { @@ -52,11 +48,7 @@ const FormItem = props => { {innerComponent} {afterInput}
- {description && ( - -
- - )} + {description && }
); @@ -69,11 +61,7 @@ const FormItem = props => { {beforeInput} {innerComponent} {afterInput} - {description && ( - -
- - )} + {description && } ); diff --git a/packages/vulcan-ui-bootstrap/lib/components/ui/Dropdown.jsx b/packages/vulcan-ui-bootstrap/lib/components/ui/Dropdown.jsx index 2a9b37d7e6..360da95461 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/ui/Dropdown.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/ui/Dropdown.jsx @@ -1,11 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { registerComponent } from 'meteor/vulcan:lib'; +import { Components, registerComponent } from 'meteor/vulcan:lib'; import Dropdown from 'react-bootstrap/Dropdown'; import DropdownItem from 'react-bootstrap/DropdownItem'; import DropdownButton from 'react-bootstrap/DropdownButton'; import { LinkContainer } from 'react-router-bootstrap'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; /* @@ -40,7 +39,7 @@ const Item = ({ index, to, labelId, label, component, componentProps = {}, itemP if (component) { menuComponent = React.cloneElement(component, componentProps); } else if (labelId) { - menuComponent = ; + menuComponent = ; } else { menuComponent = {label}; } @@ -89,7 +88,7 @@ const BootstrapDropdown = ({ label, labelId, trigger, menuItems, menuContents, v } else { // else default to DropdownButton return ( - : label} {...dropdownProps}> + : label} {...dropdownProps}> {menuBody} ); diff --git a/packages/vulcan-ui-material/lib/components/bonus/LoadMore.jsx b/packages/vulcan-ui-material/lib/components/bonus/LoadMore.jsx index 747c36c028..71dee2468f 100644 --- a/packages/vulcan-ui-material/lib/components/bonus/LoadMore.jsx +++ b/packages/vulcan-ui-material/lib/components/bonus/LoadMore.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; +import { intlShape } from 'meteor/vulcan:i18n'; import { Components, registerComponent } from 'meteor/vulcan:core'; import withStyles from '@material-ui/core/styles/withStyles'; import Typography from '@material-ui/core/Typography'; @@ -72,7 +72,7 @@ const LoadMore = (
{showCount && ( - + )} {isLoadingMore ? ( diff --git a/packages/vulcan-ui-material/lib/components/forms/FormErrors.jsx b/packages/vulcan-ui-material/lib/components/forms/FormErrors.jsx index af03bb95d4..8bfb0f6c66 100644 --- a/packages/vulcan-ui-material/lib/components/forms/FormErrors.jsx +++ b/packages/vulcan-ui-material/lib/components/forms/FormErrors.jsx @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import { replaceComponent, Components } from 'meteor/vulcan:core'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; import Snackbar from '@material-ui/core/Snackbar'; import withStyles from '@material-ui/core/styles/withStyles'; diff --git a/packages/vulcan-ui-material/lib/components/forms/FormSubmit.jsx b/packages/vulcan-ui-material/lib/components/forms/FormSubmit.jsx index d4974e6ee3..067fb5d1b1 100644 --- a/packages/vulcan-ui-material/lib/components/forms/FormSubmit.jsx +++ b/packages/vulcan-ui-material/lib/components/forms/FormSubmit.jsx @@ -7,7 +7,6 @@ import Button from '@material-ui/core/Button'; import IconButton from '@material-ui/core/IconButton'; import DeleteIcon from 'mdi-material-ui/Delete'; import Tooltip from '@material-ui/core/Tooltip'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; import classNames from 'classnames'; const styles = theme => ({ @@ -67,7 +66,7 @@ const FormSubmit = ( event.preventDefault(); cancelCallback(document); }}> - {cancelLabel ? cancelLabel : } + {cancelLabel ? cancelLabel : } ) : null} @@ -81,7 +80,7 @@ const FormSubmit = ( clearForm({ clearErrors: true, clearCurrentValues: true, clearDeletedValues: true }); revertCallback(document); }}> - {revertLabel ? revertLabel : } + {revertLabel ? revertLabel : } ) : null} @@ -91,7 +90,7 @@ const FormSubmit = ( color="secondary" className={classNames('submit-button', classes.button)} disabled={!isChanged()}> - {submitLabel ? submitLabel : } + {submitLabel ? submitLabel : }
); diff --git a/packages/vulcan-ui-material/lib/components/upload/UploadImage.jsx b/packages/vulcan-ui-material/lib/components/upload/UploadImage.jsx index 529e3d2a8b..68a3a9f05b 100755 --- a/packages/vulcan-ui-material/lib/components/upload/UploadImage.jsx +++ b/packages/vulcan-ui-material/lib/components/upload/UploadImage.jsx @@ -1,7 +1,6 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { Components, registerComponent } from 'meteor/vulcan:lib'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; import withStyles from '@material-ui/core/styles/withStyles'; import IconButton from '@material-ui/core/IconButton'; import DeleteIcon from 'mdi-material-ui/Delete'; diff --git a/packages/vulcan-ui-material/lib/components/upload/UploadInner.jsx b/packages/vulcan-ui-material/lib/components/upload/UploadInner.jsx index 8f79e7235d..1ba4f3b681 100755 --- a/packages/vulcan-ui-material/lib/components/upload/UploadInner.jsx +++ b/packages/vulcan-ui-material/lib/components/upload/UploadInner.jsx @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import { Components, registerComponent, getComponent } from 'meteor/vulcan:lib'; import Dropzone from 'react-dropzone'; import withStyles from '@material-ui/core/styles/withStyles'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; import FormControl from '@material-ui/core/FormControl'; import FormLabel from '@material-ui/core/FormLabel'; import FormHelperText from '@material-ui/core/FormHelperText'; @@ -104,7 +103,7 @@ const UploadInner = props => { rejectClassName={classes.dropzoneReject} disabled={disabled}>
- @@ -112,7 +111,7 @@ const UploadInner = props => { {uploading && (
- +
)} From 51be9c2a9031a3cffb40bba2562fe7409b2dd662 Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Mon, 14 Dec 2020 09:35:29 +0900 Subject: [PATCH 2/4] Improve User.owns --- packages/vulcan-users/lib/modules/permissions.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/vulcan-users/lib/modules/permissions.js b/packages/vulcan-users/lib/modules/permissions.js index ade9cdeea7..f4a0b2874a 100644 --- a/packages/vulcan-users/lib/modules/permissions.js +++ b/packages/vulcan-users/lib/modules/permissions.js @@ -153,11 +153,14 @@ Users.canDo = (user, actionOrActions) => { Users.owns = function(user, document) { try { if (!!document.userId) { - // case 1: document is a post or a comment, use userId to check + // case 1: use userId to check return user._id === document.userId; - } else { - // case 2: document is a user, use _id or slug to check - return document.slug ? user.slug === document.slug : user._id === document._id; + } else if (document.user) { + // case 2: use user._id to check + return user._id === document.user._id; + }else { + // case 3: document is a user, use _id to check + return user._id === document._id; } } catch (e) { return false; // user not logged in From 3ac67764b024ea78e7a87d8abeed66d1145f215e Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Mon, 14 Dec 2020 09:35:43 +0900 Subject: [PATCH 3/4] Escape _like regex --- packages/vulcan-lib/lib/modules/mongoParams.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/vulcan-lib/lib/modules/mongoParams.js b/packages/vulcan-lib/lib/modules/mongoParams.js index 0309720ded..61d5a887db 100644 --- a/packages/vulcan-lib/lib/modules/mongoParams.js +++ b/packages/vulcan-lib/lib/modules/mongoParams.js @@ -22,6 +22,10 @@ export const convertUniqueSelector = selector => { } return selector; }; + +// see https://stackoverflow.com/a/3561711 +export const escapeRegex = s => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + /* Filtering @@ -40,12 +44,12 @@ const conversionTable = { _neq: '$ne', _nin: '$nin', _is_null: value => ({ $exists: !value }), - _is: value => ({$elemMatch: {$eq: value }}), - _contains: value => ({$elemMatch: {$eq: value }}), + _is: value => ({ $elemMatch: { $eq: value } }), + _contains: value => ({ $elemMatch: { $eq: value } }), asc: 1, desc: -1, _like: value => ({ - $regex: value, + $regex: escapeRegex(value), $options: 'i', }), }; @@ -59,7 +63,6 @@ const getFieldNames = expressionArray => { }; export const filterFunction = async (collection, input = {}, context) => { - // eslint-disable-next-line no-unused-vars const { filter, limit, sort, search, filterArguments, offset, id } = input; let selector = {}; From 74fc9dc52c4d4d3f698599b0db508c465d4c49da Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Mon, 14 Dec 2020 09:40:11 +0900 Subject: [PATCH 4/4] Better error handling --- .../lib/components/ErrorCatcherContents.jsx | 4 ++-- packages/vulcan-errors/lib/components/ErrorCatcher.jsx | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/vulcan-debug/lib/components/ErrorCatcherContents.jsx b/packages/vulcan-debug/lib/components/ErrorCatcherContents.jsx index 6a7075170a..1d6a2cf8d6 100644 --- a/packages/vulcan-debug/lib/components/ErrorCatcherContents.jsx +++ b/packages/vulcan-debug/lib/components/ErrorCatcherContents.jsx @@ -1,9 +1,9 @@ import React from 'react'; import { Components, replaceComponent } from 'meteor/vulcan:lib'; -const ErrorCatcherContents = ({ error }) => ( +const ErrorCatcherContents = ({ error, message }) => (
- +

Here are some suggestions to help you fix this issue:

    diff --git a/packages/vulcan-errors/lib/components/ErrorCatcher.jsx b/packages/vulcan-errors/lib/components/ErrorCatcher.jsx index de14e6a62f..0974542777 100644 --- a/packages/vulcan-errors/lib/components/ErrorCatcher.jsx +++ b/packages/vulcan-errors/lib/components/ErrorCatcher.jsx @@ -15,6 +15,8 @@ import { withRouter } from 'react-router'; import React, { Component } from 'react'; import { Errors } from '../modules/errors.js'; +const getMessage = error => error.message || error.errorMessage; + class ErrorCatcher extends Component { state = { error: null, @@ -25,7 +27,7 @@ class ErrorCatcher extends Component { const { sourceVersion } = siteData; this.setState({ error }); Errors.log({ - message: error.message, + message: getMessage(error), error, details: { ...errorInfo, sourceVersion }, currentUser, @@ -47,15 +49,15 @@ class ErrorCatcher extends Component { render() { const { error } = this.state; - return error ? : this.props.children; + return error ? : this.props.children; } } registerComponent('ErrorCatcher', ErrorCatcher, withCurrentUser, withSiteData, withRouter); -const ErrorCatcherContents = ({ error }) => ( +const ErrorCatcherContents = ({ error, message }) => (
    - +
    );