From 5a3276c0d5815d6592e797c5b08b046549426205 Mon Sep 17 00:00:00 2001 From: Kacper Tomporowski Date: Mon, 2 Dec 2024 15:19:00 +0100 Subject: [PATCH] Release v1.1.0 --- CHANGELOG.md | 12 +++ README.md | 8 +- .../components/CKEditorInput/Configurator.js | 13 ++- admin/src/components/CKEditorInput/index.jsx | 6 +- .../plugins/StrapiEditorUsageData.js | 9 ++ .../src/components/CKEditorProvider/index.jsx | 101 +++++------------- admin/src/index.jsx | 16 +++ package.json | 4 +- yarn.lock | 20 ++-- 9 files changed, 97 insertions(+), 92 deletions(-) create mode 100644 admin/src/components/CKEditorInput/plugins/StrapiEditorUsageData.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f642454..99e7bcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 1.1.0 (December 2, 2024) + +We are happy to announce the release of CKEditor 5 Official Integration v1.1.0. + +### Release highlights + +In this release, we updated the CKEditor 5 version to [v44.0.0](https://github.com/ckeditor/ckeditor5/blob/master/CHANGELOG.md#4400-december-2-2024), which introduces high impact updates. Starting from the plugin version 1.1.0, the custom field will require passing a valid license key. + +### BREAKING CHANGES ℹ️ + +* **CKEditor 5 custom field now requires the license key**. CKEditor 5 now requires a valid license key, which can be retrieved from [Customer Portal](https://portal.ckeditor.com/). You can sign up for a [commitment-free trial](https://portal.ckeditor.com/checkout?plan=free) and get instant access to your key. + ## 1.0.2 (September 20, 2024) ### Release highlights diff --git a/README.md b/README.md index 854e728..02becf5 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ This is an official plugin, provided to you by the [CKEditor team](https://ckedi ## 🔧 Installation +> [!IMPORTANT] +> Before installation, make sure that you own a **valid CKEditor 5 license key**. Start a [commitment-free trial](https://portal.ckeditor.com/checkout?plan=free) to get instant access to the license key. You can also refer to our [license key and activation guide](https://ckeditor.com/docs//ckeditor5/latest/getting-started/licensing/license-key-and-activation.html) to learn more. + Inside your Strapi app, add the package: With `npm`: @@ -36,7 +39,7 @@ With `yarn`: yarn add @ckeditor/strapi-plugin-ckeditor ``` -Then, add the Content Security Policy configuration to allow loading CKEditor 5 from https://cdn.ckeditor.com origin, by adding the rule to `config/middlewares.ts` in your Strapi project root: +Then, add the Content Security Policy configuration to allow loading CKEditor 5 from https://cdn.ckeditor.com origin and sending the editor usage information to https://proxy-event.ckeditor.com, by adding the rule to `config/middlewares.ts` in your Strapi project root: ```js export default [ @@ -47,7 +50,8 @@ export default [ contentSecurityPolicy: { useDefaults: true, directives: { - 'script-src': ['https://cdn.ckeditor.com'] + 'script-src': ['https://cdn.ckeditor.com'], + 'connect-src': ['https://proxy-event.ckeditor.com'] }, }, }, diff --git a/admin/src/components/CKEditorInput/Configurator.js b/admin/src/components/CKEditorInput/Configurator.js index 462e652..5b0091f 100644 --- a/admin/src/components/CKEditorInput/Configurator.js +++ b/admin/src/components/CKEditorInput/Configurator.js @@ -1,4 +1,5 @@ import { StrapiMediaLib } from "./plugins/StrapiMediaLib"; +import { StrapiEditorUsageDataPlugin } from "./plugins/StrapiEditorUsageData"; import MaximumLength from "../../vendor/ckeditor5-maximum-length/index"; import "../../vendor/ckeditor5-maximum-length/index-editor.css"; @@ -77,7 +78,8 @@ const CKEDITOR_BASE_CONFIG_FOR_PRESETS = { TableColumnResize, TableCaption, WordCount, - StrapiMediaLib + StrapiMediaLib, + StrapiEditorUsageDataPlugin ], toolbar: [ 'undo', 'redo', @@ -158,7 +160,8 @@ const CKEDITOR_BASE_CONFIG_FOR_PRESETS = { TableColumnResize, TableCaption, WordCount, - StrapiMediaLib + StrapiMediaLib, + StrapiEditorUsageDataPlugin ], toolbar: [ 'undo', 'redo', @@ -264,7 +267,8 @@ const CKEDITOR_BASE_CONFIG_FOR_PRESETS = { TableCaption, WordCount, Highlight, - StrapiMediaLib + StrapiMediaLib, + StrapiEditorUsageDataPlugin ], toolbar: { items: [ @@ -408,6 +412,9 @@ export default class Configurator { const maxLength = this.fieldConfig.maxLength; const outputOption = this.fieldConfig.options.output; + const licenseKey = this.fieldConfig.licenseKey; + + config.licenseKey = licenseKey; if ( outputOption === 'Markdown' ) { config.plugins.push( Markdown ); diff --git a/admin/src/components/CKEditorInput/index.jsx b/admin/src/components/CKEditorInput/index.jsx index aa5b8d0..b11c701 100644 --- a/admin/src/components/CKEditorInput/index.jsx +++ b/admin/src/components/CKEditorInput/index.jsx @@ -31,8 +31,8 @@ const CKEditorInput = ( props ) => { const { onChange, value } = useField( name ); const [ editorInstance, setEditorInstance ] = useState(false); const { formatMessage } = useIntl(); - const { maxLengthCharacters:maxLength , ...options } = attribute.options; - const configurator = new Configurator( { options, maxLength } ); + const { maxLengthCharacters:maxLength, licenseKey, ...options } = attribute.options; + const configurator = new Configurator( { options, maxLength, licenseKey } ); const editorConfig = configurator.getEditorConfig(); const wordCounter = useRef( null ); @@ -119,4 +119,4 @@ CKEditorInput.propTypes = { required: PropTypes.bool }; -export default CKEditorInput; +export { CKEditorInput }; diff --git a/admin/src/components/CKEditorInput/plugins/StrapiEditorUsageData.js b/admin/src/components/CKEditorInput/plugins/StrapiEditorUsageData.js new file mode 100644 index 0000000..d3d097c --- /dev/null +++ b/admin/src/components/CKEditorInput/plugins/StrapiEditorUsageData.js @@ -0,0 +1,9 @@ +import { createIntegrationUsageDataPlugin } from '@ckeditor/ckeditor5-integrations-common'; +import * as pkg from '../../../../../package.json' + +export const StrapiEditorUsageDataPlugin = createIntegrationUsageDataPlugin( + 'strapi', + { + version: pkg.version, + } +); diff --git a/admin/src/components/CKEditorProvider/index.jsx b/admin/src/components/CKEditorProvider/index.jsx index c29b8d3..1e600b3 100644 --- a/admin/src/components/CKEditorProvider/index.jsx +++ b/admin/src/components/CKEditorProvider/index.jsx @@ -1,4 +1,5 @@ -import { useState, useEffect } from 'react'; +import { memo, useEffect } from 'react'; +import { useCKEditorCloud } from '@ckeditor/ckeditor5-react'; const CKEditorProvider = ( { attribute, @@ -9,85 +10,41 @@ const CKEditorProvider = ( { description = null, error = null, intlLabel } ) => { - const [ importedEditor, setImportedEditor ] = useState( null ); + // Clean up CDN scripts after unmounting the component. useEffect( () => { - const importEditor = async () => { - const module = await import( '../CKEditorInput' ); - const CKEditorInput = module.default; - - setImportedEditor( ); - }; - - const injectAssetsFromCDN = setInterval( () => { - const CDNScript = document.querySelector( '#ckeditor5-cdn-script' ); - const CDNStyles = document.querySelector( '#ckeditor5-cdn-styles' ); - - if ( !CDNStyles ) { - _injectStylesFromCDN(); - } - - if ( window.CKEDITOR ) { - window.CKEditorCDNLoaded = true; - - importEditor(); - - clearInterval( injectAssetsFromCDN ); - - return; - } - - if ( !CDNScript ) { - _injectScriptFromCDN(); - - } - }, 100 ) - return () => { - const CDNScript = document.querySelector( '#ckeditor5-cdn-script' ); + const assets = document.querySelectorAll( '[data-injected-by="ckeditor-integration"]' ); - if ( CDNScript ) { - CDNScript.remove(); - } + assets.forEach( asset => asset.remove() ); - window.CKEditorCDNLoaded = false; + window.CKEDITOR_VERSION = null; } - }, [] ); + }, [] ) - return ( - <> - { window.CKEditorCDNLoaded && importedEditor } - - ) -} - -function _injectStylesFromCDN() { - const link = document.createElement( 'link' ); - - link.rel = 'stylesheet'; - link.href = 'https://cdn.ckeditor.com/ckeditor5/43.0.0/ckeditor5.css'; - link.id = 'ckeditor5-cdn-styles'; - - document.head.appendChild( link ); -} - -function _injectScriptFromCDN() { - const script = document.createElement( 'script' ); + const cloud = useCKEditorCloud( { + version: '44.0.0', + plugins: { + CKEditorInput: async () => ( await import('../CKEditorInput') ).CKEditorInput + } + } ); - script.src = "https://cdn.ckeditor.com/ckeditor5/43.0.0/ckeditor5.umd.js"; - script.async = true; - script.id = 'ckeditor5-cdn-script' + if ( cloud.status !== 'success' ) { + return null; + } - document.body.appendChild( script ); + return ( + + ) } -export default CKEditorProvider; +export default memo( CKEditorProvider ); diff --git a/admin/src/index.jsx b/admin/src/index.jsx index 6cb30a7..e1fe8c2 100644 --- a/admin/src/index.jsx +++ b/admin/src/index.jsx @@ -39,6 +39,18 @@ export default { }, options: { base: [ + { + intlLabel: { + id: 'ckeditor.licenseKey.label', + defaultMessage: 'License key', + }, + description: { + id: 'ckeditor.licenseKey.description', + defaultMessage: "Don't have a license key? Visit https://portal.ckeditor.com/checkout?plan=free to receive it.", + }, + name: 'options.licenseKey', + type: 'text', + }, { intlLabel: { id: 'ckeditor.preset.label', @@ -155,6 +167,10 @@ export default { id: 'ckeditor.preset.error.required', defaultMessage: 'Editor preset is required', } ), + licenseKey: yup.string().required( { + id: 'ckeditor.licenseKey.error.required', + defaultMessage: 'Editor license key is required. Visit https://portal.ckeditor.com/checkout?plan=free to receive it.', + } ), } ), }, } ); diff --git a/package.json b/package.json index e93f8c1..133e186 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ckeditor/strapi-plugin-ckeditor", - "version": "1.0.2", + "version": "1.1.0", "description": "CKEditor 5 - Official Integration for Strapi", "strapi": { "name": "ckeditor", @@ -37,6 +37,6 @@ }, "license": "MIT", "dependencies": { - "@ckeditor/ckeditor5-react": "^9.1.0" + "@ckeditor/ckeditor5-react": "^9.3.0" } } diff --git a/yarn.lock b/yarn.lock index 3233f79..3d758c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,17 +2,17 @@ # yarn lockfile v1 -"@ckeditor/ckeditor5-integrations-common@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-integrations-common/-/ckeditor5-integrations-common-1.0.0.tgz#f2f73509d029398929ee30da3ae23329de5a796a" - integrity sha512-HLToIJ7FAtKX0tu9GaGb1d39Kx0i0TFelAj2pQPiwPU/6DLgM5gi+m0WCZub+syruSonmZPONtWrrZZdUoDB/g== - -"@ckeditor/ckeditor5-react@^9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-react/-/ckeditor5-react-9.1.0.tgz#fc645a055a5c84acb41c55f0c4bffab45221d2ca" - integrity sha512-48Y8Ffe21H3+3GOvjTtSITYJdeX4BINxCHyXp5zNvhTtyAyahMwG6jCgdZl1D3lwXxSq9R0/yCDHPWeMk9KOHQ== +"@ckeditor/ckeditor5-integrations-common@^2.1.0": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-integrations-common/-/ckeditor5-integrations-common-2.2.2.tgz#c494b51ad0736d087a7bc13ec634e2d66db42d5b" + integrity sha512-SKGBBrwFFmSEZawR8P9tHGRq/l2OoqoJxy9f7j0HbDGEwIpSOsCSgH0xudD6lcEbWG4QWrCS28p5n8lgPA5elQ== + +"@ckeditor/ckeditor5-react@^9.3.0": + version "9.3.1" + resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-react/-/ckeditor5-react-9.3.1.tgz#5ed94789eb5d26bf8d185185c7dfcc57f2bfb97d" + integrity sha512-2lc1ICGCOZ0loC6DeMFwhkhrodLYUsOnC2wdgMiaXnEWRI/fU0SWBAoLbsMH7i6zpq29s+ZWMEImRVbly8SmEA== dependencies: - "@ckeditor/ckeditor5-integrations-common" "^1.0.0" + "@ckeditor/ckeditor5-integrations-common" "^2.1.0" prop-types "^15.7.2" "js-tokens@^3.0.0 || ^4.0.0":