Skip to content

Commit

Permalink
Merge stable into master
Browse files Browse the repository at this point in the history
  • Loading branch information
CKTravisBot authored Jun 1, 2023
2 parents 6c7ffe6 + 0447e91 commit dfdddc2
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 132 deletions.
2 changes: 1 addition & 1 deletion docs/installation/advanced/csp.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Some CSP directives have an impact on certain rich-text editor features. Here is
* `default-src 'none'`: Resets the policy and blocks everything. All successive directives work as a whitelist. By itself, as long as it is followed by other directives, it has no impact on the editor.
* `connect-src 'self'`
* Allows the {@link features/image-upload editor upload features} to use [`XMLHttpReqests`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) (Ajax) to upload files to the server, for instance, when an image is pasted or dropped into the editor content. The `'self`' value ensures the requests remain within the same host.
* Allows {@link installation/getting-started/getting-and-setting-data#autosave-feature auto–saving editor data} using `XMLHttpRequest`.
* Allows {@link features/autosave auto–saving editor data} using `XMLHttpRequest`.

**Note**: To use [CKEditor Cloud Services](https://ckeditor.com/ckeditor-cloud-services/), include the `http://*.cke-cs.com` domain in the `connect-src` directive, for instance: `connect-src 'self' http://*.cke-cs.com`.
* `script-src 'self'`: Allows the execution of JavaScript from the current host only and can be applied only if the CKEditor 5 script file (`<script src="[ckeditor-build-path]/ckeditor.js"></script>`) is also served from that host.
Expand Down
8 changes: 3 additions & 5 deletions docs/installation/getting-started/editor-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ Most of the examples in the documentation use the `ClassicEditor` class, but thi
A CKEditor 5 build compiles a specific editor class and a set of plugins. Using builds is the simplest way to include the editor in your application, but you can also {@link installation/advanced/integrating-from-source-webpack use the editor classes and plugins directly} for greater flexibility.
</info-box>

{@snippet installation/getting-and-setting-data/build-autosave-source}

## Creating an editor with `create()`

Regardless of the chosen build, creating an editor is done using the static `create()` method.
Expand Down Expand Up @@ -54,7 +52,7 @@ ClassicEditor
} );
```

In this case the `<textarea>` element is hidden and replaced with an editor. The `<textarea>` data is used to initialize the editor content. A `<div>` element can be used in the same way.
In this case, the `<textarea>` element is hidden and replaced with an editor. The `<textarea>` data is used to initialize the editor content. A `<div>` element can be used in the same way.

### Example – inline editor

Expand All @@ -81,7 +79,7 @@ InlineEditor

### Example – balloon editor

The procedure is the same as for inline editor. The only difference is that you need to use the {@link module:editor-balloon/ballooneditor~BalloonEditor#create `BalloonEditor.create()`} method.
The procedure is the same as for the inline editor. The only difference is that you need to use the {@link module:editor-balloon/ballooneditor~BalloonEditor#create `BalloonEditor.create()`} method.

Add an element where CKEditor should initialize to your page:

Expand Down Expand Up @@ -134,7 +132,7 @@ DecoupledEditor
```

<info-box tip>
Every editor class may accept different parameters in the `create()` method and may handle the initialization differently. For instance, classic editor will replace the given element with an editor, while inline editor will use the given element to initialize an editor on it. See each editor's documentation to learn the details.
Every editor class may accept different parameters in the `create()` method and may handle the initialization differently. For instance, the classic editor will replace the given element with an editor, while the inline editor will use the given element to initialize an editor on it. See each editor's documentation to learn the details.

The interface of the editor class is not enforced either. Since different implementations of editors may vary heavily in terms of functionality, the editor class implementers have full freedom regarding the API. Therefore, the examples in this guide may not work with some editor classes.
</info-box>
Expand Down
125 changes: 7 additions & 118 deletions docs/installation/getting-started/getting-and-setting-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ order: 70
In the {@link installation/getting-started/editor-lifecycle previous tutorial} you have learned about lifecycle methods. Having the editor created, you can now set or get its data.
</info-box>

CKEditor 5 allows you to retrieve the data from and save it to your server (or to your system in general) in various ways. In this guide you can learn about the available options along with their pros and cons.
CKEditor 5 allows you to retrieve the data from and save it to your server (or to your system in general) in various ways. In this guide, you can learn about the available options along with their pros and cons.

## Automatic integration with HTML forms

