Open Source Headless CMS for Publishers and News Rooms
View Demo
·
Report Bug
·
Request Feature
- Coding Style Guide
- Code Organization
- Prisma
- Write Automated Tests
- Setup Commenting
- Email Templates
- Deployments
- Releases
npx nx serve <app-name>
example: npx nx serve bajour
Prerequisite: In the API (packages/api) exists a corresponding GraphQL endpoint.
- In
editor/src/app/api
you have to create a corresponding graphql file or define your mutation or query in an existing one. - Start the API (
npm run watch
) - In your terminal run
npx nx generate-api
- Now the file
index.ts
will be generated automatically inapps/editor/src/app/api
- Now you can import your desired endpoint in your .tsx file. See for example
subscriptionEditPanel.tsx
Please refer to the documentation https://github.com/wepublish/analytics#readme
Validation is based on rsuite validation: https://rsuitejs.com/components/form-validation/
Validation takes a schema which defines the content type and requirements necessary to specific fields. In the example below, the form to register a new user takes:
- a name of type string, the name is required,
- an email address of type string, which is also required and needs to be a valid email address
const userValidationModel = Schema.Model({
name: StringType().isRequired(t('errorMessages.noNameErrorMessage')),
email: StringType()
.isRequired(t('errorMessages.noEmailErrorMessage'))
.isEmail(t('errorMessages.invalidEmailErrorMessage')),
password: validatePassword
})
All validation types are listed on the schema type here.
Error messages are by default defined by rsuite but can be customized. In this example, they are customized in all 3 languages wePublish uses.
The model then needs to be specified in the related <Form>
and the name of the field needs to be specified in the related <FormControl>
as follow
<Form
model={validationModel}
onSubmit={validationPassed => validationPassed && handleSave()}
formValue={{name: name, email: email, password: password}}>
<FormControl
name="name"
/>
...
</Form>
The validation will be triggered onSubmit
. The handleSave()
function will be triggered only if validation is successful.
This part differs from the Rsuite documentation, where validation is checked with an if statement, which is less clean.
For validation to be triggered on onSubmit
, the form button needs to be of type submit
and needs to be inside the form:
<Form>
...
<Button type = "submit"/>
</Form>
Finally, the <Form>
needs to retrieve the values of the to-be-validated fields through formValue
. This will allow the validation to run correctly when the fields are pre-filled as well.
Validation only runs on type <Form>
. Inputs of other types, like SelectPicker
or other input types need to be turned into a form with the prop accepter={SelectPicker}
.
Permission control allows for restricting Editor content dependent on the current user's role.
createCheckedPermissionComponent
takes a list of permission IDs, any of which would permit access, followed by the component that is being controlled.
const CheckedPermissionComponent = createCheckedPermissionComponent(listOfPermissions)(ControlledComponent)
The controlled component can then be exported to replace the uncontrolled version.
export {CheckedPermissionComponent as ControlledComponent}
If checks pass, and the user retains the specified permissions then the original component will be returned, otherwise the component will return an error message notifying the user that they aren't authorized to access that content.
It is possible to hide the component without the error message by passing false as a second argument to createCheckedPermissionComponent()
.
To check whether a user retains an individual permission, the authorise()
can be called.
This takes a permission ID and returns true
or false
depending on whether the current user has that permission.
authorise(permissionID)
To properly attach label to input, as well as utilize auto-generated aria-labelledby
and aria-describeby
, rsuite provides a controlId
prop on Form.Group
. This should be watched throughout the project to ensure the best possible performance and usability of the forms. More information can be found under this link: https://rsuitejs.com/components/form/#accessibility
After requests from publishers and weighing benefits and potential issues, we decided to allow to save custom HTML blocks both in articles and pages. Allowing to save and display custom HTML blocks is risky due to the fact that these blocks can be used to run dangerous scripts on the client side that may lead to, e.g. account impersonation, observing user behaviour, loading external content or stealing sensitive data. But, as the blocks will be added by publishers themselves, it's their responsibility to make sure that the HTML blocks are secure. WePublish provides further measurements by applying a filter on the HTML that sanitises the publisher's input, minimising the risk of running malicious code.
To give further control over the content of HTML block, whitelisting certain tags and urls will be made possible in the editor's settings. In addition, we always store the original data in the database, and we allow the publisher to see the sanitised data in the settings to see how (and if) the HTML was changed thanks to the xss prevention filter - which can further help to understand the risks and dangers.
We have, however, to be aware that it's almost impossible to be 100% sure that none of the code displayed as custom HTML is dangerous.
The icon library used throughout the editor is react-icons. https://react-icons.github.io/react-icons
It's a great library created reasonably long time ago, with a good support, that includes few of the most widely used icon packages, all for free. For consistency, we decided to only use one of the icon sets in our project, and we chose material-icons from Google.
- it contains lots of icons
- it utilizes ES6 imports that allows us to include only the icons that we are using in our project
- it's free (Apache License 2.0)
- it's widely used
- it's supported by Google
The usage is very simple. In your component import the icon as a React component like this:
import { IconName } from "react-icons/md";
then just use the icon by rendering it as JSX like this:
<IconName />
Here is the complete list of icons by material-design that are supported by react-icons https://react-icons.github.io/react-icons/icons?name=md For reasearch purposes, if you want to add a new icons but struggle to find it by name (e.g. trash), you can search under this link https://fonts.google.com/icons as the search is a bit more intelligent than just looking on the string representing the icon's name. Here's an example: https://fonts.google.com/icons?icon.query=trash
The webhook will call paymentProvider.updatePaymentWithIntentState()
which will create a payment.
This creates a payment creation event which is caught by paymentProvider.setupPaymentProvider()
.
Within this event another invoice event will be triggered which is handled in events.invoiceModelEvents()