From 1d4a193f7eb0b0ac79aa3ae6e3cf36408ab39d54 Mon Sep 17 00:00:00 2001 From: Johann Pinson Date: Tue, 19 Jun 2018 11:13:32 +0200 Subject: [PATCH 01/65] feat(graphql): add DateTime support instead of String --- packages/strapi-plugin-graphql/package.json | 3 ++- packages/strapi-plugin-graphql/services/GraphQL.js | 13 +++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/strapi-plugin-graphql/package.json b/packages/strapi-plugin-graphql/package.json index b048fa98e2..c8e35cade1 100644 --- a/packages/strapi-plugin-graphql/package.json +++ b/packages/strapi-plugin-graphql/package.json @@ -27,7 +27,8 @@ "graphql-depth-limit": "^1.1.0", "graphql-playground-middleware-koa": "^1.6.1", "graphql-tools": "^2.23.1", - "graphql-type-json": "^0.2.0", + "graphql-type-json": "^0.2.1", + "graphql-type-datetime": "^0.2.1", "pluralize": "^7.0.0", "strapi-utils": "3.0.0-alpha.12.4" }, diff --git a/packages/strapi-plugin-graphql/services/GraphQL.js b/packages/strapi-plugin-graphql/services/GraphQL.js index 0e43ea9674..c345637d1e 100644 --- a/packages/strapi-plugin-graphql/services/GraphQL.js +++ b/packages/strapi-plugin-graphql/services/GraphQL.js @@ -14,6 +14,7 @@ const pluralize = require('pluralize'); const graphql = require('graphql'); const { makeExecutableSchema } = require('graphql-tools'); const GraphQLJSON = require('graphql-type-json'); +const GraphQLDateTime = require('graphql-type-datetime'); const policyUtils = require('strapi-utils').policy; module.exports = { @@ -170,6 +171,9 @@ module.exports = { case 'float': type = 'Float'; break; + case 'date': + type = 'DateTime'; + break; case 'enumeration': type = this.convertEnumType(definition, modelName, attributeName); break; @@ -453,8 +457,8 @@ module.exports = { // Add timestamps attributes. if (_.get(model, 'options.timestamps') === true) { Object.assign(initialState, { - createdAt: 'String!', - updatedAt: 'String!' + createdAt: 'DateTime!', + updatedAt: 'DateTime!' }); Object.assign(acc.resolver[globalId], { @@ -777,10 +781,11 @@ module.exports = { addCustomScalar: (resolvers) => { Object.assign(resolvers, { - JSON: GraphQLJSON + JSON: GraphQLJSON, + DateTime: GraphQLDateTime, }); - return 'scalar JSON'; + return 'scalar JSON \n scalar DateTime'; }, /** From 34aef5bbc6e82c3a8251ebd47b3e4a9e0fa930a2 Mon Sep 17 00:00:00 2001 From: Johann Pinson Date: Wed, 20 Jun 2018 10:09:34 +0200 Subject: [PATCH 02/65] fix(datetime): update npm dependencies --- packages/strapi-plugin-graphql/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/strapi-plugin-graphql/package.json b/packages/strapi-plugin-graphql/package.json index c8e35cade1..91e065a726 100644 --- a/packages/strapi-plugin-graphql/package.json +++ b/packages/strapi-plugin-graphql/package.json @@ -28,7 +28,7 @@ "graphql-playground-middleware-koa": "^1.6.1", "graphql-tools": "^2.23.1", "graphql-type-json": "^0.2.1", - "graphql-type-datetime": "^0.2.1", + "graphql-type-datetime": "^0.2.2", "pluralize": "^7.0.0", "strapi-utils": "3.0.0-alpha.12.4" }, From 6bbc5c899333c8d16f7de3e1afbabbc9f840f31c Mon Sep 17 00:00:00 2001 From: Johann Pinson Date: Wed, 20 Jun 2018 19:08:58 +0200 Subject: [PATCH 03/65] fix(datetime): add `datetime` and `timestamp` detection --- packages/strapi-plugin-graphql/services/GraphQL.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/strapi-plugin-graphql/services/GraphQL.js b/packages/strapi-plugin-graphql/services/GraphQL.js index c345637d1e..e240236ec9 100644 --- a/packages/strapi-plugin-graphql/services/GraphQL.js +++ b/packages/strapi-plugin-graphql/services/GraphQL.js @@ -172,6 +172,8 @@ module.exports = { type = 'Float'; break; case 'date': + case 'datetime': + case 'timestamp': type = 'DateTime'; break; case 'enumeration': From 039d9e5856e9e03607fd88c8a6f35fccdef0514a Mon Sep 17 00:00:00 2001 From: cyril lopez Date: Wed, 27 Jun 2018 19:00:19 +0200 Subject: [PATCH 04/65] Front-end config in core-store --- .../config/functions/bootstrap.js | 140 ++++++++++++++++++ .../config/layout.json | 13 +- .../models/Permission.settings.json | 1 - 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 packages/strapi-plugin-content-manager/config/functions/bootstrap.js diff --git a/packages/strapi-plugin-content-manager/config/functions/bootstrap.js b/packages/strapi-plugin-content-manager/config/functions/bootstrap.js new file mode 100644 index 0000000000..37c11afd5f --- /dev/null +++ b/packages/strapi-plugin-content-manager/config/functions/bootstrap.js @@ -0,0 +1,140 @@ +const _ = require('lodash'); +const pluralize = require('pluralize'); + +module.exports = async cb => { + const pickData = (model) => _.pick(model, [ + 'info', + 'connection', + 'collectionName', + 'attributes', + 'identity', + 'globalId', + 'globalName', + 'orm', + 'loadedModel', + 'primaryKey', + 'associations' + ]); + + const models = _.mapValues(strapi.models, pickData); + delete models['core_store']; + const pluginsModel = Object.keys(strapi.plugins).reduce((acc, current) => { + acc[current] = { + models: _.mapValues(strapi.plugins[current].models, pickData), + }; + + return acc; + }, {}); + + // Init schema + const schema = { plugins: {} }; + + const buildSchema = (model, name, plugin = false) => { + // Model data + const schemaModel = { + label: _.upperFirst(name), + labelPlural: _.upperFirst(pluralize(name)), + orm: model.orm || 'mongoose', + }; + + // Fields (non relation) + schemaModel.fields = _.mapValues(_.pickBy(model.attributes, attribute => + !attribute.model && !attribute.collection + ), (value, attribute) => ({ + label: _.upperFirst(attribute), + description: '', + type: value.type || 'string', + })); + + // Select fields displayed in list view + // schemaModel.list = _.slice(_.keys(schemaModel.fields), 0, 4); + schemaModel.listDisplay = Object.keys(schemaModel.fields) + // Construct Array of attr ex { type: 'string', label: 'Foo', name: 'Foo', description: '' } + .map(attr => Object.assign(schemaModel.fields[attr], { name: attr })) + // Retrieve only the fourth first items + .slice(0, 4); + + if (model.associations) { + // Model relations + schemaModel.relations = model.associations.reduce((acc, current) => { + const displayedAttribute = current.plugin ? + _.get(pluginsModel, [current.plugin, 'models', current.model || current.collection, 'info', 'mainField']) || + _.findKey(_.get(pluginsModel, [current.plugin, 'models', current.model || current.collection, 'attributes']), { type : 'string'}) || + 'id' : + _.get(models, [current.model || current.collection, 'info', 'mainField']) || + _.findKey(_.get(models, [current.model || current.collection, 'attributes']), { type : 'string'}) || + 'id'; + + acc[current.alias] = { + ...current, + description: '', + displayedAttribute, + }; + + return acc; + }, {}); + } + + if (plugin) { + return _.set(schema.plugins, `${plugin}.${name}`, schemaModel); + } + + // Set the formatted model to the schema + schema[name] = schemaModel; + }; + + _.forEach(pluginsModel, (plugin, pluginName) => { + _.forEach(plugin.models, (model, name) => { + buildSchema(model, name, pluginName); + }); + }); + + // Generate schema for models. + _.forEach(models, (model, name) => { + buildSchema(model, name); + }); + + const pluginStore = strapi.store({ + environment: strapi.config.environment, + type: 'plugin', + name: 'content-manager' + }); + + const buildSchemaKeys = (data) => Object.keys(data).reduce((acc, curr) => { + + if (curr !== 'plugins') { + + if (!data[curr].fields && _.isObject(data[curr])) { + return buildSchemaKeys(data[curr]); + } + + return acc.concat([{ [curr]: Object.keys(data[curr].fields) }]); + } + + return buildSchemaKeys(data[curr]); + }, []); + + try { + const prevSchema = await pluginStore.get({ key: 'schema' }); + + if (!prevSchema) { + pluginStore.set({ key: 'schema', value: schema }); + cb(); + } + + const prevSchemaKeys = buildSchemaKeys(prevSchema); + const schemaKeys = buildSchemaKeys(schema); + + // Update the store with the new created APIs + if (!_.isEqual(prevSchemaKeys, schemaKeys)) { + pluginStore.set({ key: 'schema', value: _.merge(schema, prevSchema) }); + } + + + } catch(err) { + console.log('error', err); + } + + + cb(); +}; \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/config/layout.json b/packages/strapi-plugin-content-manager/config/layout.json index 9e26dfeeb6..53e5def681 100644 --- a/packages/strapi-plugin-content-manager/config/layout.json +++ b/packages/strapi-plugin-content-manager/config/layout.json @@ -1 +1,12 @@ -{} \ No newline at end of file +{ + "product": { + "attributes": { + "name": { + "appearance": "" + }, + "json": { + "appearance": "" + } + } + } +} \ No newline at end of file diff --git a/packages/strapi-plugin-users-permissions/models/Permission.settings.json b/packages/strapi-plugin-users-permissions/models/Permission.settings.json index bd9acae178..2d037f3c70 100644 --- a/packages/strapi-plugin-users-permissions/models/Permission.settings.json +++ b/packages/strapi-plugin-users-permissions/models/Permission.settings.json @@ -27,7 +27,6 @@ }, "policy": { "type": "string", - "required": true, "configurable": false }, "role": { From 4aa46c5d0fd97031d25d5c39c2eb9d8906e40875 Mon Sep 17 00:00:00 2001 From: cyril lopez Date: Thu, 28 Jun 2018 09:31:17 +0200 Subject: [PATCH 05/65] Store config in core_store --- .../config/functions/bootstrap.js | 11 ++++++++--- .../strapi-plugin-content-manager/config/layout.json | 3 +++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/strapi-plugin-content-manager/config/functions/bootstrap.js b/packages/strapi-plugin-content-manager/config/functions/bootstrap.js index 37c11afd5f..90a4836412 100644 --- a/packages/strapi-plugin-content-manager/config/functions/bootstrap.js +++ b/packages/strapi-plugin-content-manager/config/functions/bootstrap.js @@ -35,6 +35,11 @@ module.exports = async cb => { label: _.upperFirst(name), labelPlural: _.upperFirst(pluralize(name)), orm: model.orm || 'mongoose', + search: true, + filters: true, + bulkActions: true, + pageEntries: 20, + defaultSort: 'id' }; // Fields (non relation) @@ -50,10 +55,11 @@ module.exports = async cb => { // schemaModel.list = _.slice(_.keys(schemaModel.fields), 0, 4); schemaModel.listDisplay = Object.keys(schemaModel.fields) // Construct Array of attr ex { type: 'string', label: 'Foo', name: 'Foo', description: '' } - .map(attr => Object.assign(schemaModel.fields[attr], { name: attr })) + // NOTE: Do we allow sort on boolean? + .map(attr => Object.assign(schemaModel.fields[attr], { name: attr, sortable: true, searchable: true })) // Retrieve only the fourth first items .slice(0, 4); - + if (model.associations) { // Model relations schemaModel.relations = model.associations.reduce((acc, current) => { @@ -101,7 +107,6 @@ module.exports = async cb => { }); const buildSchemaKeys = (data) => Object.keys(data).reduce((acc, curr) => { - if (curr !== 'plugins') { if (!data[curr].fields && _.isObject(data[curr])) { diff --git a/packages/strapi-plugin-content-manager/config/layout.json b/packages/strapi-plugin-content-manager/config/layout.json index 53e5def681..430ecb52f2 100644 --- a/packages/strapi-plugin-content-manager/config/layout.json +++ b/packages/strapi-plugin-content-manager/config/layout.json @@ -6,6 +6,9 @@ }, "json": { "appearance": "" + }, + "bool": { + "appearance": "" } } } From f09cace58211a602ee4507d6f2206d371b66628a Mon Sep 17 00:00:00 2001 From: cyril lopez Date: Thu, 28 Jun 2018 09:46:10 +0200 Subject: [PATCH 06/65] Remove JSON & Wysiwyg from helper-plugin and move them to content-manager --- .../lib/src/components/InputJSON/index.js | 201 ----- .../lib/src/components/InputJSON/jsonlint.js | 424 ---------- .../lib/src/components/InputJSON/styles.scss | 40 - .../components/InputJSONWithErrors/index.js | 227 ----- .../InputJSONWithErrors/styles.scss | 5 - .../lib/src/components/InputsIndex/index.js | 4 - .../components/Wysiwyg/componentsStyles.scss | 209 ----- .../lib/src/components/Wysiwyg/constants.js | 98 --- .../lib/src/components/Wysiwyg/converter.js | 19 - .../src/components/Wysiwyg/customSelect.js | 42 - .../lib/src/components/Wysiwyg/helpers.js | 116 --- .../lib/src/components/Wysiwyg/image.js | 22 - .../lib/src/components/Wysiwyg/index.js | 792 ------------------ .../lib/src/components/Wysiwyg/link.js | 45 - .../src/components/Wysiwyg/previewControl.js | 29 - .../src/components/Wysiwyg/previewWysiwyg.js | 261 ------ .../lib/src/components/Wysiwyg/strategies.js | 35 - .../lib/src/components/Wysiwyg/styles.scss | 115 --- .../lib/src/components/Wysiwyg/toggleMode.js | 36 - .../lib/src/components/Wysiwyg/utils.js | 74 -- .../lib/src/components/Wysiwyg/video.js | 26 - .../components/WysiwygBottomControls/index.js | 57 -- .../WysiwygBottomControls/styles.scss | 41 - .../src/components/WysiwygDropUpload/index.js | 26 - .../components/WysiwygDropUpload/styles.scss | 12 - .../lib/src/components/WysiwygEditor/index.js | 27 - .../components/WysiwygInlineControls/index.js | 111 --- .../WysiwygInlineControls/styles.scss | 123 --- .../components/WysiwygWithErrors/Loadable.js | 7 - .../components/WysiwygWithErrors/Loader.js | 11 - .../src/components/WysiwygWithErrors/index.js | 222 ----- .../components/WysiwygWithErrors/styles.scss | 24 - packages/strapi-helper-plugin/package.json | 5 +- .../admin/src/components/Edit/index.js | 4 + .../config/layout.json | 3 + .../package.json | 5 +- 36 files changed, 12 insertions(+), 3486 deletions(-) delete mode 100644 packages/strapi-helper-plugin/lib/src/components/InputJSON/index.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/InputJSON/jsonlint.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/InputJSON/styles.scss delete mode 100644 packages/strapi-helper-plugin/lib/src/components/InputJSONWithErrors/index.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/InputJSONWithErrors/styles.scss delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/componentsStyles.scss delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/constants.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/converter.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/customSelect.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/helpers.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/image.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/index.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/link.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/previewControl.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/previewWysiwyg.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/strategies.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/styles.scss delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/toggleMode.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/utils.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/Wysiwyg/video.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/WysiwygBottomControls/index.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/WysiwygBottomControls/styles.scss delete mode 100644 packages/strapi-helper-plugin/lib/src/components/WysiwygDropUpload/index.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/WysiwygDropUpload/styles.scss delete mode 100644 packages/strapi-helper-plugin/lib/src/components/WysiwygEditor/index.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/WysiwygInlineControls/index.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/WysiwygInlineControls/styles.scss delete mode 100644 packages/strapi-helper-plugin/lib/src/components/WysiwygWithErrors/Loadable.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/WysiwygWithErrors/Loader.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/WysiwygWithErrors/index.js delete mode 100644 packages/strapi-helper-plugin/lib/src/components/WysiwygWithErrors/styles.scss diff --git a/packages/strapi-helper-plugin/lib/src/components/InputJSON/index.js b/packages/strapi-helper-plugin/lib/src/components/InputJSON/index.js deleted file mode 100644 index 199287766e..0000000000 --- a/packages/strapi-helper-plugin/lib/src/components/InputJSON/index.js +++ /dev/null @@ -1,201 +0,0 @@ -/** - * - * InputJSON - * - */ - - -import React from 'react'; -import PropTypes from 'prop-types'; -import cm from 'codemirror'; -import 'codemirror/lib/codemirror.css'; -import 'codemirror/mode/javascript/javascript'; -import 'codemirror/addon/lint/lint'; -import 'codemirror/addon/lint/javascript-lint'; -import 'codemirror/addon/edit/closebrackets'; -import 'codemirror/addon/selection/mark-selection'; -import 'codemirror/theme/liquibyte.css'; -import 'codemirror/theme/xq-dark.css'; -import 'codemirror/theme/3024-day.css'; -import 'codemirror/theme/3024-night.css'; -import 'codemirror/theme/blackboard.css'; -import 'codemirror/theme/monokai.css'; -import 'codemirror/theme/cobalt.css'; - -import { isEmpty, isObject, trimStart } from 'lodash'; -import jsonlint from './jsonlint'; -import styles from './styles.scss'; - -const WAIT = 600; -const stringify = JSON.stringify; -const parse = JSON.parse; -const DEFAULT_THEME = 'monokai'; -const THEMES = ['blackboard', 'cobalt', 'monokai', '3024-day', '3024-night', 'liquibyte', 'xq-dark']; - -class InputJSON extends React.Component { - constructor(props) { - super(props); - this.editor = React.createRef(); - this.state = { error: false, markedText: null }; - } - - componentDidMount() { - // Init codemirror component - this.codeMirror = cm.fromTextArea(this.editor.current, { - autoCloseBrackets: true, - lineNumbers: true, - matchBrackets: true, - mode: 'application/json', - smartIndent: true, - styleSelectedText: true, - tabSize: 2, - theme: DEFAULT_THEME, - }); - this.codeMirror.on('change', this.handleChange); - this.codeMirror.on('blur', this.handleBlur); - - this.setSize(); - this.setInitValue(); - } - - componentDidUpdate(prevProps) { - if (isEmpty(prevProps.value) && !isEmpty(this.props.value) && !this.state.hasInitValue) { - this.setInitValue(); - } - } - - setInitValue = () => { - const { value } = this.props; - - if (isObject(value) && value !== null) { - try { - parse(stringify(value)); - this.setState({ hasInitValue: true }); - - return this.codeMirror.setValue(stringify(value, null, 2)); - } catch(err) { - - return this.setState({ error: true }); - } - } - } - - setSize = () => this.codeMirror.setSize('100%', 'auto'); - - setTheme = (theme) => this.codeMirror.setOption('theme', theme); - - getContentAtLine = (line) => this.codeMirror.getLine(line); - - getEditorOption = (opt) => this.codeMirror.getOption(opt); - - getValue = () => this.codeMirror.getValue(); - - markSelection = ({ message }) => { - let line = parseInt( - message - .split(':')[0] - .split('line ')[1], - 10, - ) - 1; - - let content = this.getContentAtLine(line); - - if (content === '{') { - line = line + 1; - content = this.getContentAtLine(line); - } - const chEnd = content.length; - const chStart = chEnd - trimStart(content, ' ').length; - const markedText = this.codeMirror.markText({ line, ch: chStart }, { line, ch: chEnd }, { className: styles.colored }); - this.setState({ markedText }); - } - - timer = null; - - handleBlur = ({ target }) => { - const { name, onBlur } = this.props; - - if (target === undefined) { // codemirror catches multiple events - onBlur({ - target: { - name, - type: 'json', - value: this.getValue(), - }, - }); - - } - } - - handleChange = () => { - const { hasInitValue } = this.state; - const { name, onChange } = this.props; - let value = this.codeMirror.getValue(); - - try { - value = parse(value); - } catch(err) { - // Silent - } - - // Update the parent - onChange({ - target: { - name, - value, - type: 'json', - }, - }); - - if (!hasInitValue) { - this.setState({ hasInitValue: true }); - } - - // Remove higlight error - if (this.state.markedText) { - this.state.markedText.clear(); - this.setState({ markedText: null, error: null }); - } - - clearTimeout(this.timer); - this.timer = setTimeout(() => this.testJSON(this.codeMirror.getValue()), WAIT); - } - - testJSON = (value) => { - try { - jsonlint.parse(value); - } catch(err) { - this.markSelection(err); - } - } - - render() { - if (this.state.error) { - return
error json
; - } - - return ( -
-