Expand Down Expand Up @@ -87,7 +87,7 @@ In your HTTP server, you can now read the editor data from the `content` variabl
<textarea><p>This is some sample content.</p></textarea>
```

While simple content like mentioned above does not itself require to be encoded, encoding the data will prevent losing text like "&lt;" or "&lt;img&gt;".
While simple content like that mentioned above does not itself require to be encoded, encoding the data will prevent losing text like "&lt;" or "&lt;img&gt;".
</info-box>

## Manually retrieving the data
Expand Down Expand Up @@ -115,7 +115,7 @@ To replace the editor content with new data, use the `setData()` method:
editor.setData( '<p>Some text.</p>' );
```

For that, you need to store the reference to the `editor` because &mdash; unlike in CKEditor 4 &mdash; there is no global `CKEDITOR.instances` property. You can do that in multiple ways, for example by assigning the `editor` to a variable defined outside the `then()`'s callback:
For that, you need to store the reference to the `editor` because there is no global `CKEDITOR.instances` property. You can do that in multiple ways, for example by assigning the `editor` to a variable defined outside the `then()`'s callback:

```js
let editor;
Expand Down Expand Up @@ -149,129 +149,18 @@ ClassicEditor.create( document.querySelector( '#editor' ), {
```

<info-box warning>
Enabling the `updateSourceElementOnDestroy` option in your configuration might have some security implications, depending on plugins you use. While the editing view is secured, there might be some unsafe content in the data output, so enable this option only if you know what you are doing. Be especially careful when using the Markdown, General HTML Support and HTML embed features.
Enabling the `updateSourceElementOnDestroy` option in your configuration might have some security implications, depending on the plugins you use. While the editing view is secured, there might be some unsafe content in the data output, so enable this option only if you know what you are doing. Be especially careful when using the Markdown, General HTML Support, and HTML embed features.
</info-box>

## Autosave feature

The {@link module:autosave/autosave~Autosave} feature allows you to automatically save the data (e.g. send it to the server) when needed (when the user changed the content).
The {@link module:autosave/autosave~Autosave} feature allows you to automatically save the data (e.g. send it to the server) when needed. This can happen, for example, when the user changed the content.

<info-box>
This plugin is unavailable in any of the builds by default so you need to {@link installation/plugins/installing-plugins install it}.
</info-box>

Assuming that you implemented a `saveData()` function that sends the data to your server and returns a promise which is resolved once the data is successfully saved, configuring the autosave feature is as simple as:

```js
ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [
Autosave,

// ... other plugins
],

autosave: {
save( editor ) {
return saveData( editor.getData() );
}
},

// ... other configuration options
} );
```

