mirror of
https://github.com/strapi/strapi.git
synced 2025-11-09 06:40:42 +00:00
erge branch 'develop' into features/webhooks
This commit is contained in:
commit
14d95f3de1
@ -437,7 +437,9 @@ You can update these template in the **Email Templates** tab in the admin panel.
|
|||||||
- `URL` is the Strapi backend URL that confirm the code (by default `/auth/email-confirmation`).
|
- `URL` is the Strapi backend URL that confirm the code (by default `/auth/email-confirmation`).
|
||||||
|
|
||||||
## Security configuration
|
## Security configuration
|
||||||
JWT tokens can be verified and trusted because the information is digitally signed. To sign a token a *secret* is required. By default Strapi generates one that is stored in `./your-app/extensions/users-permissions/config/jwt.json`. This is useful during development but for security reasons it is **recommended** to set a custom token via an environment variable `JWT_SECRET` when deploying to production. It is also possible to modify `jwt.json` file to accept `JWT_TOKEN` automatically by doing following ([docs](https://strapi.io/documentation/3.0.0-beta.x/concepts/configurations.html#dynamic-configurations)).
|
|
||||||
|
JWT tokens can be verified and trusted because the information is digitally signed. To sign a token a _secret_ is required. By default Strapi generates one that is stored in `./your-app/extensions/users-permissions/config/jwt.json`. This is useful during development but for security reasons it is **recommended** to set a custom token via an environment variable `JWT_SECRET` when deploying to production. It is also possible to modify `jwt.json` file to accept `JWT_TOKEN` automatically by doing following ([docs](https://strapi.io/documentation/3.0.0-beta.x/concepts/configurations.html#dynamic-configurations)).
|
||||||
|
|
||||||
```
|
```
|
||||||
"jwtSecret": "${process.env.JWT_SECRET}"
|
"jwtSecret": "${process.env.JWT_SECRET}"
|
||||||
```
|
```
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
const DATE_FORMATS = {
|
||||||
|
date: 'dddd, MMMM Do YYYY',
|
||||||
|
datetime: 'dddd, MMMM Do YYYY HH:mm',
|
||||||
|
time: 'HH:mm A',
|
||||||
|
timestamp: 'dddd, MMMM Do YYYY',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DATE_FORMATS;
|
||||||
@ -5,11 +5,12 @@ import { get, isEmpty, isNull, isObject, toLower, toString } from 'lodash';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { IcoContainer, useGlobalContext } from 'strapi-helper-plugin';
|
import { IcoContainer, useGlobalContext } from 'strapi-helper-plugin';
|
||||||
import useListView from '../../hooks/useListView';
|
import useListView from '../../hooks/useListView';
|
||||||
|
|
||||||
import CustomInputCheckbox from '../CustomInputCheckbox';
|
import CustomInputCheckbox from '../CustomInputCheckbox';
|
||||||
import MediaPreviewList from '../MediaPreviewList';
|
import MediaPreviewList from '../MediaPreviewList';
|
||||||
|
|
||||||
import { ActionContainer, Truncate, Truncated } from './styledComponents';
|
import { ActionContainer, Truncate, Truncated } from './styledComponents';
|
||||||
|
import DATE_FORMATS from './DATE_FORMATS';
|
||||||
|
|
||||||
|
const dateToUtcTime = date => moment.parseZone(date).utc();
|
||||||
|
|
||||||
const getDisplayedValue = (type, value, name) => {
|
const getDisplayedValue = (type, value, name) => {
|
||||||
switch (toLower(type)) {
|
switch (toLower(type)) {
|
||||||
@ -28,7 +29,6 @@ const getDisplayedValue = (type, value, name) => {
|
|||||||
case 'boolean':
|
case 'boolean':
|
||||||
return value !== null ? toString(value) : '-';
|
return value !== null ? toString(value) : '-';
|
||||||
case 'date':
|
case 'date':
|
||||||
case 'time':
|
|
||||||
case 'datetime':
|
case 'datetime':
|
||||||
case 'timestamp': {
|
case 'timestamp': {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
@ -40,10 +40,7 @@ const getDisplayedValue = (type, value, name) => {
|
|||||||
? JSON.stringify(value)
|
? JSON.stringify(value)
|
||||||
: value;
|
: value;
|
||||||
|
|
||||||
return moment
|
return dateToUtcTime(date).format(DATE_FORMATS[type]);
|
||||||
.parseZone(date)
|
|
||||||
.utc()
|
|
||||||
.format('dddd, MMMM Do YYYY');
|
|
||||||
}
|
}
|
||||||
case 'password':
|
case 'password':
|
||||||
return '••••••••';
|
return '••••••••';
|
||||||
@ -51,6 +48,16 @@ const getDisplayedValue = (type, value, name) => {
|
|||||||
case 'file':
|
case 'file':
|
||||||
case 'files':
|
case 'files':
|
||||||
return value;
|
return value;
|
||||||
|
case 'time': {
|
||||||
|
const [hour, minute, second] = value.split(':');
|
||||||
|
const timeObj = {
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
second,
|
||||||
|
};
|
||||||
|
const date = moment().set(timeObj);
|
||||||
|
return date.format(DATE_FORMATS.time);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ function reducer(state, action) {
|
|||||||
|
|
||||||
return state
|
return state
|
||||||
.updateIn(['modifiedData', ...action.keys], () => {
|
.updateIn(['modifiedData', ...action.keys], () => {
|
||||||
if (action.value._isAMomentObject === true) {
|
if (action.value && action.value._isAMomentObject === true) {
|
||||||
return moment(action.value, 'YYYY-MM-DD HH:mm:ss').format();
|
return moment(action.value, 'YYYY-MM-DD HH:mm:ss').format();
|
||||||
}
|
}
|
||||||
return action.value;
|
return action.value;
|
||||||
|
|||||||
@ -5,28 +5,34 @@
|
|||||||
|
|
||||||
import React, { memo } from 'react';
|
import React, { memo } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { DateTime } from '@buffetjs/custom';
|
||||||
import {
|
import {
|
||||||
InputDate,
|
DatePicker,
|
||||||
InputNumber,
|
|
||||||
InputSelect,
|
|
||||||
InputText,
|
InputText,
|
||||||
} from 'strapi-helper-plugin';
|
InputNumber,
|
||||||
|
Select,
|
||||||
|
TimePicker,
|
||||||
|
} from '@buffetjs/core';
|
||||||
import { InputWrapperDate } from './components';
|
import { InputWrapperDate } from './components';
|
||||||
|
|
||||||
const getInputType = attrType => {
|
const getInputType = attrType => {
|
||||||
switch (attrType) {
|
switch (attrType) {
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
return InputSelect;
|
return Select;
|
||||||
case 'date':
|
case 'date':
|
||||||
case 'datetime':
|
|
||||||
case 'timestamp':
|
case 'timestamp':
|
||||||
case 'timestampUpdate':
|
case 'timestampUpdate':
|
||||||
return InputDate;
|
return DatePicker;
|
||||||
|
case 'datetime':
|
||||||
|
return DateTime;
|
||||||
case 'integer':
|
case 'integer':
|
||||||
case 'biginteger':
|
case 'biginteger':
|
||||||
case 'decimal':
|
case 'decimal':
|
||||||
case 'float':
|
case 'float':
|
||||||
return InputNumber;
|
return InputNumber;
|
||||||
|
case 'time':
|
||||||
|
return TimePicker;
|
||||||
default:
|
default:
|
||||||
return InputText;
|
return InputText;
|
||||||
}
|
}
|
||||||
@ -34,10 +40,18 @@ const getInputType = attrType => {
|
|||||||
|
|
||||||
function Input({ type, ...rest }) {
|
function Input({ type, ...rest }) {
|
||||||
const Component = getInputType(type);
|
const Component = getInputType(type);
|
||||||
const style = { width: '210px', paddingTop: '4px' };
|
let style = type !== 'time' ? { width: '210px', paddingTop: '4px' } : {};
|
||||||
|
|
||||||
|
if (['integer', 'biginteger', 'float', 'decimal'].includes(type)) {
|
||||||
|
style = { marginRight: '20px' };
|
||||||
|
}
|
||||||
const styles =
|
const styles =
|
||||||
type === 'boolean' ? { minWidth: '100px', maxWidth: '200px' } : style;
|
type === 'boolean' ? { minWidth: '100px', maxWidth: '200px' } : style;
|
||||||
const wrapperStyle = type == 'boolean' ? { marginRight: '20px' } : {};
|
const wrapperStyle =
|
||||||
|
type == 'boolean' ||
|
||||||
|
['date', 'timestamp', 'time', 'datetime'].includes(type)
|
||||||
|
? { marginRight: '20px' }
|
||||||
|
: { marginRight: '10px' };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InputWrapperDate type={type || 'text'} style={wrapperStyle}>
|
<InputWrapperDate type={type || 'text'} style={wrapperStyle}>
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const Option = ({ id, value }) => {
|
||||||
|
return (
|
||||||
|
<FormattedMessage id={id}>
|
||||||
|
{msg => <option value={value}>{msg}</option>}
|
||||||
|
</FormattedMessage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Option.propTypes = {
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Option;
|
||||||
@ -53,39 +53,27 @@ const Button = styled.button`
|
|||||||
|
|
||||||
const InputWrapperDate = styled.div`
|
const InputWrapperDate = styled.div`
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
span {
|
||||||
|
left: 5px;
|
||||||
|
}
|
||||||
|
.rc-input-number-handler-wrap {
|
||||||
|
right: -5px !important;
|
||||||
|
}
|
||||||
|
.rc-input-number-input-wrap {
|
||||||
|
max-width: 210px;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
> div {
|
||||||
|
width: 210px;
|
||||||
|
}
|
||||||
|
|
||||||
${({ type }) => {
|
${({ type }) => {
|
||||||
if (
|
if (type === 'datetime') {
|
||||||
type.includes('date') ||
|
return `
|
||||||
type === 'timestampUpdate' ||
|
> div {
|
||||||
type === 'timestamp'
|
width: 300px;
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
position: relative;
|
|
||||||
&:before {
|
|
||||||
content: '\f073';
|
|
||||||
position: absolute;
|
|
||||||
left: 6px;
|
|
||||||
top: 1px;
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 3px 0px 0px 3px;
|
|
||||||
background: #fafafb;
|
|
||||||
color: #b3b5b9;
|
|
||||||
text-align: center;
|
|
||||||
font-family: 'FontAwesome';
|
|
||||||
font-size: 1.4rem;
|
|
||||||
line-height: 32px;
|
|
||||||
z-index: 999;
|
|
||||||
-webkit-font-smoothing: none;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
padding-left: 42px;
|
|
||||||
box-shadow: 0px 1px 1px rgba(104, 118, 142, 0.05);
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -103,5 +91,4 @@ const Input = styled.input`
|
|||||||
font-family: 'Lato' !important;
|
font-family: 'Lato' !important;
|
||||||
box-shadow: 0px 1px 1px rgba(104, 118, 142, 0.05);
|
box-shadow: 0px 1px 1px rgba(104, 118, 142, 0.05);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export { Button, InputWrapper, Wrapper, InputWrapperDate, Input };
|
export { Button, InputWrapper, Wrapper, InputWrapperDate, Input };
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import React, { memo } from 'react';
|
import React, { memo } from 'react';
|
||||||
import { get, isEmpty } from 'lodash';
|
import { get, isEmpty } from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { Select } from '@buffetjs/core';
|
||||||
import { InputSelect } from 'strapi-helper-plugin';
|
|
||||||
import { Button, InputWrapper, Wrapper } from './components';
|
import { Button, InputWrapper, Wrapper } from './components';
|
||||||
import Input from './Input';
|
import Input from './Input';
|
||||||
|
import Option from './Option';
|
||||||
import getFilters from './utils';
|
import getFilters from './utils';
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
@ -46,7 +46,7 @@ function FilterPickerOption({
|
|||||||
isRemoveButton
|
isRemoveButton
|
||||||
onClick={() => onRemoveFilter(index)}
|
onClick={() => onRemoveFilter(index)}
|
||||||
/>
|
/>
|
||||||
<InputSelect
|
<Select
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
// Change the attribute
|
// Change the attribute
|
||||||
onChange(e);
|
onChange(e);
|
||||||
@ -55,13 +55,15 @@ function FilterPickerOption({
|
|||||||
}}
|
}}
|
||||||
name={`${index}.name`}
|
name={`${index}.name`}
|
||||||
value={get(modifiedData, [index, 'name'], '')}
|
value={get(modifiedData, [index, 'name'], '')}
|
||||||
selectOptions={allowedAttributes.map(attr => attr.name)}
|
options={allowedAttributes.map(attr => attr.name)}
|
||||||
style={styles.select}
|
style={styles.select}
|
||||||
/>
|
/>
|
||||||
<InputSelect
|
<Select
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
name={`${index}.filter`}
|
name={`${index}.filter`}
|
||||||
selectOptions={filtersOptions}
|
options={filtersOptions.map(option => (
|
||||||
|
<Option {...option} key={option.value} />
|
||||||
|
))}
|
||||||
style={styles.selectMiddle}
|
style={styles.selectMiddle}
|
||||||
value={get(modifiedData, [index, 'filter'], '')}
|
value={get(modifiedData, [index, 'filter'], '')}
|
||||||
/>
|
/>
|
||||||
@ -69,7 +71,7 @@ function FilterPickerOption({
|
|||||||
type={type}
|
type={type}
|
||||||
name={`${index}.value`}
|
name={`${index}.value`}
|
||||||
value={get(modifiedData, [index, 'value'], '')}
|
value={get(modifiedData, [index, 'value'], '')}
|
||||||
selectOptions={['true', 'false']}
|
options={['true', 'false']}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
{showAddButton && <Button type="button" onClick={onClickAddFilter} />}
|
{showAddButton && <Button type="button" onClick={onClickAddFilter} />}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user