The autosave feature listens to the {@link module:engine/model/document~Document#event:change:data `editor.model.document#change:data`} event, throttles it and executes the `config.autosave.save()` function.

It also listens to the native [`window#beforeunload`](https://developer.mozilla.org/en-US/docs/Web/Events/beforeunload) event and blocks it in the following cases:

* The data has not been saved yet (the `save()` function did not resolve its promise or it was not called yet due to throttling).
* Or any of the editor features registered a {@link module:core/pendingactions~PendingActions "pending action"} (e.g. that an image is being uploaded).

This automatically secures you from the user leaving the page before the content is saved or some ongoing actions like image upload did not finish.

The minimum time period between two save actions can be configured using the {@link module:autosave/autosave~AutosaveConfig#waitingTime `config.waitingTime`} property to not overload the backend. 1 second is the default waiting time before the next save action if nothing has changed in the meantime after the editor data has changed.

```js
ClassicEditor
.create( document.querySelector( '#editor' ), {
autosave: {
waitingTime: 5000, // in ms
save( editor ) {}
},

// ... other configuration options
} );
```

### Demo

This demo shows a simple integration of the editor with a fake HTTP server (which needs 1000ms to save the content).

```js
ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [
Autosave,

// ... other plugins
],

autosave: {
save( editor ) {
return saveData( editor.getData() );
}
}
} )
.then( editor => {
window.editor = editor;

displayStatus( editor );
} )
.catch( err => {
console.error( err.stack );
} );

// Save the data to a fake HTTP server (emulated here with a setTimeout()).
function saveData( data ) {
return new Promise( resolve => {
setTimeout( () => {
console.log( 'Saved', data );

resolve();
}, HTTP_SERVER_LAG );
} );
}

// Update the "Status: Saving..." info.
function displayStatus( editor ) {
const pendingActions = editor.plugins.get( 'PendingActions' );
const statusIndicator = document.querySelector( '#editor-status' );

pendingActions.on( 'change:hasAny', ( evt, propertyName, newValue ) => {
if ( newValue ) {
statusIndicator.classList.add( 'busy' );
} else {
statusIndicator.classList.remove( 'busy' );
}
} );
}
```

How to understand this demo:

* The status indicator shows when the editor has some unsaved content or pending actions.
* If you drop a big image into this editor, you will see that it is busy during the entire period when the image is being uploaded.
* The editor is also busy when saving the content is in progress (the `save()`'s promise was not resolved).
* The autosave feature has a throttling mechanism which groups frequent changes (e.g. typing) are grouped in batches.
* The autosave itself does not check whether the data has really changed. It bases on changes in the model which, in special cases, may not be "visible" in the data. You can add such a check yourself if you would like to avoid sending the same data to the server twice in a row.
* You will be asked whether you want to leave the page if an image is being uploaded or the data has not been saved successfully yet. You can test that by dropping a big image into the editor or changing the "HTTP server lag" to a high value (e.g. 9000ms) and typing something. These actions will make the editor "busy" for a longer time &mdash; try leaving the page then.

{@snippet installation/getting-and-setting-data/autosave}
Please refer to the {@link features/autosave Autosave} guide for details.

## Handling users exiting the page

An additional concern when integrating the editor in your website is that the user may mistakenly leave before saving the data. This problem is automatically handled by the [autosave feature](#autosave-feature) described above, but if you do not use it and instead chose different integration methods, you should consider handling these two scenarios:
An additional concern when integrating the editor in your website is that the user may mistakenly leave before saving the data. This problem is automatically handled by the {@link features/autosave autosave feature}, but if you do not use it and instead chose different integration methods, you should consider handling these two scenarios:

* The user leaves the page before saving the data (e.g. mistakenly closes a tab or clicks some link).
* The user saved the data, but there are some pending actions like an image upload.
Expand Down
1 change: 1 addition & 0 deletions docs/umberto.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@
"installation/getting-started/frameworks/react.html": "installation/frameworks/react.html",
"installation/getting-started/frameworks/vuejs-v2.html": "installation/integrations/vuejs-v2.html",
"installation/getting-started/frameworks/vuejs-v3.html": "installation/integrations/vuejs-v3.html",
"installation/getting-started/getting-and-setting-data.html#autosave-feature": "features/autosave.html",
"installation/getting-started/installing-plugins.html": "installation/plugins/installing-plugins.html",
"installation/getting-started/maintenance.html": "updating/maintaining.html",
"installation/getting-started/predefined-builds.html#build-customization": "installation/getting-started/quick-start-other.html#build-customization",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div id="snippet-autosave">
<p>Type some text to test the <a href="#autosave-feature">autosave</a> feature.</p>
<p>Type some text or input some rich content to test the autosave feature.</p>
</div>

<div id="snippet-autosave-header">
Expand All @@ -19,7 +19,7 @@

<p>Server data:</p>

<pre class="highlight language-html" id="snippet-autosave-console">Type some text to test the &#x3C;a href=&#x22;#autosave-feature&#x22;&#x3E;autosave&#x3C;/a&#x3E; feature.</pre>
<pre class="highlight language-html" id="snippet-autosave-console">Type some text or input some rich content to test the autosave feature.</pre>

<style>
#snippet-autosave-header {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* This is the additional demo snippet.
* The base snippet sits in the /_snippets/installation/getting-and-setting-data folder.
* This is due to legacy reasons.
*/

/* globals ClassicEditor, console, window, document, setTimeout */

import { CS_CONFIG } from '@ckeditor/ckeditor5-cloud-services/tests/_utils/cloud-services-config';
Expand Down
2 changes: 1 addition & 1 deletion packages/ckeditor5-autosave/docs/api/autosave.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This package implements the autosaving feature for CKEditor 5.

## Documentation

See the {@link installation/getting-started/getting-and-setting-data#autosave-feature autosave feature guide} and the {@link module:autosave/autosave~Autosave} plugin documentation.
See the {@link features/autosave Autosave feature guide} and the {@link module:autosave/autosave~Autosave} plugin documentation.

## Installation

Expand Down
Loading

0 comments on commit dfdddc2

Please sign in to comment.