diff --git a/.api-version b/.api-version index 84c5308f03..fa5fce04b3 100644 --- a/.api-version +++ b/.api-version @@ -1 +1 @@ -7.9.0 \ No newline at end of file +8.0.0 \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c436d9243a..976600a0d1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,34 +1,34 @@ -This page outlines how you can provide feedback and contribute to TinyMCE documentation. +This page provides guidance on submitting feedback and contributing to TinyMCE documentation. # Contributor Code of Conduct -For our Contributor Code of Conduct, visit our [Contributor Covenant Code of Conduct on GitHub](https://github.com/tinymce/tinymce-docs/blob/develop/CODE_OF_CONDUCT.md#contributor-covenant-code-of-conduct). +To review the Contributor Code of Conduct, visit the [Contributor Covenant Code of Conduct on GitHub](https://github.com/tinymce/tinymce-docs/blob/develop/CODE_OF_CONDUCT.md#contributor-covenant-code-of-conduct). -# How to contribute to TinyMCE’s documentation +# Contributing to TinyMCE documentation -We welcome people with technical writing experience to help make TinyMCE’s docs as useful to our community as possible. Although you don’t need to be an expert developer to contribute, a basic understanding of software development is helpful. +Contributions from individuals with technical writing experience are encouraged to support the clarity and usefulness of the TinyMCE documentation. While expertise in software development is not required, a basic understanding is beneficial. -What is more important is your ability to articulate complex ideas. Our goal is to help developers understand TinyMCE’s functionality as quickly as possible. +The primary requirement is the ability to communicate complex concepts clearly. The goal is to help developers understand and apply TinyMCE functionality efficiently. -Simply branch the [docs GitHub repository](https://github.com/tinymce/tinymce-docs) and submit a PR, or reach out to the [TinyMCE docs team](https://github.com/tinymce/tinymce-docs/issues/new?assignees=&labels=question&template=question.md) if you have a specific question. +To contribute, create a branch from the [docs GitHub repository](https://github.com/tinymce/tinymce-docs) and submit a pull request (PR). For documentation-specific questions, open an issue using the [TinyMCE docs team contact form](https://github.com/tinymce/tinymce-docs/issues/new?assignees=&labels=question&template=question.md). # TinyMCE documentation tech stack -The documentation is built with [Antora](https://antora.org/) and text files are written in [AsciiDoc](https://asciidoc.org/). You will find the [repo here](https://github.com/tinymce/tinymce-docs). +The documentation is built with [Antora](https://antora.org/), and text content is authored in [AsciiDoc](https://asciidoc.org/). The source repository is available on GitHub: [tinymce-docs](https://github.com/tinymce/tinymce-docs). -## Highlighting +## Syntax highlighting -Code language syntax and associated elements are highlighted using an open and closing backtick. For example, this `object` is actually enclosed with backticks like this: `object`. +Inline code elements must be enclosed in backticks. For example, to highlight `object`, use the following syntax: -Highlight words relating specifically to executable parts of software languages. Keys and their values when pertaining to objects and methods (particularly the case with TinyMCE) should have backticks. We cannot cover every use case here and contributors need to use their best judgment. +Highlight keywords that refer to executable parts of a language. Keys and values used in objects and method options (common in TinyMCE) should also be enclosed in backticks. Exercise judgment for other cases not explicitly covered here. ## Code snippets -Code snippets should be complete, executable code blocks starting with the `tinymce` object. We write code snippets using [source blocks](https://docs.asciidoctor.org/asciidoc/latest/verbatim/source-blocks/). These start by specifying the `source` style and the code snippet language in square brackets to enable syntax highlighting. The language is typically html, css, js, or ts. +Code snippets must be complete, executable blocks that begin with the `tinymce` object. Snippets are formatted using [AsciiDoc source blocks](https://docs.asciidoctor.org/asciidoc/latest/verbatim/source-blocks/), which start with a `[source,]` declaration. Supported languages include `html`, `css`, `js`, and `ts`. -Include any key values pairs and method options that are required to enable a user to test the snippet in a local HTML file. You do not need to include the `html` surrounding the `tinymce` object that would be required to make the snippet work. +Each snippet must include the key-value pairs and method options necessary to run the example in a local HTML file. It is not necessary to include the full HTML structure around the `tinymce` object. -For example, this is good: +**Valid example:** ```js [source, js] ---- @@ -40,7 +40,7 @@ For example, this is good: ---- ``` -This is not: +**Invalid example:** ```js [source, js] ---- @@ -55,7 +55,7 @@ This is not: ## URLs and links -Resources to be linked take the form of a URL following by the linked text between two brackets. Linking an external resource looks like this: +Resources must be linked using a URL followed by the linked text in square brackets. External links are formatted as follows: ``` hello, http://www.example.com[text to link] to the URL example.com. @@ -63,7 +63,7 @@ Resources to be linked take the form of a URL following by the linked text betwe ## Live demos -New live demos can be added to the [modules/ROOT/examples/live-demos directory](modules/ROOT/examples/live-demos). It then can be referenced in your doc with the following code: +New live demos can be added to the [`modules/ROOT/examples/live-demos`](modules/ROOT/examples/live-demos) directory. These demos can then be referenced in documentation using: ``` liveDemo::{sub-directory-name}[] @@ -72,20 +72,19 @@ New live demos can be added to the [modules/ROOT/examples/live-demos directory]( ## Adding metadata to a page -Every page should have a meta section immediately after the heading containing information that is both informative and system critical. If you are creating a page or editing one that currently has inadequate meta information, please read the comments next to the default meta fields below. +Each page must include a metadata section immediately following the top-level heading. This section contains essential system-level information. When creating or updating a page, ensure that the following meta fields are present and accurate: ``` - :navtitle: // Descriptive title for the navigation bar, if omitted nav defaults to title field. - :description: // Detailed description at page level for the header section under the title. - :keywords: // Comma-separated list of keywords in the content. + :navtitle: // Descriptive title for the navigation bar. If omitted, defaults to the value of the `title` field. + :description: // Detailed description at page header section. + :keywords: // Comma-separated list of relevant keywords. ``` # Recommended tools -The following tools are recommended for significant edits to the documentation. The warnings and notifications from these tools should only be used as *guides*, not as *rules*. +The following tools are recommended for significant documentation contributions. The feedback provided by these tools should serve as guidance rather than strict requirements: -- A Spelling Checker (US English). Note: Some IDE/text editor spelling - checkers work well with Pascal-case, Camel-case, and Snake-case. +- A US English spelling checker. Many IDEs and text editors support spell checking for PascalCase, camelCase, and snake_case. - Alex - [Alex.js integrations](https://alexjs.com/#integrations). @@ -94,36 +93,29 @@ The following tools are recommended for significant edits to the documentation. # Style guide -You are not required to know the style guide, **but** changes may be requested on GitHub pull requests to Tiny documentation where these guidelines are not met. +Familiarity with the style guide is not required; however, edits may be requested on pull requests if content does not align with documentation standards. -The Tiny documentation style guide is based on the [Readability Guidelines](https://readabilityguidelines.co.uk) provided by [Content Design London](https://contentdesign.london/usability/readability-guidelines/). We have chosen this guide because it’s: +The TinyMCE documentation style guide is based on the [Readability Guidelines](https://readabilityguidelines.co.uk) provided by [Content Design London](https://contentdesign.london/usability/readability-guidelines/). This guide was selected due to its community-driven approach and strong focus on readability. -- open to the community - -- focused on readability. - -The information in the following subsections gives an overview of key points and lists both exceptions and additions. Please follow the guidelines on this page when they differ from the linked information. +The following subsections provide key highlights, exceptions, and additions to the source guidelines. When inconsistencies arise, defer to the guidelines listed on this page. ## Keep it simple -Use short, simple words where possible. Use formal language. +Favor short, simple words. Use formal language. -Do not use: +Avoid the following: -- Long sentences. +- Slang (e.g., *there you go*) -- Slang; such as *there you go*. +- Jargon (e.g., *leverage*, *streamline*) -- Jargon; such as *leverage* and *streamline*. +- Ambiguous contractions (e.g., *there’d*, *it’ll*) -- Ambiguous contractions; such as *there’d*, and *it’ll*. +- Latin terms (e.g., *i.e.*, *e.g.*, *etc.*, *vs.*, *via*) -- Latin terms; such as *i.e.*, *e.g.*, *etc.*, *vs.*, and *via*. +- Metaphors (e.g., *cherry picking*, *nutshell*) -- Metaphors; such as *cherry picking* and *nutshell*. - -- Complex or specialist terms; such as *chrome* (the toolbar, menu - bar, status bar) and *upstream*. +- Complex or specialist terms (e.g., *chrome* as in UI elements, *upstream*) For guidelines on using specialist terms, see: [Content Design London: Readability Guidelines - Specialist terms](https://readabilityguidelines.co.uk/clear-language/specialist-terms/). @@ -137,19 +129,19 @@ For information on: For a short list of some commonly used Latin terms, see: [Australian Government Style Manual - Latin shortened forms](https://www.stylemanual.gov.au/format-writing-and-structure/clear-language-and-writing-style/plain-english-and-word-choice/latin-shortened-forms). -## Use US English (en\_US) spelling +## Use US English (en_US) spelling -Use United States English. For example: +Use United States English spelling conventions. For example: -- "behavior" rather than "behaviour". +- "behavior" instead of "behaviour" -- "canceled" rather than "cancelled". +- "canceled" instead of "cancelled" ## Use proper names for programs and languages -When referring to the name of a development language, use the proper name or the industry convention. +Always refer to development languages and software using their proper names or recognized industry conventions. -For example: +Correct examples include: - "CSS" not "css" @@ -157,7 +149,7 @@ For example: - "React" not "React-js" -When using these terms in code elements (\`) or pre blocks (\`\`\`), use standard syntax. Such as: +When referencing code elements (`` ` ``) or fenced code blocks (`` ``` ``), use standard syntax. For example: - `tinymce` @@ -165,7 +157,7 @@ When using these terms in code elements (\`) or pre blocks (\`\`\`), use standar - `var React = require('react');` -When referring to a program, use the proper name. +When referring to software programs, use their full, proper names. For example: For example: @@ -177,53 +169,53 @@ For example: ## TinyMCE or tinymce -Use the capitalized version of TinyMCE when referring to the open source project or the editor. "TinyMCE" is an abbreviation of "Tiny MoxieCode Editor", but is better known as TinyMCE. +Use the capitalized form **TinyMCE** when referring to the open source project or the editor interface. TinyMCE is derived from "Tiny MoxieCode Editor," but is more commonly known by its abbreviation. -Use lowercase when referring to the `tinymce` JavaScript object. +Use lowercase when referring to the **`tinymce`** JavaScript object. -## Use Active voice not Passive voice +## Use active voice, not passive voice -Use active voice. Passive voice decreases readability and comprehension. +Use active voice to improve readability and comprehension. Passive constructions should be avoided unless grammatically necessary. -For information on the difference between active and passive voice, see: [Grammar Girl: Active Voice Versus Passive Voice](https://www.quickanddirtytips.com/education/grammar/active-voice-versus-passive-voice). +For a detailed explination, see: [Grammar Girl: Active Voice Versus Passive Voice](https://www.quickanddirtytips.com/education/grammar/active-voice-versus-passive-voice). ## Titles and headings -Titles and headings should be: +Titles and headings must be: -- Descriptive but concise +- Descriptive yet concise -- Written using Sentence-case capitalization. +- Written in sentence case capitalization. -Sentence case capitalization is more comfortable to read in technical documentation. +Sentence case capitalization improves readability in technical documentation. -For guidelines on headings and titles, see: [Content Design London: Readability Guidelines - Headings and titles](https://readabilityguidelines.co.uk/content-design/headings-titles/). +For additional guidence, see: [Content Design London: Readability Guidelines - Headings and titles](https://readabilityguidelines.co.uk/content-design/headings-titles/). ## Using abbreviations, acronyms, and intialisms -General points: +General guidelines: -- Do not use points or spaces. +- Do not use periods or spaces in abbreviations. -- Write out "for example" and "that is" in full (not "eg" or "ie"). +- Write out "for example" and "that is" in full (not "e.g." or "i.e."). -- If an acronym is better understood than the full text, use the acronym. +- When the acronym is more familiar than its expanded form, use the acronym. -- Use all capital letters for initialisms. +- Use all capital letters for initialisms (e.g., HTML, CSS). -- Start with a capital letter for acronyms. +- Use an initial capital letter for acronyms (e.g., AsciiDoc, Antora). -- Capitalize single letters in expressions. +- Capitalize single-letter expressions (e.g., X-axis, Y-coordinate). -- Provide full text explanations. +- Provide the full form of any abbreviation or acronym on first use. -- Consider providing a full explanation each time. +- When clarity is critical, consider repeating the full form with each usage. For guidelines on using abbreviations, acronyms, and intialisms; see: [Content Design London: Readability Guidelines - Abbreviations and acronyms](https://readabilityguidelines.co.uk/grammar-points/abbreviations-and-acronyms/). ## Adverbs -Avoid adverbs, such as: *very* and *usually*. +Avoid adverbs such as *very*, *usually*, *quickly*, and *clearly*, unless necessary for precision. For examples and a definition of an adverb, see: [Cambridge Dictionary: Grammar - Adverbs](https://dictionary.cambridge.org/grammar/british-grammar/adverbs_2). @@ -231,57 +223,63 @@ For (casual) information on removing adverbs, see: [Grammar Girl: How to Elimina ## Contractions -Avoid contractions, such as: *can’t*, *don’t*, *they’re*, and *could’ve*. +Avoid contractions. Use full word forms to improve clarity and consistency in technical documentation. + +- Examples to avoid include: *can’t*, *don’t*, *they’re*, *could’ve*. For information on contractions, see: [Content Design London: Readability Guidelines - Contractions](https://readabilityguidelines.co.uk/grammar-points/contractions/). ## Hyphens -Limit use of hyphens. Some general pointers: +Use hyphens only when necessary to avoid confusion or ambiguity. Follow current usage standards and remain consistent throughout the content. + +General principles: -- Only use a hyphen if the word or phrase is confusing without it. +- Use a hyphen if omitting it would cause confusion. -- Make sure your hyphen usage is up to date. +- Avoid outdated or unnecessary hyphenation. -- Be consistent with hyphen usage. +- Maintain consistent usage patterns across similar terms. For guidelines on using hyphens, see: [Content Design London: Readability Guidelines - Hyphens and dashes](https://readabilityguidelines.co.uk/grammar-points/hyphens-and-dashes/). ## Pronouns -Avoid pronouns, such as: *we*, *you*, *their*, and *I*. +Avoid pronouns, such as *we*, *you*, *their*, and *I*. For examples and a definition of an pronoun, see: [Cambridge Dictionary: Grammar - Pronouns](https://dictionary.cambridge.org/grammar/british-grammar/pronouns_1). For reasons to avoid pronouns, see: [Content Design London: Readability Guidelines - We, you, our, your, my](https://readabilityguidelines.co.uk/audiences-devices-channels/we-you-our-your-my/). -## First, Second, or Third Person Perspective +## First, second, or third person perspective -Write in a second person perspective, such as: *You*, and *your*. Remember to avoid using pronouns, including *you* and *your*. +Documentation should avoid using any narrative perspective that requires pronouns. Although second person (*you*, *your*) is often used in general guidance, pronouns should still be excluded for clarity and consistency. -For information on writing in a second person perspective, see: [Grammar Girl: First, Second, and Third Person](https://www.quickanddirtytips.com/education/grammar/first-second-and-third-person?page=1). +For an explanation of narrative perspectives, see: [Grammar Girl: First, Second, and Third Person](https://www.quickanddirtytips.com/education/grammar/first-second-and-third-person?page=1). ## Links and cross-references When adding links or cross-references: -- Make link text meaningful. +- Use meaningful and descriptive link text. -- Avoid mid-sentence links. +- Avoid placing links mid-sentence. -- Match the destination content. +- Ensure link text accurately matches the destination content. -- Use sentence case. +- Use sentence case for link text. For guidelines on adding links to the documentation, see: [Content Design London: Readability Guidelines - Links](https://readabilityguidelines.co.uk/content-design/links/). ## Images and icons -Avoid adding images to the documentation. They can quickly become outdated. Use a description or a "demo" instead. +Avoid adding images unless necessary. Images may become outdated quickly and often require maintenance. When applicable, replace images with text descriptions or live demos. + +When images are required: -When images are used, reuse existing images if possible. This includes icons. +- Reuse existing images when possible (including icons). -Ensure a brief description of the image is provided in the alternative text attribute. +- Always include descriptive alternative text using the `alt` attribute. For information on providing useful alternative text for images, see: [WCAG 2.1 specification: G95 - Providing short text alternatives that provide a brief description of the non-text content](https://www.w3.org/WAI/WCAG21/Techniques/general/G95.html). diff --git a/README.md b/README.md index be169e039f..48fa09273e 100644 --- a/README.md +++ b/README.md @@ -1,119 +1,101 @@ -# TinyMCE documentation +# TinyMCE Documentation -This project maintains the documentation for TinyMCE at -[https://www.tiny.cloud/docs](https://www.tiny.cloud/docs). If you have any -modifications you wish to contribute, fork this project, make the changes -and submit a pull request. You will need to sign the contributor’s license -agreement, which will be emailed to you upon creating the pull request. +This project maintains the official documentation for TinyMCE, available at [https://www.tiny.cloud/docs](https://www.tiny.cloud/docs). If you have modifications or improvements to contribute, fork this repository, make the necessary changes, and submit a pull request (PR). A contributor's license agreement (CLA) must be signed before your contribution can be merged. This agreement will be sent via email when you create a PR. -This project is built using [Antora](https://antora.org/). +This project is built using [Antora](https://antora.org/), a powerful documentation site generator that supports multi-repository collaboration, content modularization, and flexible versioning. ## Contributing to the TinyMCE Documentation -If you would like to contribute to the TinyMCE project please read the TinyMCE Documentation Contributor’s Guide at either: +To contribute to the TinyMCE documentation project, please review the following resources: -- [TinyMCE Documentation - Contributor's Guide](https://www.tiny.cloud/docs/configure/contributing-docs/). -- [GitHub - How to contribute to TinyMCE’s documentation](https://github.com/tinymce/tinymce-docs/blob/release/docs-6/CONTRIBUTING.md#how-to-contribute-to-tinymces-documentation). +- [GitHub - How to contribute to TinyMCE’s documentation](https://github.com/tinymce/tinymce-docs/blob/main/CONTRIBUTING.md#how-to-contribute-to-tinymces-documentation) -## Working on TinyMCE documentation +These guides cover contribution guidelines, project structure, style conventions, and best practices for submitting changes. -### Compiling or building the documentation +## Setting Up Your Development Environment -The following procedure assists with building (or compiling) the documentation locally. Tiny recommends testing and reviewing changes locally prior to submitting a pull request. +To contribute effectively, you should set up a local development environment. This allows you to preview and test your changes before submitting a PR. -#### Installing Prerequisites +### Prerequisites -##### Linux users +Ensure the following software is installed: -You need the following programs installed on your computer: +- [Node.js](https://nodejs.org/en/) (version 22.9 or lower) +- [Yarn](https://yarnpkg.com/) +- Git -#### First time set up +### Cloning the Repository -Once you have installed any missing prerequisites, in a terminal or on a command prompt: +Clone the TinyMCE documentation repository: -1. Clone the git repository: - ``` - git clone git@github.com:tinymce/tinymce-docs.git - ``` - -2. Change directory into the cloned git repository: - ``` - cd tinymce-docs - ``` - -3. Run yarn install - ``` - yarn install - ``` +```bash +git clone git@github.com:tinymce/tinymce-docs.git +cd tinymce-docs +yarn +``` -#### Run the development version of the documentation +### Running the Development Server -To create a development version of the documentation, run: +To build and serve the documentation locally: -``` +```bash yarn build -yarn serve +yarn start-dev ``` +Visit [http://127.0.0.1:4000](http://127.0.0.1:4000) to view the documentation. The server supports hot-reloading, so changes will automatically reflect when you save your work. -To view the documentation; in a web browser, navigate to [http://127.0.0.1:4000](http://127.0.0.1:4000). - -> **Note**: The development version of the documentation will update automatically when you save changes locally. +> **Note:** The `yarn build` command generates the API reference documentation from the TinyMCE source code. To adjust the API version, edit the `API_VERSION` variable in the `scripts/api-reference.sh` file. Alternatively, use `yarn build-local` to build using a local TinyMCE instance: -The `yarn build` step will download the latest TinyMCE package and generate new API reference content from source code. To change the version of TinyMCE API, open the `-scripts/api-reference.sh` file and edit the API_VERSION to the TinyMCE version you would like to generate API docs for. Alternatively, to build using a local version of TinyMCE, `yarn build-local ../path/to/local/TinyMCE`. +Example: -> **Note**: The development server does not need to be stopped prior to running the `yarn build` command, antora should pick up the new changes generated by the build step. +```bash +yarn build-local ../path/to/local/tinymce +``` -#### TinyMCE API documentation +### API Documentation -The TinyMCE API documentation is maintained within the [TinyMCE project repository](https://github.com/tinymce/tinymce) and compiled for the documentation site using [MoxieDoc](https://github.com/tinymce/moxiedoc). +The TinyMCE API documentation is compiled and generated using [MoxieDoc](https://github.com/tinymce/moxiedoc) from the core [TinyMCE project repository](https://github.com/tinymce/tinymce). To update the published API docs: -To update the published API documentation: +1. Update the `.api-version` file. +2. Run `yarn build`: -1. Change the version in `.api-version`. -2. Run `yarn build`. + * Running `yarn build` downloads the TinyMCE package specified in `.api-version` and generates new API reference content from source. 3. Commit the changes. -Running `yarn build` downloads the TinyMCE package specified in `.api-version` and generates new API reference content from source. +> **Warning:** The API documentation should not be edited manually. Always generate it from source to ensure accuracy. -**Note:** The API documentation should never be edited manually. +## Live Demos -##### Prerequisites +Live demos can be added to the `modules/ROOT/examples/live-demos` directory. Reference them in your documentation with: -- [Node.js](https://nodejs.org/en/). - - -### Live Demos +```asciidoc +liveDemo::{sub-directory-name}[] +``` -New live demos can be added to the [modules/ROOT/examples/live-demos directory](modules/ROOT/examples/live-demos). It then can be referenced in your doc with the following code: +### Overriding the tinymce URL in Live Demos -``` - liveDemo::{sub-directory-name}[] -``` +By default, live demos load TinyMCE from the URL specified in the `tinymce_live_demo_url` attribute in the `antora.yml` file. This can be overridden for specific use cases: -#### Overriding the tinymce URL in live demos +* Testing a new feature on the `dev` channel. +* Running the site locally while testing live demos on a different channel. -All live demos usually get their `tinymce.min.js` URL from the `tinymce_live_demo_url` setting in the `antora.yml` file. -However, there are some instances where you wish to override this, e.g. +To help with this, there are two mechanisms for overriding the `tinymce.min.js` URL: - - You want to push/deploy a branch for a new feature that's only on the 'dev' channel. - - You want to run the site locally, but test out the live demos in a different channel. +1. **Global Override:** + To change the TinyMCE URL for all live demos, modify the `tinymce_live_demo_url` attribute in `antora-playbook-dev.yml`: -To help with this, there are two mechanisms for overriding the `tinymce.min.js` URL. + ```yaml + asciidoc: + attributes: + tinymce_live_demo_url: https://your-custom-url.com/tinymce.min.js + ``` - 1. Change the URL for all live demos by setting the `tinymce_live_demo_url` attribute in `antora-playbook-dev.yml`. For example: - ``` - asciidoc - attributes: - tinymce_live_demo_url: URL_to_script_file - ``` +2. **Per-Demo Override:** Use the `script_url_override` attribute: + To override the URL for a specific live demo: - 2. Change the URL for an individual live demo by setting `script_url_override` attribute in the live demo markup. For example: - ``` - liveDemo::{sub-directory-name}[script_url_override='URL_to_script_file'] - ``` + ```asciidoc + liveDemo::{sub-directory-name}[script_url_override='https://your-custom-url.com/tinymce.min.js'] + ``` - - This is useful if you want to deploy the develop branch for a feature only in the 'dev' channel. - - This only overrides the URL for one live demo. - - Don't use this in more than one live demo on a page. - - Don't use this long-term - when the feature is fully rolled-out, use the standard channel. +> **Caution:** Use this sparingly. Avoid using different URLs for multiple demos on the same page, and remember to revert these changes once the feature is fully released. diff --git a/antora-playbook.yml b/antora-playbook.yml index 1ff597c1d2..94ce607ae1 100644 --- a/antora-playbook.yml +++ b/antora-playbook.yml @@ -8,7 +8,7 @@ content: branches: HEAD start_path: ./ - url: https://github.com/tinymce/tinymce-docs.git - branches: [ tinymce/5, tinymce/6, tinymce/7] + branches: [ tinymce/5, tinymce/6, tinymce/7 ] urls: html_extension_style: indexify latest_version_segment: latest diff --git a/antora.yml b/antora.yml index 19391046b8..98a90fea0a 100644 --- a/antora.yml +++ b/antora.yml @@ -8,14 +8,14 @@ asciidoc: idseparator: '-@' # generic variables companyurl: https://www.tiny.cloud - cdnurl: https://cdn.tiny.cloud/1/no-api-key/tinymce/7/tinymce.min.js - tdcdnurl: https://cdn.tiny.cloud/1/_your_api_key_/tinydrive/7/tinydrive.min.js - tinymce_live_demo_url: https://cdn.tiny.cloud/1/qagffr3pkuv17a8on1afax661irst1hbr4e6tbv888sz91jc/tinymce/7/tinymce.min.js - tinydrive_live_demo_url: https://cdn.tiny.cloud/1/qagffr3pkuv17a8on1afax661irst1hbr4e6tbv888sz91jc/tinydrive/7/tinydrive.min.js + cdnurl: https://cdn.tiny.cloud/1/no-api-key/tinymce/8/tinymce.min.js + tdcdnurl: https://cdn.tiny.cloud/1/_your_api_key_/tinydrive/8/tinydrive.min.js + tinymce_live_demo_url: https://cdn.tiny.cloud/1/qagffr3pkuv17a8on1afax661irst1hbr4e6tbv888sz91jc/tinymce/8/tinymce.min.js + tinydrive_live_demo_url: https://cdn.tiny.cloud/1/qagffr3pkuv17a8on1afax661irst1hbr4e6tbv888sz91jc/tinydrive/8/tinydrive.min.js webcomponent_url: https://cdn.jsdelivr.net/npm/@tinymce/tinymce-webcomponent/dist/tinymce-webcomponent.min.js jquery_url: https://cdn.jsdelivr.net/npm/@tinymce/tinymce-jquery@2/dist/tinymce-jquery.min.js openai_proxy_url: https://openai.ai-demo-proxy.tiny.cloud/v1/chat/completions - openai_proxy_token: eyJhbGciOiJFUzM4NCJ9.eyJhdWQiOlsiaHR0cHM6Ly9vcGVuYWkuYWktZGVtby1wcm94eS50aW55LmNsb3VkLyJdLCJleHAiOjE3NTEzMjgwMDAsImh0dHBzOi8vb3BlbmFpLmFpLWRlbW8tcHJveHkudGlueS5jbG91ZC9yb2xlIjoicHVibGljLWRlbW8iLCJpc3MiOiJodHRwczovL2FpLWRlbW8tcHJveHkudGlueS5jbG91ZC8iLCJqdGkiOiJmOGFmY2EyNC1mN2FhLTQxMjktYTc2Yy02YThlZDU3YjAyZjYiLCJzdWIiOiJhaS1hc3Npc3RhbnQtZGVtbyJ9.Xu0apHCbxgmRQTeTqrTIDFFhh2CgKeARRXa3mCxSGoCwZqkoQaFRZBCzDo8Xz7DuUa5mW2XHl-HYcYiXJM9ly16d0oY7lJefHBeLlmJEBE1CSttHBkCRWZS8eFLCasL6 + openai_proxy_token: eyJhbGciOiJFUzM4NCJ9.eyJhdWQiOlsiaHR0cHM6Ly9vcGVuYWkuYWktZGVtby1wcm94eS50aW55LmNsb3VkLyJdLCJleHAiOjE3ODI4NjQwMDAsImh0dHBzOi8vb3BlbmFpLmFpLWRlbW8tcHJveHkudGlueS5jbG91ZC9yb2xlIjoicHVibGljLWRlbW8iLCJpc3MiOiJodHRwczovL2FpLWRlbW8tcHJveHkudGlueS5jbG91ZC8iLCJqdGkiOiIxZWY3NjJiNi1mZDYyLTQ3ZWQtOGRkNS0yOGRmMzBkZDU4YmMiLCJzdWIiOiJhaS1hc3Npc3RhbnQtZGVtbyJ9.WC8GIY19MgZneDVZoA-Ttt9E7gNkD0Yl-pcM_5c2RT3RdV_zE0i4bPOGBJpg_g6wu_4ki2ery6_JZtk2Q9gXEBHH7fIu7hXDFS8uIV9qe8MyxEqqRncGFVDjSTldVXGS default_meta_keywords: tinymce, documentation, docs, plugins, customizable skins, configuration, examples, html, php, java, javascript, image editor, inline editor, distraction-free editor, classic editor, wysiwyg # product docker variables dockerimageimportfromwordexporttoword: registry.containers.tiny.cloud/docx-converter-tiny diff --git a/modules/ROOT/examples/live-demos/a11ychecker/index.js b/modules/ROOT/examples/live-demos/a11ychecker/index.js index bd41b7b2b9..6b07a57d11 100644 --- a/modules/ROOT/examples/live-demos/a11ychecker/index.js +++ b/modules/ROOT/examples/live-demos/a11ychecker/index.js @@ -2,8 +2,10 @@ tinymce.init({ selector: 'textarea#a11ychecker', plugins: 'a11ychecker advcode table advlist lists image media anchor link autoresize', toolbar: 'a11ycheck | blocks bold forecolor backcolor | bullist numlist | link image media anchor | table | code', + max_height: 500, a11y_advanced_options: true, a11ychecker_html_version: 'html5', a11ychecker_level: 'aaa', + a11ychecker_allow_decorative_images: true, content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }' }); diff --git a/modules/ROOT/examples/live-demos/comments-callback-with-mentions/index.js b/modules/ROOT/examples/live-demos/comments-callback-with-mentions/index.js index bb74433204..8cd150d15d 100644 --- a/modules/ROOT/examples/live-demos/comments-callback-with-mentions/index.js +++ b/modules/ROOT/examples/live-demos/comments-callback-with-mentions/index.js @@ -1,18 +1,23 @@ import ('https://cdn.jsdelivr.net/npm/@faker-js/faker@9/dist/index.min.js').then(({ faker }) => { + /* This represents a database of users on the server */ const userDb = { 'michaelcook': { id: 'michaelcook', name: 'Michael Cook', - fullName: 'Michael Cook', - description: 'Product Owner', - image: "{{imagesdir}}/avatars/michaelcook.png" + avatar: '{{imagesdir}}/avatars/michaelcook.png', + custom: { + fullName: 'Michael Cook', + description: 'Product Owner' + } }, 'kalebwilson': { id: 'kalebwilson', name: 'Kaleb Wilson', - fullName: 'Kaleb Wilson', - description: 'Marketing Director', - image: "{{imagesdir}}/avatars/kalebwilson.png" + avatar: '{{imagesdir}}/avatars/kalebwilson.png', + custom: { + fullName: 'Kaleb Wilson', + description: 'Marketing Director', + } } }; @@ -35,7 +40,7 @@ import ('https://cdn.jsdelivr.net/npm/@faker-js/faker@9/dist/index.min.js').then const getAuthorInfo = (uid) => { const user = userDb[uid]; if (user) { - return fillAuthorInfo(user.id, user.fullName, user.image); + return fillAuthorInfo(user.id, user.custom.fullName, user.avatar); } return { author: uid, @@ -85,8 +90,8 @@ import ('https://cdn.jsdelivr.net/npm/@faker-js/faker@9/dist/index.min.js').then const resolvedConversationDb = {}; const setupFakeServer = () => { - const images = [ adminUser.image, currentUser.image ]; - const userNames = [ adminUser.fullName, currentUser.fullName ]; + const images = [ adminUser.avatar, currentUser.avatar ]; + const userNames = [ adminUser.custom.fullName, currentUser.custom.fullName ]; for (let i = 0; i < numberOfUsers; i++) { images.push(faker.image.avatar()); @@ -99,14 +104,16 @@ import ('https://cdn.jsdelivr.net/npm/@faker-js/faker@9/dist/index.min.js').then [currentUser.id]: currentUser }; userNames.map((fullName) => { - if ((fullName !== currentUser.fullName) && (fullName !== adminUser.fullName)) { + if ((fullName !== currentUser.custom.fullName) && (fullName !== adminUser.custom.fullName)) { const id = fullName.toLowerCase().replace(/ /g, ''); userDb[id] = { id, name: fullName, - fullName, - description: faker.person.jobTitle(), - image: images[Math.floor(images.length * Math.random())] + avatar: images[Math.floor(images.length * Math.random())], + custom: { + fullName, + description: faker.person.jobTitle(), + } }; } }); @@ -118,8 +125,8 @@ import ('https://cdn.jsdelivr.net/npm/@faker-js/faker@9/dist/index.min.js').then const users = Object.keys(userDb).map((id) => ({ id, name: userDb[id].name, - image: userDb[id].image, - description: userDb[id].description + image: userDb[id].avatar, + description: userDb[id].custom.description })); resolve(users); }, fakeDelay); @@ -320,9 +327,6 @@ import ('https://cdn.jsdelivr.net/npm/@faker-js/faker@9/dist/index.min.js').then }); setTimeout(() => done({ conversations: fetchedConversations }), fakeDelay); }; - - // Read the above `getAuthorInfo` function to see how this could be implemented - const tinycomments_fetch_author_info = (done) => done(getAuthorInfo(currentUid)); tinymce.init({ selector: 'textarea#comments-callback-with-mentions', @@ -350,9 +354,16 @@ import ('https://cdn.jsdelivr.net/npm/@faker-js/faker@9/dist/index.min.js').then mentions_select, tinycomments_mode: 'callback', - tinycomments_author: currentUser.id, - tinycomments_author_name: currentUser.fullName, - tinycomments_avatar: currentUser.image, + user_id: currentUser.id, + fetch_users: (userIds) => { + return Promise.all( + userIds.map( + (userId) => new Promise( + (resolve) => resolve(userDb[userId] || { id: userId }) + ) + ) + ) + }, tinycomments_create, tinycomments_reply, tinycomments_delete, @@ -362,6 +373,5 @@ import ('https://cdn.jsdelivr.net/npm/@faker-js/faker@9/dist/index.min.js').then tinycomments_delete_comment, tinycomments_edit_comment, tinycomments_fetch, - tinycomments_fetch_author_info }); }); diff --git a/modules/ROOT/examples/live-demos/comments-embedded-with-mentions/index.js b/modules/ROOT/examples/live-demos/comments-embedded-with-mentions/index.js index c2639167f7..5ff44ba8bc 100644 --- a/modules/ROOT/examples/live-demos/comments-embedded-with-mentions/index.js +++ b/modules/ROOT/examples/live-demos/comments-embedded-with-mentions/index.js @@ -1,19 +1,28 @@ import ('https://cdn.jsdelivr.net/npm/@faker-js/faker@9/dist/index.min.js').then(({ faker }) => { - const adminUser = { - id: 'johnsmith', - name: 'John Smith', - fullName: 'John Smith', - description: 'Company Founder', - image: "https://i.pravatar.cc/150?img=11" + /* This represents a database of users on the server */ + const userDb = { + 'johnsmith': { + id: 'johnsmith', + name: 'John Smith', + avatar: 'https://i.pravatar.cc/150?img=11', + custom: { + fullName: 'John Smith', + description: 'Company Founder', + } + }, + 'jennynichols': { + id: 'jennynichols', + name: 'Jenny Nichols', + avatar: 'https://i.pravatar.cc/150?img=10', + custom: { + fullName: 'Jenny Nichols', + description: 'Marketing Director', + } + } }; - const currentUser = { - id: 'jennynichols', - name: 'Jenny Nichols', - fullName: 'Jenny Nichols', - description: 'Marketing Director', - image: "https://i.pravatar.cc/150?img=10" - }; + const adminUser = userDb['johnsmith']; + const currentUser = userDb['jennynichols']; const fakeDelay = 500; const numberOfUsers = 200; @@ -24,28 +33,25 @@ import ('https://cdn.jsdelivr.net/npm/@faker-js/faker@9/dist/index.min.js').then const userRequest = {}; const setupFakeServer = () => { - const images = [ adminUser.image, currentUser.image ]; - const userNames = [ adminUser.fullName, currentUser.fullName ]; + const images = [ adminUser.avatar, currentUser.avatar ]; + const userNames = [ adminUser.custom.fullName, currentUser.custom.fullName ]; for (let i = 0; i < numberOfUsers; i++) { images.push(faker.image.avatar()); userNames.push(`${faker.person.firstName()} ${faker.person.lastName()}`); } - - /* This represents a database of users on the server */ - const userDb = { - [adminUser.id]: adminUser, - [currentUser.id]: currentUser - }; + userNames.map((fullName) => { - if ((fullName !== currentUser.fullName) && (fullName !== adminUser.fullName)) { + if ((fullName !== currentUser.custom.fullName) && (fullName !== adminUser.custom.fullName)) { const id = fullName.toLowerCase().replace(/ /g, ''); userDb[id] = { id, name: fullName, - fullName, - description: faker.person.jobTitle(), - image: images[Math.floor(images.length * Math.random())] + avatar: images[Math.floor(images.length * Math.random())], + custom: { + fullName, + description: faker.person.jobTitle(), + } }; } }); @@ -155,9 +161,16 @@ import ('https://cdn.jsdelivr.net/npm/@faker-js/faker@9/dist/index.min.js').then tinycomments_mode: 'embedded', sidebar_show: 'showcomments', - tinycomments_author: currentUser.id, - tinycomments_author_name: currentUser.fullName, - tinycomments_avatar: currentUser.image, + user_id: currentUser.id, + fetch_users: (userIds) => { + return Promise.all( + userIds.map( + (userId) => new Promise( + (resolve) => resolve(userDb[userId] || { id: userId }) + ) + ) + ) + }, tinycomments_can_resolve, }); }); diff --git a/modules/ROOT/examples/live-demos/comments-embedded/index.js b/modules/ROOT/examples/live-demos/comments-embedded/index.js index d0aa9d9c21..6514394809 100644 --- a/modules/ROOT/examples/live-demos/comments-embedded/index.js +++ b/modules/ROOT/examples/live-demos/comments-embedded/index.js @@ -16,7 +16,7 @@ tinymce.init({ quickbars_image_toolbar: 'alignleft aligncenter alignright | rotateleft rotateright | imageoptions', tinycomments_mode: 'embedded', sidebar_show: 'showcomments', - tinycomments_author: currentAuthor, + user_id: currentAuthor, tinycomments_can_resolve: (req, done, fail) => { const allowed = req.comments.length > 0 && req.comments[0].author === currentAuthor; diff --git a/modules/ROOT/examples/live-demos/custom-toolbar-split-button/index.js b/modules/ROOT/examples/live-demos/custom-toolbar-split-button/index.js index 256c3488f5..f60882e3e6 100644 --- a/modules/ROOT/examples/live-demos/custom-toolbar-split-button/index.js +++ b/modules/ROOT/examples/live-demos/custom-toolbar-split-button/index.js @@ -6,7 +6,8 @@ tinymce.init({ editor.ui.registry.addSplitButton('myButton', { text: 'My Button', icon: 'info', - tooltip: 'This is an example split-button', + tooltip: 'Execute my action', + chevronTooltip: 'My Button menu options', onAction: () => editor.insertContent('

You clicked the main button

'), onItemAction: (api, value) => editor.insertContent(value), fetch: (callback) => { diff --git a/modules/ROOT/examples/live-demos/full-featured/example.js b/modules/ROOT/examples/live-demos/full-featured/example.js index f3069b013d..2c2f774016 100644 --- a/modules/ROOT/examples/live-demos/full-featured/example.js +++ b/modules/ROOT/examples/live-demos/full-featured/example.js @@ -8,15 +8,18 @@ const openai_api_key = ""; const isSmallScreen = window.matchMedia('(max-width: 1023.5px)').matches; +const tinymceElement = document.querySelector('textarea#full-featured'); +const model = tinymceElement.getAttribute('suggestededits-model'); + tinymce.init({ selector: 'textarea#full-featured', - plugins: 'importword exportword exportpdf ai preview powerpaste casechange importcss tinydrive searchreplace autolink autosave save directionality advcode visualblocks visualchars fullscreen image link math media mediaembed codesample table charmap pagebreak nonbreaking anchor tableofcontents insertdatetime advlist lists checklist wordcount tinymcespellchecker a11ychecker editimage help formatpainter permanentpen pageembed charmap tinycomments mentions quickbars linkchecker emoticons advtable footnotes mergetags autocorrect typography advtemplate markdown revisionhistory', + plugins: 'importword exportword exportpdf ai suggestededits preview powerpaste casechange importcss tinydrive searchreplace autolink autosave save directionality advcode visualblocks visualchars fullscreen image link math media mediaembed codesample table charmap pagebreak nonbreaking anchor tableofcontents insertdatetime advlist lists checklist wordcount tinymcespellchecker a11ychecker editimage help formatpainter permanentpen pageembed charmap tinycomments mentions quickbars emoticons advtable footnotes mergetags autocorrect typography advtemplate markdown revisionhistory', tinydrive_token_provider: 'URL_TO_YOUR_TOKEN_PROVIDER', tinydrive_dropbox_app_key: 'YOUR_DROPBOX_APP_KEY', tinydrive_google_drive_key: 'YOUR_GOOGLE_DRIVE_KEY', tinydrive_google_drive_client_id: 'YOUR_GOOGLE_DRIVE_CLIENT_ID', mobile: { - plugins: 'ai preview powerpaste casechange importcss tinydrive searchreplace autolink autosave save directionality advcode visualblocks visualchars fullscreen image link math media mediaembed codesample table charmap pagebreak nonbreaking anchor tableofcontents insertdatetime advlist lists checklist wordcount tinymcespellchecker a11ychecker help formatpainter pageembed charmap mentions quickbars linkchecker emoticons advtable footnotes mergetags autocorrect typography advtemplate', + plugins: 'ai suggestededits preview powerpaste casechange importcss tinydrive searchreplace autolink autosave save directionality advcode visualblocks visualchars fullscreen image link math media mediaembed codesample table charmap pagebreak nonbreaking anchor tableofcontents insertdatetime advlist lists checklist wordcount tinymcespellchecker a11ychecker help formatpainter pageembed charmap mentions quickbars emoticons advtable footnotes mergetags autocorrect typography advtemplate', }, menu: { tc: { @@ -25,7 +28,7 @@ tinymce.init({ } }, menubar: 'file edit view insert format tools table tc help', - toolbar: "undo redo | importword exportword exportpdf | revisionhistory | aidialog aishortcuts | blocks fontsizeinput | bold italic | align numlist bullist | link image | table math media pageembed | lineheight outdent indent | strikethrough forecolor backcolor formatpainter removeformat | charmap emoticons checklist | code fullscreen preview | save print | pagebreak anchor codesample footnotes mergetags | addtemplate inserttemplate | addcomment showcomments | ltr rtl casechange | spellcheckdialog a11ycheck", // Note: if a toolbar item requires a plugin, the item will not present in the toolbar if the plugin is not also loaded. + toolbar: "undo redo | importword exportword exportpdf | suggestededits | revisionhistory | aidialog aishortcuts | blocks fontsizeinput | bold italic | align numlist bullist | link image | table math media pageembed | lineheight outdent indent | strikethrough forecolor backcolor formatpainter removeformat | charmap emoticons checklist | code fullscreen preview | save print | pagebreak anchor codesample footnotes mergetags | addtemplate inserttemplate | addcomment showcomments | ltr rtl casechange | spellcheckdialog a11ycheck", // Note: if a toolbar item requires a plugin, the item will not present in the toolbar if the plugin is not also loaded. autosave_ask_before_unload: true, autosave_interval: '30s', autosave_prefix: '{path}{query}-{id}-', @@ -439,4 +442,16 @@ tinymce.init({ mentions_menu_complete: mentions_menu_complete, // TODO: Implement mentions_menu_complete mentions_select: mentions_select, // TODO: Implement mentions_select mentions_item_type: "profile", + + // Suggested edits plugin settings + user_id: 'kalebwilson', + fetch_users: (userIds) => Promise.all(userIds + .map((userId) => + fetch(`/users/${userId}`) // Fetch user data from the server + .then((response) => response.json()) + .catch(() => ({ id: userId })) // Still return a valid user object even if the fetch fails + )), + suggestededits_model: model, + suggestededits_access: 'full', + suggestededits_content: 'html' }); diff --git a/modules/ROOT/examples/live-demos/full-featured/index.js b/modules/ROOT/examples/live-demos/full-featured/index.js index 3a6655ff7f..c9721b1615 100644 --- a/modules/ROOT/examples/live-demos/full-featured/index.js +++ b/modules/ROOT/examples/live-demos/full-featured/index.js @@ -412,9 +412,33 @@ tinymce.ScriptLoader.loadScripts(['https://cdn.jsdelivr.net/npm/faker@5/dist/fak ]); }; + /** Fake user database for suggested edits */ + const suggestededitsUserDb = { + adamhayes: { + id: 'adamhayes', + name: 'Adam Hayes', + avatar: `https://randomuser.me/api/portraits/men/4.jpg`, + }, + martincook: { + id: 'martincook', + name: 'Martin Cook', + avatar: `https://randomuser.me/api/portraits/men/5.jpg`, + }, + kalebwilson: { + id: 'kalebwilson', + name: 'Kaleb Wilson', + avatar: `https://randomuser.me/api/portraits/men/6.jpg`, + }, + sarahjones: { + id: 'sarahjones', + name: 'Sarah Jones', + avatar: `https://randomuser.me/api/portraits/women/1.jpg`, + } + }; + tinymce.init({ selector: 'textarea#full-featured', - plugins: 'ai preview powerpaste casechange importcss tinydrive searchreplace autolink autosave save directionality advcode visualblocks visualchars fullscreen image link math media mediaembed codesample table charmap pagebreak nonbreaking anchor tableofcontents insertdatetime advlist lists checklist wordcount tinymcespellchecker a11ychecker editimage help formatpainter permanentpen pageembed charmap tinycomments mentions quickbars linkchecker emoticons advtable footnotes mergetags autocorrect typography advtemplate markdown revisionhistory importword exportword exportpdf', + plugins: 'ai suggestededits preview powerpaste casechange importcss tinydrive searchreplace autolink autosave save directionality advcode visualblocks visualchars fullscreen image link math media mediaembed codesample table charmap pagebreak nonbreaking anchor tableofcontents insertdatetime advlist lists checklist wordcount tinymcespellchecker a11ychecker editimage help formatpainter permanentpen pageembed charmap tinycomments mentions quickbars emoticons advtable footnotes mergetags autocorrect typography advtemplate markdown revisionhistory importword exportword exportpdf', editimage_cors_hosts: ['picsum.photos'], tinydrive_token_provider: (success, failure) => { success({ token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqb2huZG9lIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.Ks_BdfH4CWilyzLNk8S2gDARFhuxIauLa8PwhdEQhEo' }); @@ -424,7 +448,7 @@ tinymce.ScriptLoader.loadScripts(['https://cdn.jsdelivr.net/npm/faker@5/dist/fak tinydrive_google_drive_key: 'AIzaSyAsVRuCBc-BLQ1xNKtnLHB3AeoK-xmOrTc', tinydrive_google_drive_client_id: '748627179519-p9vv3va1mppc66fikai92b3ru73mpukf.apps.googleusercontent.com', mobile: { - plugins: 'ai preview powerpaste casechange importcss tinydrive searchreplace autolink autosave save directionality advcode visualblocks visualchars fullscreen image link math media mediaembed codesample table charmap pagebreak nonbreaking anchor tableofcontents insertdatetime advlist lists checklist wordcount tinymcespellchecker a11ychecker help formatpainter pageembed charmap mentions quickbars linkchecker emoticons advtable footnotes mergetags autocorrect typography advtemplate', + plugins: 'ai suggestededits preview powerpaste casechange importcss tinydrive searchreplace autolink autosave save directionality advcode visualblocks visualchars fullscreen image link math media mediaembed codesample table charmap pagebreak nonbreaking anchor tableofcontents insertdatetime advlist lists checklist wordcount tinymcespellchecker a11ychecker help formatpainter pageembed charmap mentions quickbars emoticons advtable footnotes mergetags autocorrect typography advtemplate', }, menu: { tc: { @@ -433,7 +457,7 @@ tinymce.ScriptLoader.loadScripts(['https://cdn.jsdelivr.net/npm/faker@5/dist/fak } }, menubar: 'file edit view insert format tools table tc help', - toolbar: "undo redo | importword exportword exportpdf | revisionhistory | aidialog aishortcuts | blocks fontsizeinput | bold italic | align numlist bullist | link image | table math media pageembed | lineheight outdent indent | strikethrough forecolor backcolor formatpainter removeformat | charmap emoticons checklist | code fullscreen preview | save print | pagebreak anchor codesample footnotes mergetags | addtemplate inserttemplate | addcomment showcomments | ltr rtl casechange | spellcheckdialog a11ycheck", // Note: if a toolbar item requires a plugin, the item will not present in the toolbar if the plugin is not also loaded. + toolbar: "undo redo | importword exportword exportpdf | suggestededits | revisionhistory | aidialog aishortcuts | blocks fontsizeinput | bold italic | align numlist bullist | link image | table math media pageembed | lineheight outdent indent | strikethrough forecolor backcolor formatpainter removeformat | charmap emoticons checklist | code fullscreen preview | save print | pagebreak anchor codesample footnotes mergetags | addtemplate inserttemplate | addcomment showcomments | ltr rtl casechange | spellcheckdialog a11ycheck", // Note: if a toolbar item requires a plugin, the item will not present in the toolbar if the plugin is not also loaded. autosave_ask_before_unload: true, autosave_interval: '30s', autosave_prefix: '{path}{query}-{id}-', @@ -583,10 +607,15 @@ tinymce.ScriptLoader.loadScripts(['https://cdn.jsdelivr.net/npm/faker@5/dist/fak } }, revisionhistory_fetch: fetchRevisions, - revisionhistory_author: { - id: 'john.doe', - name: 'John Doe' - }, revisionhistory_display_author: true, + // Suggested Edits plugin configuration + user_id: 'kalebwilson', + fetch_users: (userIds) => Promise.all(userIds + .map((userId) => new Promise((resolve) => + resolve(suggestededitsUserDb[userId] || { id: userId })) + )), + suggestededits_content: 'html', + suggestededits_access: 'full', }); -}); + +}); \ No newline at end of file diff --git a/modules/ROOT/examples/live-demos/revisionhistory/index.js b/modules/ROOT/examples/live-demos/revisionhistory/index.js index 946faa0854..7b3b282b87 100644 --- a/modules/ROOT/examples/live-demos/revisionhistory/index.js +++ b/modules/ROOT/examples/live-demos/revisionhistory/index.js @@ -4,6 +4,15 @@ const getRandomDelay = () => { return Math.floor(Math.random() * (maxDelay - minDelay + 1)) + minDelay; }; +/* This represents a database of users on the server */ +const userDb = { + 'john.doe': { + id: 'john.doe', + name: 'John Doe', + avatar: 'https://i.pravatar.cc/150?img=11' + } +}; + const lightRevisions = [ { revisionId: '3', @@ -202,9 +211,15 @@ tinymce.init({ content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }', revisionhistory_fetch, revisionhistory_fetch_revision, - revisionhistory_author: { - id: 'john.doe', - name: 'John Doe' - }, + user_id: 'john.doe', + fetch_users: (userIds) => { + return Promise.all( + userIds.map( + (userId) => new Promise( + (resolve) => resolve(userDb[userId] || { id: userId }) + ) + ) + ) + }, revisionhistory_display_author: true }); diff --git a/modules/ROOT/examples/live-demos/spellchecker/index.js b/modules/ROOT/examples/live-demos/spellchecker/index.js index 617b1a579d..b3b3d0f3cb 100644 --- a/modules/ROOT/examples/live-demos/spellchecker/index.js +++ b/modules/ROOT/examples/live-demos/spellchecker/index.js @@ -3,31 +3,30 @@ tinymce.init({ plugins: 'code tinymcespellchecker link', toolbar: 'spellchecker language spellcheckdialog', height: 500, - spellchecker_language: 'en_US', + spellchecker_language: 'en-US', content_langs: [ - { title: 'Afrikaans (South Africa)', code: 'af_ZA', customCode: 'af_ZA' }, - { title: 'English (Australia)', code: 'en_AU' }, - { title: 'English (Canada)', code: 'en_CA' }, - { title: 'English (United Kingdom)', code: 'en_GB' }, - { title: 'English (United States)', code: 'en_US' }, - { title: 'Medical English (US)', code: 'en_US', customCode: 'en_US-medical' }, - { title: 'Medical English (UK)', code: 'en_GB', customCode: 'en_GB-medical' }, + { title: 'Afrikaans (South Africa)', code: 'af-ZA', customCode: 'af-ZA' }, + { title: 'English (Australia)', code: 'en-AU' }, + { title: 'English (Canada)', code: 'en-CA' }, + { title: 'English (United Kingdom)', code: 'en-GB' }, + { title: 'English (United States)', code: 'en-US' }, + { title: 'Medical English (US)', code: 'en-US', customCode: 'en-US-medical' }, { title: 'Danish', code: 'da' }, - { title: 'Dutch', code: 'nl_NL' }, + { title: 'Dutch', code: 'nl-NL' }, { title: 'Finnish', code: 'fi' }, { title: 'French', code: 'fr' }, - { title: 'German', code: 'de_DE' }, + { title: 'German', code: 'de-DE' }, { title: 'Hungarian', code: 'hu' }, - { title: 'Italian', code: 'it_IT' }, - { title: 'Maori (New Zealand)', code: 'mi_NZ' }, - { title: 'Norwegian Bokmål', code: 'nb_NO' }, + { title: 'Italian', code: 'it-IT' }, + { title: 'Maori (New Zealand)', code: 'mi-NZ' }, + { title: 'Norwegian Bokmål', code: 'nb-NO' }, { title: 'Norwegian Nynorsk', code: 'nn' }, { title: 'Polish', code: 'pl' }, - { title: 'Portuguese (Brazil)', code: 'pt_BR' }, - { title: 'Portuguese (Portugal)', code: 'pt_PT' }, + { title: 'Portuguese (Brazil)', code: 'pt-BR' }, + { title: 'Portuguese (Portugal)', code: 'pt-PT' }, { title: 'Spanish', code: 'es' }, - { title: 'Swedish', code: 'sv_SE' }, - { title: 'Swedish (Finland)', code: 'sv_FI' } + { title: 'Swedish', code: 'sv-SE' }, + { title: 'Swedish (Finland)', code: 'sv-FI' } ], content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }' }); diff --git a/modules/ROOT/examples/live-demos/suggestededits-access-feedback/example.js b/modules/ROOT/examples/live-demos/suggestededits-access-feedback/example.js new file mode 100644 index 0000000000..49d1a9df43 --- /dev/null +++ b/modules/ROOT/examples/live-demos/suggestededits-access-feedback/example.js @@ -0,0 +1,21 @@ +const tinymceElement = document.querySelector('textarea#suggested-edits'); +const model = tinymceElement.getAttribute('suggestededits-model'); + +tinymce.init({ + selector: 'textarea#suggested-edits', + height: 500, + plugins: 'suggestededits advlist anchor autolink code charmap emoticons fullscreen help image link lists media preview searchreplace table', + toolbar: 'undo redo | suggestededits | styles fontsizeinput | bold italic | align bullist numlist | table link image | code', + user_id: 'michaelcook', + fetch_users: (userIds) => Promise.all(userIds + .map((userId) => + fetch(`/users/${userId}`) // Fetch user data from the server + .then((response) => response.json()) + .catch(() => ({ id: userId })) // Still return a valid user object even if the fetch fails + )), + content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }', + readonly: false, // Set to true to prevent edits to the content + suggestededits_access: 'feedback', // Set this value to restrict the permissions in the Suggested Edits view + suggestededits_content: 'html', + suggestededits_model: model +}); \ No newline at end of file diff --git a/modules/ROOT/examples/live-demos/suggestededits-access-feedback/index.html b/modules/ROOT/examples/live-demos/suggestededits-access-feedback/index.html new file mode 100644 index 0000000000..b410001410 --- /dev/null +++ b/modules/ROOT/examples/live-demos/suggestededits-access-feedback/index.html @@ -0,0 +1,44 @@ + diff --git a/modules/ROOT/examples/live-demos/suggestededits-access-feedback/index.js b/modules/ROOT/examples/live-demos/suggestededits-access-feedback/index.js new file mode 100644 index 0000000000..f493d2f43e --- /dev/null +++ b/modules/ROOT/examples/live-demos/suggestededits-access-feedback/index.js @@ -0,0 +1,437 @@ +/** Fake user database */ +const userDb = { + adamhayes: { + id: 'adamhayes', + name: 'Adam Hayes', + avatar: `https://randomuser.me/api/portraits/men/4.jpg`, + }, + martincook: { + id: 'martincook', + name: 'Martin Cook', + avatar: `https://randomuser.me/api/portraits/men/5.jpg`, + }, + kalebwilson: { + id: 'kalebwilson', + name: 'Kaleb Wilson', + avatar: `https://randomuser.me/api/portraits/men/6.jpg`, + }, + sarahjones: { + id: 'sarahjones', + name: 'Sarah Jones', + avatar: `https://randomuser.me/api/portraits/women/1.jpg`, + } +}; + +const model = { + "history": { + "2": [ + { + "id": 1, + "uid": "sarahjones", + "timestamp": 1752576936000, + "feedback": "Nice improvement!" + } + ] + }, + "version": 1, + "contents": [ + { + "type": "p", + "children": [ + { + "type": "img", + "attrs": { + "style": "display: block; margin-left: auto; margin-right: auto;", + "title": "Tiny Logo", + "src": "https://www.tiny.cloud/docs/images/logos/android-chrome-256x256.png", + "alt": "TinyMCE Logo", + "width": "128", + "height": "128" + } + } + ] + }, + { + "type": "h2", + "attrs": { + "style": "text-align: center;" + }, + "children": [ + { + "text": "Welcome to the TinyMCE Suggested Edits " + }, + { + "text": "interactive ", + "opData": { + "id": 1, + "type": "insert", + "uid": "adamhayes", + "timestamp": 1752015064000 + } + }, + { + "text": "demo!" + } + ] + }, + { + "type": "p", + "attrs": { + "style": "text-align: center;" + }, + "children": [ + { + "text": "Try out the Suggested Edits feature" + }, + { + "text": ": type in the editor, apply formatting or delete some content. T", + "opData": { + "id": 2, + "type": "insert", + "uid": "adamhayes", + "timestamp": 1752415064000 + } + }, + { + "text": " by typing in the editor and t", + "opData": { + "id": 2, + "type": "remove", + "uid": "adamhayes", + "timestamp": 1752415064000 + } + }, + { + "text": "hen" + }, + { + "text": ",", + "opData": { + "id": 3, + "type": "insert", + "uid": "adamhayes", + "timestamp": 1752515064000 + } + }, + { + "text": " click" + }, + { + "text": "ing", + "opData": { + "id": 4, + "type": "remove", + "uid": "adamhayes", + "timestamp": 1752515064000 + } + }, + { + "text": " the Review Changes button in the toolbar" + }, + { + "text": " to see your changes", + "opData": { + "id": 5, + "type": "insert", + "uid": "kalebwilson", + "timestamp": 1752615064000 + } + }, + { + "text": "." + } + ] + }, + { + "type": "p", + "attrs": { + "style": "text-align: center;" + }, + "children": [ + { + "text": "And visit the " + }, + { + "text": "pricing page", + "opData": { + "id": 6, + "type": "modify", + "uid": "kalebwilson", + "timestamp": 1752615064000 + }, + "format": [ + { + "type": "a", + "attrs": { + "href": "https://www.tiny.cloud/pricing" + } + } + ], + "oldFormat": [ + { + "type": "a", + "attrs": { + "href": "https://www.tiny.cloud/pricing" + } + }, + "em" + ] + }, + { + "text": " to learn more about our Premium plugins." + } + ] + }, + { + "type": "h2", + "children": [ + { + "text": "A simple table to play with" + } + ] + }, + { + "type": "table", + "attrs": { + "style": "border-collapse: collapse; width: 100%;", + "border": "1" + }, + "children": [ + { + "type": "thead", + "children": [ + { + "type": "tr", + "attrs": { + "style": "text-align: left;" + }, + "children": [ + { + "type": "th", + "children": [ + { + "text": "Product" + } + ] + }, + { + "type": "th", + "children": [ + { + "text": "Cost" + } + ] + }, + { + "type": "th", + "children": [ + { + "text": "Really?" + } + ] + } + ] + } + ] + }, + { + "type": "tbody", + "children": [ + { + "type": "tr", + "children": [ + { + "type": "td", + "children": [ + { + "text": "TinyMCE Cloud" + } + ] + }, + { + "type": "td", + "children": [ + { + "text": "Get started for free" + } + ] + }, + { + "type": "td", + "children": [ + { + "text": "Yes!", + "format": [ + "strong" + ] + } + ] + } + ] + }, + { + "type": "tr", + "children": [ + { + "type": "td", + "children": [ + { + "text": "Plupload" + } + ] + }, + { + "type": "td", + "children": [ + { + "text": "Free" + } + ] + }, + { + "type": "td", + "children": [ + { + "text": "Yes!", + "format": [ + "strong" + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "h2", + "opData": { + "id": 7, + "type": "insert", + "uid": "martincook", + "timestamp": 1752576331000 + }, + "children": [ + { + "text": "Found a bug?" + } + ] + }, + { + "type": "p", + "children": [ + { + "text": " ", + "opData": { + "id": 7, + "type": "remove", + "uid": "martincook", + "timestamp": 1752576331000 + } + }, + { + "text": "If you believe you have found a bug please create an issue on the ", + "opData": { + "id": 7, + "type": "insert", + "uid": "martincook", + "timestamp": 1752576331000 + } + }, + { + "text": "GitHub repo", + "opData": { + "id": 7, + "type": "insert", + "uid": "martincook", + "timestamp": 1752576331000 + }, + "format": [ + { + "type": "a", + "attrs": { + "href": "https://github.com/tinymce/tinymce/issues" + } + } + ] + }, + { + "text": " to report it to the developers.", + "opData": { + "id": 7, + "type": "insert", + "uid": "martincook", + "timestamp": 1752576331000 + } + } + ] + }, + { + "type": "h2", + "children": [ + { + "text": "Finally…" + } + ] + }, + { + "type": "p", + "children": [ + { + "text": "Don’t forget to check out " + }, + { + "text": "Plupload", + "format": [ + { + "type": "a", + "attrs": { + "href": "http://www.plupload.com", + "target": "_blank", + "rel": "noopener" + } + } + ] + }, + { + "text": ", the upload solution featuring HTML5 upload support." + } + ] + }, + { + "type": "p", + "children": [ + { + "text": "Thanks for supporting TinyMCE. We hope it helps you and your users create great content." + } + ] + }, + { + "type": "p", + "children": [ + { + "text": "All the best from the TinyMCE team." + } + ] + } + ] +}; + +tinymce.init({ + selector: 'textarea#suggestededits-access-feedback', + height: 500, + plugins: 'suggestededits advlist anchor autolink code charmap emoticons fullscreen help image link lists media preview searchreplace table', + toolbar: 'undo redo | suggestededits | styles fontsizeinput | bold italic | align bullist numlist | table link image | code', + user_id: 'kalebwilson', + fetch_users: (userIds) => Promise.all(userIds + .map((userId) => new Promise((resolve) => + resolve(userDb[userId] || { id: userId })) + )), + content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }', + readonly: false, + suggestededits_access: 'feedback', + suggestededits_content: 'html', + suggestededits_model: model +}); \ No newline at end of file diff --git a/modules/ROOT/examples/live-demos/suggestededits-access-read/example.js b/modules/ROOT/examples/live-demos/suggestededits-access-read/example.js new file mode 100644 index 0000000000..9fee7e94ad --- /dev/null +++ b/modules/ROOT/examples/live-demos/suggestededits-access-read/example.js @@ -0,0 +1,21 @@ +const tinymceElement = document.querySelector('textarea#suggested-edits'); +const model = tinymceElement.getAttribute('suggestededits-model'); + +tinymce.init({ + selector: 'textarea#suggested-edits', + height: 500, + plugins: 'suggestededits advlist anchor autolink code charmap emoticons fullscreen help image link lists media preview searchreplace table', + toolbar: 'undo redo | suggestededits | styles fontsizeinput | bold italic | align bullist numlist | table link image | code', + user_id: 'michaelcook', + fetch_users: (userIds) => Promise.all(userIds + .map((userId) => + fetch(`/users/${userId}`) // Fetch user data from the server + .then((response) => response.json()) + .catch(() => ({ id: userId })) // Still return a valid user object even if the fetch fails + )), + content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }', + readonly: false, // Set to true to prevent edits to the content + suggestededits_access: 'read', // Set this value to restrict the permissions in the Suggested Edits view + suggestededits_content: 'html', + suggestededits_model: model +}); \ No newline at end of file diff --git a/modules/ROOT/examples/live-demos/suggestededits-access-read/index.html b/modules/ROOT/examples/live-demos/suggestededits-access-read/index.html new file mode 100644 index 0000000000..d0487689c2 --- /dev/null +++ b/modules/ROOT/examples/live-demos/suggestededits-access-read/index.html @@ -0,0 +1,44 @@ + diff --git a/modules/ROOT/examples/live-demos/suggestededits-access-read/index.js b/modules/ROOT/examples/live-demos/suggestededits-access-read/index.js new file mode 100644 index 0000000000..99336e6129 --- /dev/null +++ b/modules/ROOT/examples/live-demos/suggestededits-access-read/index.js @@ -0,0 +1,437 @@ +/** Fake user database */ +const userDb = { + adamhayes: { + id: 'adamhayes', + name: 'Adam Hayes', + avatar: `https://randomuser.me/api/portraits/men/4.jpg`, + }, + martincook: { + id: 'martincook', + name: 'Martin Cook', + avatar: `https://randomuser.me/api/portraits/men/5.jpg`, + }, + kalebwilson: { + id: 'kalebwilson', + name: 'Kaleb Wilson', + avatar: `https://randomuser.me/api/portraits/men/6.jpg`, + }, + sarahjones: { + id: 'sarahjones', + name: 'Sarah Jones', + avatar: `https://randomuser.me/api/portraits/women/1.jpg`, + } +}; + +const model = { + "history": { + "2": [ + { + "id": 1, + "uid": "sarahjones", + "timestamp": 1752576936000, + "feedback": "Nice improvement!" + } + ] + }, + "version": 1, + "contents": [ + { + "type": "p", + "children": [ + { + "type": "img", + "attrs": { + "style": "display: block; margin-left: auto; margin-right: auto;", + "title": "Tiny Logo", + "src": "https://www.tiny.cloud/docs/images/logos/android-chrome-256x256.png", + "alt": "TinyMCE Logo", + "width": "128", + "height": "128" + } + } + ] + }, + { + "type": "h2", + "attrs": { + "style": "text-align: center;" + }, + "children": [ + { + "text": "Welcome to the TinyMCE Suggested Edits " + }, + { + "text": "interactive ", + "opData": { + "id": 1, + "type": "insert", + "uid": "adamhayes", + "timestamp": 1752015064000 + } + }, + { + "text": "demo!" + } + ] + }, + { + "type": "p", + "attrs": { + "style": "text-align: center;" + }, + "children": [ + { + "text": "Try out the Suggested Edits feature" + }, + { + "text": ": type in the editor, apply formatting or delete some content. T", + "opData": { + "id": 2, + "type": "insert", + "uid": "adamhayes", + "timestamp": 1752415064000 + } + }, + { + "text": " by typing in the editor and t", + "opData": { + "id": 2, + "type": "remove", + "uid": "adamhayes", + "timestamp": 1752415064000 + } + }, + { + "text": "hen" + }, + { + "text": ",", + "opData": { + "id": 3, + "type": "insert", + "uid": "adamhayes", + "timestamp": 1752515064000 + } + }, + { + "text": " click" + }, + { + "text": "ing", + "opData": { + "id": 4, + "type": "remove", + "uid": "adamhayes", + "timestamp": 1752515064000 + } + }, + { + "text": " the Review Changes button in the toolbar" + }, + { + "text": " to see your changes", + "opData": { + "id": 5, + "type": "insert", + "uid": "kalebwilson", + "timestamp": 1752615064000 + } + }, + { + "text": "." + } + ] + }, + { + "type": "p", + "attrs": { + "style": "text-align: center;" + }, + "children": [ + { + "text": "And visit the " + }, + { + "text": "pricing page", + "opData": { + "id": 6, + "type": "modify", + "uid": "kalebwilson", + "timestamp": 1752615064000 + }, + "format": [ + { + "type": "a", + "attrs": { + "href": "https://www.tiny.cloud/pricing" + } + } + ], + "oldFormat": [ + { + "type": "a", + "attrs": { + "href": "https://www.tiny.cloud/pricing" + } + }, + "em" + ] + }, + { + "text": " to learn more about our Premium plugins." + } + ] + }, + { + "type": "h2", + "children": [ + { + "text": "A simple table to play with" + } + ] + }, + { + "type": "table", + "attrs": { + "style": "border-collapse: collapse; width: 100%;", + "border": "1" + }, + "children": [ + { + "type": "thead", + "children": [ + { + "type": "tr", + "attrs": { + "style": "text-align: left;" + }, + "children": [ + { + "type": "th", + "children": [ + { + "text": "Product" + } + ] + }, + { + "type": "th", + "children": [ + { + "text": "Cost" + } + ] + }, + { + "type": "th", + "children": [ + { + "text": "Really?" + } + ] + } + ] + } + ] + }, + { + "type": "tbody", + "children": [ + { + "type": "tr", + "children": [ + { + "type": "td", + "children": [ + { + "text": "TinyMCE Cloud" + } + ] + }, + { + "type": "td", + "children": [ + { + "text": "Get started for free" + } + ] + }, + { + "type": "td", + "children": [ + { + "text": "Yes!", + "format": [ + "strong" + ] + } + ] + } + ] + }, + { + "type": "tr", + "children": [ + { + "type": "td", + "children": [ + { + "text": "Plupload" + } + ] + }, + { + "type": "td", + "children": [ + { + "text": "Free" + } + ] + }, + { + "type": "td", + "children": [ + { + "text": "Yes!", + "format": [ + "strong" + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "h2", + "opData": { + "id": 7, + "type": "insert", + "uid": "martincook", + "timestamp": 1752576331000 + }, + "children": [ + { + "text": "Found a bug?" + } + ] + }, + { + "type": "p", + "children": [ + { + "text": " ", + "opData": { + "id": 7, + "type": "remove", + "uid": "martincook", + "timestamp": 1752576331000 + } + }, + { + "text": "If you believe you have found a bug please create an issue on the ", + "opData": { + "id": 7, + "type": "insert", + "uid": "martincook", + "timestamp": 1752576331000 + } + }, + { + "text": "GitHub repo", + "opData": { + "id": 7, + "type": "insert", + "uid": "martincook", + "timestamp": 1752576331000 + }, + "format": [ + { + "type": "a", + "attrs": { + "href": "https://github.com/tinymce/tinymce/issues" + } + } + ] + }, + { + "text": " to report it to the developers.", + "opData": { + "id": 7, + "type": "insert", + "uid": "martincook", + "timestamp": 1752576331000 + } + } + ] + }, + { + "type": "h2", + "children": [ + { + "text": "Finally…" + } + ] + }, + { + "type": "p", + "children": [ + { + "text": "Don’t forget to check out " + }, + { + "text": "Plupload", + "format": [ + { + "type": "a", + "attrs": { + "href": "http://www.plupload.com", + "target": "_blank", + "rel": "noopener" + } + } + ] + }, + { + "text": ", the upload solution featuring HTML5 upload support." + } + ] + }, + { + "type": "p", + "children": [ + { + "text": "Thanks for supporting TinyMCE. We hope it helps you and your users create great content." + } + ] + }, + { + "type": "p", + "children": [ + { + "text": "All the best from the TinyMCE team." + } + ] + } + ] +}; + +tinymce.init({ + selector: 'textarea#suggestededits-access-read', + height: 500, + plugins: 'suggestededits advlist anchor autolink code charmap emoticons fullscreen help image link lists media preview searchreplace table', + toolbar: 'undo redo | suggestededits | styles fontsizeinput | bold italic | align bullist numlist | table link image | code', + user_id: 'kalebwilson', + fetch_users: (userIds) => Promise.all(userIds + .map((userId) => new Promise((resolve) => + resolve(userDb[userId] || { id: userId })) + )), + content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }', + readonly: false, + suggestededits_access: 'read', + suggestededits_content: 'html', + suggestededits_model: model, +}); \ No newline at end of file diff --git a/modules/ROOT/examples/live-demos/suggestededits/example.js b/modules/ROOT/examples/live-demos/suggestededits/example.js new file mode 100644 index 0000000000..345c68ebc5 --- /dev/null +++ b/modules/ROOT/examples/live-demos/suggestededits/example.js @@ -0,0 +1,20 @@ +const tinymceElement = document.querySelector('textarea#suggested-edits'); +const model = tinymceElement.getAttribute('suggestededits-model'); + +tinymce.init({ + selector: 'textarea#suggested-edits', + height: 500, + plugins: 'suggestededits advlist anchor autolink code charmap emoticons fullscreen help image link lists media preview searchreplace table', + toolbar: 'undo redo | suggestededits | styles fontsizeinput | bold italic | align bullist numlist | table link image | code', + user_id: 'michaelcook', + fetch_users: (userIds) => Promise.all(userIds + .map((userId) => + fetch(`/users/${userId}`) // Fetch user data from the server + .then((response) => response.json()) + .catch(() => ({ id: userId })) // Still return a valid user object even if the fetch fails + )), + content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }', + suggestededits_model: model, + suggestededits_access: 'full', + suggestededits_content: 'html' +}); \ No newline at end of file diff --git a/modules/ROOT/examples/live-demos/suggestededits/index.html b/modules/ROOT/examples/live-demos/suggestededits/index.html new file mode 100644 index 0000000000..22a924a1b2 --- /dev/null +++ b/modules/ROOT/examples/live-demos/suggestededits/index.html @@ -0,0 +1,44 @@ + diff --git a/modules/ROOT/examples/live-demos/suggestededits/index.js b/modules/ROOT/examples/live-demos/suggestededits/index.js new file mode 100644 index 0000000000..22a13fc46e --- /dev/null +++ b/modules/ROOT/examples/live-demos/suggestededits/index.js @@ -0,0 +1,436 @@ +/** Fake user database */ +const userDb = { + adamhayes: { + id: 'adamhayes', + name: 'Adam Hayes', + avatar: `https://randomuser.me/api/portraits/men/4.jpg`, + }, + martincook: { + id: 'martincook', + name: 'Martin Cook', + avatar: `https://randomuser.me/api/portraits/men/5.jpg`, + }, + kalebwilson: { + id: 'kalebwilson', + name: 'Kaleb Wilson', + avatar: `https://randomuser.me/api/portraits/men/6.jpg`, + }, + sarahjones: { + id: 'sarahjones', + name: 'Sarah Jones', + avatar: `https://randomuser.me/api/portraits/women/1.jpg`, + } +}; + +const model = { + "history": { + "2": [ + { + "id": 1, + "uid": "sarahjones", + "timestamp": 1752576936000, + "feedback": "Nice improvement!" + } + ] + }, + "version": 1, + "contents": [ + { + "type": "p", + "children": [ + { + "type": "img", + "attrs": { + "style": "display: block; margin-left: auto; margin-right: auto;", + "title": "Tiny Logo", + "src": "https://www.tiny.cloud/docs/images/logos/android-chrome-256x256.png", + "alt": "TinyMCE Logo", + "width": "128", + "height": "128" + } + } + ] + }, + { + "type": "h2", + "attrs": { + "style": "text-align: center;" + }, + "children": [ + { + "text": "Welcome to the TinyMCE Suggested Edits " + }, + { + "text": "interactive ", + "opData": { + "id": 1, + "type": "insert", + "uid": "adamhayes", + "timestamp": 1752015064000 + } + }, + { + "text": "demo!" + } + ] + }, + { + "type": "p", + "attrs": { + "style": "text-align: center;" + }, + "children": [ + { + "text": "Try out the Suggested Edits feature" + }, + { + "text": ": type in the editor, apply formatting or delete some content. T", + "opData": { + "id": 2, + "type": "insert", + "uid": "adamhayes", + "timestamp": 1752415064000 + } + }, + { + "text": " by typing in the editor and t", + "opData": { + "id": 2, + "type": "remove", + "uid": "adamhayes", + "timestamp": 1752415064000 + } + }, + { + "text": "hen" + }, + { + "text": ",", + "opData": { + "id": 3, + "type": "insert", + "uid": "adamhayes", + "timestamp": 1752515064000 + } + }, + { + "text": " click" + }, + { + "text": "ing", + "opData": { + "id": 4, + "type": "remove", + "uid": "adamhayes", + "timestamp": 1752515064000 + } + }, + { + "text": " the Review Changes button in the toolbar" + }, + { + "text": " to see your changes", + "opData": { + "id": 5, + "type": "insert", + "uid": "kalebwilson", + "timestamp": 1752615064000 + } + }, + { + "text": "." + } + ] + }, + { + "type": "p", + "attrs": { + "style": "text-align: center;" + }, + "children": [ + { + "text": "And visit the " + }, + { + "text": "pricing page", + "opData": { + "id": 6, + "type": "modify", + "uid": "kalebwilson", + "timestamp": 1752615064000 + }, + "format": [ + { + "type": "a", + "attrs": { + "href": "https://www.tiny.cloud/pricing" + } + } + ], + "oldFormat": [ + { + "type": "a", + "attrs": { + "href": "https://www.tiny.cloud/pricing" + } + }, + "em" + ] + }, + { + "text": " to learn more about our Premium plugins." + } + ] + }, + { + "type": "h2", + "children": [ + { + "text": "A simple table to play with" + } + ] + }, + { + "type": "table", + "attrs": { + "style": "border-collapse: collapse; width: 100%;", + "border": "1" + }, + "children": [ + { + "type": "thead", + "children": [ + { + "type": "tr", + "attrs": { + "style": "text-align: left;" + }, + "children": [ + { + "type": "th", + "children": [ + { + "text": "Product" + } + ] + }, + { + "type": "th", + "children": [ + { + "text": "Cost" + } + ] + }, + { + "type": "th", + "children": [ + { + "text": "Really?" + } + ] + } + ] + } + ] + }, + { + "type": "tbody", + "children": [ + { + "type": "tr", + "children": [ + { + "type": "td", + "children": [ + { + "text": "TinyMCE Cloud" + } + ] + }, + { + "type": "td", + "children": [ + { + "text": "Get started for free" + } + ] + }, + { + "type": "td", + "children": [ + { + "text": "Yes!", + "format": [ + "strong" + ] + } + ] + } + ] + }, + { + "type": "tr", + "children": [ + { + "type": "td", + "children": [ + { + "text": "Plupload" + } + ] + }, + { + "type": "td", + "children": [ + { + "text": "Free" + } + ] + }, + { + "type": "td", + "children": [ + { + "text": "Yes!", + "format": [ + "strong" + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "h2", + "opData": { + "id": 7, + "type": "insert", + "uid": "martincook", + "timestamp": 1752576331000 + }, + "children": [ + { + "text": "Found a bug?" + } + ] + }, + { + "type": "p", + "children": [ + { + "text": " ", + "opData": { + "id": 7, + "type": "remove", + "uid": "martincook", + "timestamp": 1752576331000 + } + }, + { + "text": "If you believe you have found a bug please create an issue on the ", + "opData": { + "id": 7, + "type": "insert", + "uid": "martincook", + "timestamp": 1752576331000 + } + }, + { + "text": "GitHub repo", + "opData": { + "id": 7, + "type": "insert", + "uid": "martincook", + "timestamp": 1752576331000 + }, + "format": [ + { + "type": "a", + "attrs": { + "href": "https://github.com/tinymce/tinymce/issues" + } + } + ] + }, + { + "text": " to report it to the developers.", + "opData": { + "id": 7, + "type": "insert", + "uid": "martincook", + "timestamp": 1752576331000 + } + } + ] + }, + { + "type": "h2", + "children": [ + { + "text": "Finally…" + } + ] + }, + { + "type": "p", + "children": [ + { + "text": "Don’t forget to check out " + }, + { + "text": "Plupload", + "format": [ + { + "type": "a", + "attrs": { + "href": "http://www.plupload.com", + "target": "_blank", + "rel": "noopener" + } + } + ] + }, + { + "text": ", the upload solution featuring HTML5 upload support." + } + ] + }, + { + "type": "p", + "children": [ + { + "text": "Thanks for supporting TinyMCE. We hope it helps you and your users create great content." + } + ] + }, + { + "type": "p", + "children": [ + { + "text": "All the best from the TinyMCE team." + } + ] + } + ] +}; + +tinymce.init({ + selector: 'textarea#suggestededits', + height: 500, + plugins: 'suggestededits advlist anchor autolink code charmap emoticons fullscreen help image link lists media preview searchreplace table', + toolbar: 'undo redo | suggestededits | styles fontsizeinput | bold italic | align bullist numlist | table link image | code', + user_id: 'kalebwilson', + fetch_users: (userIds) => Promise.all(userIds + .map((userId) => new Promise((resolve) => + resolve(userDb[userId] || { id: userId })) + )), + content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }', + suggestededits_model: model, + suggestededits_content: 'html', + suggestededits_access: 'full' +}); diff --git a/modules/ROOT/images/ask-ai/ask-ai-widget.png b/modules/ROOT/images/ask-ai/ask-ai-widget.png new file mode 100644 index 0000000000..48ecc2d9af Binary files /dev/null and b/modules/ROOT/images/ask-ai/ask-ai-widget.png differ diff --git a/modules/ROOT/images/icons/checkmark-filled.svg b/modules/ROOT/images/icons/checkmark-filled.svg new file mode 100644 index 0000000000..5fb476da1d --- /dev/null +++ b/modules/ROOT/images/icons/checkmark-filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/modules/ROOT/images/icons/close-filled.svg b/modules/ROOT/images/icons/close-filled.svg new file mode 100644 index 0000000000..f4362e0de8 --- /dev/null +++ b/modules/ROOT/images/icons/close-filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/modules/ROOT/images/icons/feedback.svg b/modules/ROOT/images/icons/feedback.svg new file mode 100644 index 0000000000..8aefd52267 --- /dev/null +++ b/modules/ROOT/images/icons/feedback.svg @@ -0,0 +1,3 @@ + + + diff --git a/modules/ROOT/images/icons/suggestededits-badge.svg b/modules/ROOT/images/icons/suggestededits-badge.svg new file mode 100644 index 0000000000..4c1409ca15 --- /dev/null +++ b/modules/ROOT/images/icons/suggestededits-badge.svg @@ -0,0 +1 @@ + diff --git a/modules/ROOT/images/icons/suggestededits.svg b/modules/ROOT/images/icons/suggestededits.svg new file mode 100644 index 0000000000..dbd5bb6d0f --- /dev/null +++ b/modules/ROOT/images/icons/suggestededits.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/modules/ROOT/images/scripttag.png b/modules/ROOT/images/scripttag.png index 597fa5caf0..c9c5d1731b 100644 Binary files a/modules/ROOT/images/scripttag.png and b/modules/ROOT/images/scripttag.png differ diff --git a/modules/ROOT/moxiedoc_nav.adoc b/modules/ROOT/moxiedoc_nav.adoc index d5a3ba9f57..286e62940d 100644 --- a/modules/ROOT/moxiedoc_nav.adoc +++ b/modules/ROOT/moxiedoc_nav.adoc @@ -17,6 +17,7 @@ *** xref:apis/tinymce.shortcuts.adoc[tinymce.Shortcuts] *** xref:apis/tinymce.theme.adoc[tinymce.Theme] *** xref:apis/tinymce.undomanager.adoc[tinymce.UndoManager] +*** xref:apis/tinymce.userlookup.adoc[tinymce.UserLookup] *** xref:apis/tinymce.windowmanager.adoc[tinymce.WindowManager] ** tinymce.dom *** xref:apis/tinymce.dom.bookmarkmanager.adoc[tinymce.dom.BookmarkManager] diff --git a/modules/ROOT/nav.adoc b/modules/ROOT/nav.adoc index 04ebfa9fe1..19068e10fd 100644 --- a/modules/ROOT/nav.adoc +++ b/modules/ROOT/nav.adoc @@ -62,6 +62,7 @@ *** xref:localize-your-language.adoc[Localization] *** xref:spell-checking.adoc[Spell checking] *** xref:editor-content-css.adoc[CSS for rendering content] +*** xref:userlookup.adoc[Using the UserLookup API] ** Environment setup guides *** React framework **** xref:react-cloud.adoc[Using the Tiny Cloud] @@ -140,8 +141,6 @@ **** xref:configure-imageproxy-service.adoc[Image Proxy service settings] **** xref:configure-spelling-service.adoc[Spelling service settings] **** xref:self-hosting-hunspell.adoc[Spelling service - Using Hunspell dictionaries] -**** xref:configure-logging-services.adoc[Activity logging] -*** xref:introduction-to-premium-selfhosted-services.adoc[Server-side component installation without Docker] *** xref:troubleshoot-server.adoc[Troubleshoot server-side components] ** Customizing the editor appearance *** xref:customize-ui.adoc[Customizing the UI] @@ -345,6 +344,7 @@ **** xref:introduction-to-tiny-spellchecker.adoc[Spell Checker] **** xref:custom-dictionaries-for-tiny-spellchecker.adoc[Adding custom dictionaries] *** xref:autocorrect.adoc[Spelling Autocorrect] +*** xref:suggestededits.adoc[Suggested Edits] *** xref:tableofcontents.adoc[Table of Contents] *** xref:advanced-templates.adoc[Templates] *** Tiny Drive @@ -416,17 +416,14 @@ ** xref:release-notes.adoc[Release notes for {productname}] *** {productname} 8.0 **** xref:8.0-release-notes.adoc#overview[Overview] -**** xref:8.0-release-notes.adoc#new-premium-plugins[New Premium plugins] +**** xref:new-premium-plugins[New Premium plugins] **** xref:8.0-release-notes.adoc#accompanying-premium-plugin-changes[Accompanying Premium plugin changes] -**** xref:8.0-release-notes.adoc#accompanying-open-source-plugin-end-of-life-announcement[Accompanying open source plugin end-of-life-announcement] -**** xref:8.0-release-notes.adoc#accompanying-enhanced-skins-and-icon-packs-changes[Accompanying Enhanced Skins & Icon Packs changes] +**** xref:accompanying-enhanced-skins-and-icon-packs-changes[Accompanying Enhanced Skins & Icon Packs changes] **** xref:8.0-release-notes.adoc#improvements[Improvements] **** xref:8.0-release-notes.adoc#additions[Additions] **** xref:8.0-release-notes.adoc#changes[Changes] **** xref:8.0-release-notes.adoc#removed[Removed] **** xref:8.0-release-notes.adoc#bug-fixes[Bug fixes] -**** xref:8.0-release-notes.adoc#security-fixes[Security fixes] -**** xref:8.0-release-notes.adoc#known-issues[Known issues] ** xref:changelog.adoc[Changelog] * xref:invalid-api-key.adoc[Invalid API key] * xref:license-key.adoc[License key] diff --git a/modules/ROOT/pages/8.0-release-notes.adoc b/modules/ROOT/pages/8.0-release-notes.adoc index afcee2aab4..7604c6faae 100644 --- a/modules/ROOT/pages/8.0-release-notes.adoc +++ b/modules/ROOT/pages/8.0-release-notes.adoc @@ -1,4 +1,3 @@ - = {productname} {release-version} :release-version: 8.0.0 :navtitle: {productname} {release-version} @@ -12,85 +11,194 @@ include::partial$misc/admon-releasenotes-for-stable.adoc[] [[overview]] == Overview -{productname} {release-version} was released for {enterpriseversion} and {cloudname} on ,
^^, . These release notes provide an overview of the changes for {productname} {release-version}, including: +{productname} {release-version} was released for {enterpriseversion} and {cloudname} on Wednesday, July 23^rd^, 2025. These release notes provide an overview of the changes for {productname} {release-version}, including: -// Remove sections and section boilerplates as necessary. -// Pluralise as necessary or remove the placeholder plural marker. -* xref:new-premium-plugin[New Premium plugin] -* xref:new-open-source-plugin[New Open Source plugin] +* xref:new-premium-plugins[New Premium plugins] * xref:accompanying-premium-plugin-changes[Accompanying Premium plugin changes] -* xref:accompanying-premium-plugin-end-of-life-announcement[Accompanying Premium plugin end-of-life announcement] -* xref:accompanying-open-source-plugin-end-of-life-announcement[Accompanying open source plugin end-of-life-announcement] * xref:accompanying-enhanced-skins-and-icon-packs-changes[Accompanying Enhanced Skins & Icon Packs changes] * xref:improvements[Improvements] * xref:additions[Additions] * xref:changes[Changes] * xref:bug-fixes[Bug fixes] -* xref:security-fixes[Security fixes] * xref:deprecated[Deprecated] -* xref:known-issues[Known issues] +[IMPORTANT] +==== +This release includes breaking changes to the license key system. All commercial self-hosted deployments must update their license keys to use the new `T8LK:` prefix format. For details on this and other breaking changes when considering upgrading, see xref:migration-from-7x.adoc[Migrating from {productname} 7 to {productname} {release-version}]. +==== -[[new-premium-plugin]] -New Premium plugin +[[new-premium-plugins]] +== New Premium plugins The following new Premium plugin was released alongside {productname} {release-version}. -=== +=== Suggested Edits -The new Premium plugin, **** // description here. +The {productname} {release-version} release includes an accompanying release of the **Suggested Edits** premium plugin. -For information on the **** plugin, see xref:.adoc[]. +The **Suggested Edits** plugin allows multiple users to collaborate on a document. The review window shows which user suggested which edits, whether they added, removed, modified, or replaced any content, and allows users to provide feedback on those suggestions or give a final review by accepting or rejecting them. +For information on the **Suggested Edits** plugin, see: xref:suggestededits.adoc[Suggested Edits]. -[[new-open-source-plugin]] -== New Open Source plugin -The following new Open Source plugin was released alongside {productname} {release-version}. +[[accompanying-premium-plugin-changes]] +== Accompanying Premium plugin changes -=== +The following premium plugin updates were released alongside {productname} {release-version}. -The new open source plugin, **** // description here. +=== Mentions -For information on the **** plugin, see xref:.adoc[]. +The {productname} {release-version} release includes an accompanying release of the **Mentions** premium plugin. +**Mentions** Premium plugin includes the following fix and removal. -[[accompanying-premium-plugin-changes]] -== Accompanying Premium plugin changes +=== Mentions username not updating with skin changes in dark mode -The following premium plugin updates were released alongside {productname} {release-version}. +Previously, the username displayed on the mentions card did not update correctly when switching between skins, particularly in dark mode. This issue caused the username to become invisible against the dark background, making it difficult for users to read. + +To resolve this, the styling for the username has been updated. The class now uses the `@text-color` variable for its default styling, ensuring that it inherits the correct color from its parent element when the item is active. This change guarantees that the username remains visible and readable in both light and dark themes. + +=== Removed unused Mentions plugin stylesheet + +In {release-version}, the unused `mentions.css` file was removed from the **Mentions** plugin. In earlier versions (such as 7.9), this empty CSS file was still being loaded, despite its contents having been moved to the Oxide theme in a prior update. This change ensures cleaner resource loading and avoids unnecessary network requests. + +[NOTE] +The removal does not impact plugin functionality and applies to all editor modes. + +For information on the **Mentions** plugin, see: xref:mentions.adoc[Mentions]. + +=== Spell Checker + +The {productname} {release-version} release includes an accompanying release of the **Spell Checker** premium plugin. + +**Spell Checker** Premium plugin includes the following fix and improvements. + +=== Content marked with `lang` attributes was sometimes not spellchecked correctly. +// #TINY-12101 + +Previously, the spellchecker did not consistently handle content marked with different `lang` attributes, particularly when the language codes used different formats. This inconsistency led to missed spellchecking for words in spans that used alternate casing or separator conventions (e.g., `en-GB` vs `en_GB`). + +This issue was introduced in {productname} 6.4, where documents containing a mix of language codes using different formats would not be reliably spellchecked. As a result, misspelled words in spans using alternate casing or separator conventions were not detected, even though they functioned as expected in version 6.3. + +In {release-version}, support for mixed-case and mixed-format language codes has been restored. The spellchecker now correctly identifies and handles variations in language attribute formatting, ensuring that content is reliably checked regardless of code style. This improvement enables accurate spellchecking for documents that include multiple languages and diverse regional conventions. + +==== Input label for misspelled word was not announced by screen readers. +// #TINY-12167 + +In previous versions, the Spell Checker dialog's misspelled word input field lacked proper accessibility labeling, preventing screen readers from announcing its purpose. {productname} {release-version} resolves this accessibility limitation by adding the appropriate label association between the "Misspelled Word" text and its input field. This enhancement allows screen readers to correctly identify and announce the input field's purpose, improving the spell checking experience for users who rely on assistive technologies. -=== +=== Language code standardization to RFC5646/BCP47 format +// #TINY-12090 -The {productname} {release-version} release includes an accompanying release of the **** premium plugin. +The {productname} {release-version} release introduces standardized support for language codes using the RFC5646 (also known as BCP47) format. This update applies to all language-related configurations, including: -**** includes the following . +* UI language packs (premium and community) +* Spellchecker language codes +* HTML `lang` attributes +* Content language selection -==== +**Changes** -// CCFR here. +* Language codes now use hyphens (`-`) instead of underscores (`_`). For example: +** `'en-US'` instead of `'en_US'` +** `'zh-TW'` instead of `'zh_TW'` +** `'pt-BR'` instead of `'pt_BR'` +* Base language codes without regions (e.g., `'en'`, `'es'`, `'fr'`) remain unchanged. +* File names for language packs have been updated to use hyphens. +* Premium plugins have been updated to recognize RFC5646-compliant codes. -For information on the **** plugin, see: xref:.adoc[]. +**Backward Compatibility** +To ensure a smooth migration -[[accompanying-premium-plugin-end-of-life-announcement]] -== Accompanying Premium plugin end-of-life announcement +* Both hyphenated and underscore formats are supported in {productname} {release-version}. +* A console warning is displayed when the legacy underscore format is used. +* Both formats of language pack files are distributed with {productname} {release-version}. +* {productname} 9 will support only the RFC5646-compliant hyphenated format. -The following Premium plugin has been announced as reaching its end-of-life: +**Migration** -=== +. Update language-related configuration in `tinymce.init`: ++ +[source,javascript] +---- +language: 'en-US', +spellchecker_language: 'en-US' +---- ++ +. Update `content_langs` configuration: ++ +[source,javascript] +---- +content_langs: [ + { title: 'English (US)', code: 'en-US' }, + { title: 'Portuguese (Brazil)', code: 'pt-BR' } +] +---- ++ +. Rename any custom dictionary files to use hyphens (e.g., `'en-GB.txt'` instead of `'en_GB.txt'`). +. Ensure any server-side integrations or custom language pack files follow the new naming convention. -{productname}’s xref:.adoc[] plugin will be deactivated on
, , and is no longer available for purchase. +This standardization aligns {productname} with modern web standards and improves consistency across its multilingual features. +For information on the **Spell Checker** premium plugin, see: xref:introduction-to-tiny-spellchecker.adoc[Spell Checker plugin] or for an complete list of supported languages, see: xref:introduction-to-tiny-spellchecker.adoc#supported-languages[Supported languages]. -[[accompanying-open-source-plugin-end-of-life-announcement]] -== Accompanying open source plugin end-of-life announcement +=== Accessibility Checker -The following open source plugin has been announced as reaching its end-of-life: +The {productname} {release-version} release includes an accompanying release of the **Accessibility Checker** premium plugin. -=== +**Accessibility Checker** Premium plugin includes the following improvements. -{productname}’s xref:.adoc[] plugin will be deactivated on
, , and is no longer available for purchase. +=== Accessibility: Enhanced image accessibility checks in a11ychecker plugin +// #TINY-10903 + +The {productname} {release-version} introduces four new image accessibility rules (xref:a11ychecker.adoc##image-rules[Image rules]) in the xref:a11ychecker.adoc[a11ychecker] plugin to improve compliance and authoring guidance. These rules help identify common accessibility issues related to image use and provide actionable recommendations: + +* *xref:a11ychecker.adoc#I1[I1: Mixed Signals & Decorative Policy]*: Flags images with conflicting accessibility indicators (e.g., `alt` with `role="presentation"`) and ensures decorative images comply with best practices. +* *xref:a11ychecker.adoc#I2[I2: Alt Attribute Requirement]*: Enforces the presence of the `alt` attribute on all `` elements, regardless of other labeling mechanisms like `aria-label`, `aria-labelledby`, or `title`. +* *xref:a11ychecker.adoc#I3[I3: Filename Detection]*: Warns when `alt` text appears to be a filename (e.g., `image123.jpg`), including cases with URL encoding. +* *xref:a11ychecker.adoc#I4[I4: Alt Text Length]*: Detects `alt` text that exceeds a configurable maximum length, promoting concise descriptions. The default limit is 150 characters and can be adjusted using the new xref:a11ychecker.adoc#a11ychecker_alt_text_max_length[`a11ychecker_alt_text_max_length`] setting. + +Additional improvements include a rule precedence system to avoid duplicate violations, centralized image intent detection (e.g., decorative, informative, mixed signals, incomplete), and more readable user-facing messages. Technical terminology such as "alternative text" has been replaced with "text description," and repair instructions are now more contextual and user-friendly. + +=== New `a11ychecker_alt_text_max_length` configuration option for Accessibility Checker alt text length +// #TINY-10903 + +A new configuration option, xref:a11ychecker.adoc#a11ychecker_alt_text_max_length[`a11ychecker_alt_text_max_length`], has been added to the Accessibility Checker plugin. This option allows users to configure the maximum allowed length for image alt text descriptions. The default value is 150 characters. For example: + +[source,js] +---- +tinymce.init({ + selector: 'textarea', + plugins: 'a11ychecker', + a11ychecker_alt_text_max_length: 150 // Set maximum length to 150 characters +}); +---- + +For more information on the Accessibility Checker plugin, see: xref:a11ychecker.adoc[Accessibility Checker]. + +=== PowerPaste + +The {productname} {release-version} release includes an accompanying release of the **PowerPaste** premium plugin. + +==== Word import of lists with a "lighter" level failed parsing. + +When users pasted content from Microsoft Word documents containing lists styled with Word’s "No List" setting into the editor with PowerPaste enabled, the operation failed due to a parsing error. As a result, the content was not inserted into the editor, causing disruption to content workflows. {productname} {release-version} addresses this issue by updating the **PowerPaste** parser to correctly handle lists with the "No List" style. As a result, users can now successfully paste such content into the editor without encountering errors. + +For information on the **PowerPaste** plugin, see: xref:introduction-to-powerpaste.adoc[PowerPaste]. + +=== Comments + +The {productname} {release-version} release includes an accompanying release of the **Comments** premium plugin. + +**Comments** Premium plugin includes the following removal. + +==== Removed unused Comments stylesheet +// #TINY-11287 + +In {release-version}, the unused `tinycomments.css` file was removed from the **Comments** plugin. In earlier versions (such as 7.9), this empty CSS file was still being loaded, despite its contents having been moved to the Oxide theme in a prior update. This change ensures cleaner resource loading and avoids unnecessary network requests. + +For information on the **Comments** plugin, see: xref:introduction-to-tiny-comments.adoc[Introduction to Tiny Comments]. [[accompanying-enhanced-skins-and-icon-packs-changes]] @@ -110,87 +218,472 @@ For information on using Enhanced Skins & Icon Packs, see: xref:enhanced-skins-a [[improvements]] == Improvements -{productname} {release-version} also includes the following improvement: +{productname} {release-version} also includes the following improvements: + +=== Enhanced cross-origin resource handling + +Added a new function-based `crossorigin` configuration option that provides granular control over cross-origin resource loading. The function receives the resource URL and type (script or stylesheet) as parameters and can return 'anonymous', 'use-credentials', or undefined to control the crossorigin attribute. -=== -// #TINY-vwxyz1 +.Example: Setting `+crossorigin="anonymous"+` to script. +[source,html,subs="attributes+"] +---- + +---- +Or; -// CCFR here. +.Example: Setting reusable constant function. +[source,js] +---- +const crossOriginFunction = (url, resourceType) => { + // Returning 'anonymous' or 'use-credentials' here would explicitly set the attribute + return 'anonymous'; + // return 'use-credentials'; + // return undefined; // Omits the 'crossorigin' attribute for all resources by returning undefined +}; + +tinymce.init({ + selector: "textarea", + crossorigin: crossOriginFunction +}); +---- + +This improvement ensures consistent resource loading behavior across different deployment scenarios and provides better control over CORS settings for both scripts and stylesheets. + +For more details, see xref:tinymce-and-cors.adoc#crossorigin[crossorigin configuration option] documentation. + +=== Tooltips can now be closed by pressing the `escape` key. +// #TINY-12054 + +Prior to {release-version}, tooltips could not be dismissed using the keyboard, which posed an accessibility concern. As of {productname} {release-version}, tooltips can now be closed via the `escape` key, improving accessibility. + +=== Instructions on how to navigate the color swatch, image select and insert table widget are now announced by screen readers. +// #TINY-12189 + +Previously, interactive elements such as the color swatch, create table grid, and image select were missing an `aria-label`. This resulted in a lack of instructions for keyboard users and hindered overall accessibility. + +{productname} {release-version} now includes `aria-labels` for these elements, providing clear keyboard navigation instructions. This improvement ensures a more accessible experience for all users. + +=== Resize handles are more accessible with `role` and `aria-valuetext` attributes. +// #TINY-11421 + +In previous versions of {productname}, the editor's resize handle lacked a `role` attribute, raising accessibility concerns. + +In {productname} {release-version}, the resize handle now includes a `role` seperator and an `aria-valuetext` attribute that dynamically reflects the current dimensions of the editor. These improvements enhance accessibility and ensure more accurate announcements by screen readers. + +=== Focus is now restored to a dialog after closing an alert, confirmation or another dialog +// #TINY-12038 + +Previously, when a dialog was opened from within another dialog — such as a file picker triggered via a `file_picker_callback` the focus was not correctly restored to the original dialog after closing the secondary one. This issue also affected alert and confirmation dialogs opened from within a parent dialog. + +As a result, keyboard users experienced disrupted navigation and loss of focus context, impairing accessibility and usability in multi-dialog workflows. + +In {release-version}, {productname} now restores focus to the triggering button in the parent dialog once the nested dialog is closed. This improvement enhances keyboard navigation and accessibility in scenarios involving multiple overlapping dialogs. + +=== Added support for `skip_focus` option in ToggleToolbarDrawer command +// #TINY-12044 + +The `ToggleToolbarDrawer` command now supports the standard `skip_focus` option, which allows opening the toolbar drawer without focusing the editor. This provides consistent behavior with other editor commands. + +.Updated new `skip_focus` option usage command +[source] +---- +editor.execCommand('ToggleToolbarDrawer', false, null, { skip_focus: true }); +---- + +For more information on the `ToggleToolbarDrawer` command, see: xref:editor-command-identifiers.adoc[Available Commands]. + +=== Split buttons are now rendered as separate action and chevron buttons. +// #TINY-8665 + +Split buttons in {productname} {release-version} now render as two independent, focusable components instead of a single composite element. The main button performs the primary action, while the chevron button controls the dropdown menu. This design improvement enhances accessibility by allowing separate focus targets and ARIA labels for each button. + +=== The translate API now automatically replaces three dots in a row with an ellipsis character. +// #TINY-12155 + +Previously, menu items and tooltips containing three consecutive periods (`...`) were read aloud by the JAWS screen reader as "dot dot dot," which could confuse users relying on assistive technologies. To enhance accessibility, the `translate` API in {productname} {release-version} now automatically replaces three dots with the typographic ellipsis character (`…`) across all translations. This update ensures a consistent visual appearance while improving screen reader behavior, as the ellipsis character is not read aloud. The change is implemented directly in the translation function, ensuring comprehensive coverage across all languages. [[additions]] == Additions -{productname} {release-version} also includes the following addition: +{productname} {release-version} also includes the following additions: + +=== New `allow_html_in_comments` option to allow HTML-like content inside comment data +// #TINY-12220 + +Comments containing HTML-like content are removed from the editor's content by default for security reasons. This is done during the sanitization process, which ensures that any HTML-like content in comments is stripped out. + +{productname} introduces a new configuration option, xref:content-filtering.adoc#allow_html_in_comments[allow_html_in_comments], to give users control over whether HTML-like content in comments should be retained or removed. By default, this content is removed for security, but setting the option to `true` allows HTML-like content in comments to be preserved when needed. + +=== Split button tooltip configuration +// #TINY-8665 + +The `+chevronTooltip+` property provides custom tooltip text for the chevron button portion of split buttons. When `+chevronTooltip+` is not specified, {productname} automatically generates tooltip text using the pattern `+"{tooltip} menu"+`. The `+setTooltip+` method updates both main and chevron tooltips to maintain consistency. + +For more information, see xref:custom-split-toolbar-button.adoc[Split toolbar buttons]. + +=== New User Lookup API for retrieving and caching user details +// #TINY-11974 + +{productname} {release-version} introduces a new xref:userlookup.adoc[User Lookup API] that enables integrations to retrieve and cache user details (such as names and avatars) and identify the current user within the editor. This API is particularly useful when building features that rely on user context, such as commenting systems or displaying lists of elements containing user information. + +The User Lookup API provides efficient user data management with built-in caching, reducing redundant network requests and improving performance. It can be used as a universal solution for `+_author+` and `+_author_avatar+` configurations across various plugins, including currently supported plugins such as **Suggested Edits**, **Comments**, and **Revision History**. + +Key features include: + +* **Current user identification**: Set and retrieve the active user's ID using the new xref:userlookup.adoc#user_id[`user_id`] configuration option +* **Flexible user fetching**: Configure custom user data retrieval through the xref:userlookup.adoc#fetch_users[`fetch_users`] callback function +* **Built-in caching**: Automatic caching of user data to minimize API calls +* **Fallback handling**: Automatic generation of default user information when custom fetch functions are not provided +* **Extensible user objects**: Support for custom metadata through the `custom` property + +==== Basic configuration + +The API requires two main configuration options: + +[source,js] +---- +tinymce.init({ + selector: 'textarea', + user_id: 'alextaylor', // Set the current user's unique identifier + fetch_users: (userIds) => { + // Return a Promise that resolves to an array of user objects + return Promise.all( + userIds.map(userId => + fetch(`/users/${userId}`) + .then(response => response.json()) + .catch(() => ({ id: userId })) // Fallback for failed requests + ) + ); + } +}); +---- + +==== API usage + +Once configured, the API provides two main methods: + +* `editor.userLookup.userId` - Returns the current user's ID +* `editor.userLookup.fetchUsers(userIds)` - Fetches and caches user information -=== -// #TINY-vwxyz1 +[source,js] +---- +editor.on('init', () => { + // Get current user ID + console.log('Current user:', editor.userLookup.userId); + + // Fetch multiple users + const users = editor.userLookup.fetchUsers(['user-1', 'user-2']); + + // Handle individual user promises + users['user-1'].then(user => { + console.log('User 1 name:', user.name); + }); +}); +---- -// CCFR here. +For comprehensive documentation and examples, see: xref:userlookup.adoc[User Lookup API]. +=== New `list_max_depth` option to limit list indentation +// #TINY-11937 + +{productname} {release-version} introduced a new `list_max_depth` option that allows configuration of the maximum indent depth for list items. This setting accepts a non-negative integer value, where `0` permits list creation without any indentation, and higher values set the maximum allowable indent depth. Negative values are invalid and will trigger an error. When the option is not set, list behavior remains unchanged. + +The setting applies only to list indentation and does not affect paragraph indentation. Existing content is not altered, and pasted lists that exceed the maximum indent depth will retain their structure but cannot be further indented. When multiple list items are selected, only items eligible for indentation will be indented individually—validation is applied per item, not across the full selection. + +Outdent functionality remains unaffected and always available. The state of indent and outdent controls reflects the current indentation capabilities based on this setting. + +==== Example configuration + +The following example shows a basic configuration for the `list_max_depth` option, which limits list indentation to a maximum of 2 levels: + +[source,js] +---- +tinymce.init({ + selector: "textarea", + plugins: [ + "advlist", "anchor", "autolink", "charmap", "code", "fullscreen", + "help", "image", "insertdatetime", "link", "lists", "media", + "preview", "searchreplace", "table", "visualblocks", + ], + list_max_depth: 2, + toolbar: "undo redo | styles | bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image", +}); +---- + +For more information, see: xref:user-formatting-options.adoc#list_max_depth[User formatting - List max depth]. [[changes]] == Changes -{productname} {release-version} also includes the following change: +{productname} {release-version} includes the following changes: + +=== Enhanced license key system with T8LK prefix + +The license key system in {productname} 8 has been enhanced to provide improved subscription access control and streamlined renewal processes. This change primarily affects self-hosted deployments, as cloud users ({cloudname}) do not require a license key. + +[IMPORTANT] +==== +* If you use {cloudname}, no license key is required - your cloud subscription automatically includes the commercial license. +* For self-hosted deployments, all commercial license keys must use the new `T8LK:` prefix format. +* Existing TinyMCE 7 license keys cannot be made compatible with TinyMCE 8 by adding the T8LK prefix. +==== + +Key changes for self-hosted deployments: + +* New `T8LK:` prefix requirement for all commercial license keys. +* No server communication or "phone home" checks required +* Support for both date-based and version-locked keys +* Streamlined hybrid deployment configuration +* Improved support for air-gapped and offline environments + +[IMPORTANT] +==== +*Cloud Deployments:* + +* When using {cloudname}, only an API key is required - no license key needed +* Your cloud subscription automatically includes the commercial license + +*Self-hosted Deployments:* + +* Commercial use: Contact your account manager or visit link:https://www.tiny.cloud/my-account[Tiny Cloud Account] to obtain a T8LK-prefixed license key +* GPL use: Set `license_key: 'gpl'` (case insensitive) +* Open source projects using premium features: Use the combined format `license_key: 'GPL+T8LK:...'` + +*Required License Key Manager Setup for Self-hosted Commercial Deployments:* + +For commercial license keys to function, the license key manager must be properly configured. **The editor will not work without this setup.** + +* **Standalone hosting** (e.g., in a `/public` folder): The `+licensekeymanager+` folder is **required** to be located within the `+plugins+` folder alongside all other plugins. +* **Bundled applications**: The `+licensekeymanager/index.js+` file is **required** to be imported, otherwise the editor will result in a disabled state. + +.Example: Importing the License Key Manager in a bundled application +[source,javascript] +---- +import 'tinymce/plugins/licensekeymanager'; +---- + +==== -=== -// #TINY-vwxyz1 +=== Upgrading from {productname} 7 -// CCFR here. +* *Cloud Users ({cloudname})*: No license key changes needed - simply update your {productname} version. +* *Self-hosted Users*: Contact your account manager to obtain a new T8LK-prefixed license key. + +For more information on the new license key system, see: xref:license-key.adoc[License Key]. + +=== DomParser no longer tries to fix some nodes when parsed with a context +// #TINY-8205 + +Previously, the `DomParser` would attempt to fix invalid HTML structures when parsing with a context, such as when inserting content into a specific element. This behavior lead to unexpected results, especially when the structure was not valid in the context of the parent element. + +In {productname} {release-version}, the `DomParser` has been updated to no longer attempt to fix invalid nodes when parsing with a context. Instead, it will validate the nodes and only insert them if they are valid in the context of their parent element. If the nodes are invalid, they will not be inserted. [[removed]] == Removed -{productname} {release-version} also includes the following removal: +{productname} {release-version} also includes the following removal: -=== -// #TINY-vwxyz1 +=== Removed undocumented `documentBaseUrl` property from Editor instances. `documentBaseURI` is the supported property now. +// #TINY-12182 -// CCFR here. +An undocumented `documentBaseUrl` property was previously accessible on `Editor` instances. This property was not part of the supported public API and could lead to confusion or inconsistent usage across implementations. +To improve API clarity and maintain a well-defined interface, the `documentBaseUrl` property has been removed. Integrators should use the officially supported `documentBaseURI` property to access the base URI of the document. [[bug-fixes]] == Bug fixes -{productname} {release-version} also includes the following bug fix: +{productname} {release-version} also includes the following bug fixes: + +=== Tabbing when a `figure` was selected didn't move the selection to the `figcaption` correctly. +// #TINY-11753 + +Previously, the `tab` key did not correctly handle nested components. Specifically, when a `CET` (Content Editable True) element was placed inside a `CEF` (Content Editable False) element such as a `figcaption`, focus behavior was unintuitive. This led to inconsistent and confusing keyboard navigation when interacting with editable nested elements like a `figcaption`. + +{productname} {release-version} addresses this issue by ensuring that pressing the `tab` key correctly moves focus to the `CET` element when it is nested within a `CEF` element. This ensures smooth and predictable keyboard navigation for nested editable elements such as a `figcaption`. + +=== Cutting a whole HTML element would add an empty paragraph. +// #TINY-12171 + +Previously, an issue was identified where cutting an entire block element, such as a `div` or `p` HTML element, resulted in the block being replaced by an empty paragraph. This behavior only occurred for blocks not contained within tables and led to inconsistent outcomes where cutting content inside tables behaved differently from outside. Additionally, the behavior diverged from that of the backspace `delete` operation. + +{productname} {release-version} resolves this issue by unifying the behavior for content both inside and outside tables. The updated implementation aligns cutting behavior with that of the backspace `delete` operation, ensuring a more consistent and predictable editing experience. + +=== Codesample dialog would not disable spellcheck as expected. +// #TINY-12120 + +Previously, the `browser_spellcheck` setting did not correctly disable spellchecking in the Codesample dialog's text area. This was because the setting was applied to the dialog’s text area rather than the main editor instance. + +In addition, the `advcode` and `code` plugin dialogs did not explicitly disable browser-based spellchecking. As a result, red underlines appeared unnecessarily in code content, regardless of the `browser_spellcheck` setting. + +=== Attempting to add a newline directly after a `block` element such as an image would cause an error. +// #TINY-12045 + +In previous versions of {productname}, the content selection logic incorrectly assumed that the target element resided within a text node. In reality, it was placed directly inside a `
` element. This mismatch led to a scenario where the selected content was mistakenly reinserted into itself, resulting in errors when pressing `Enter` immediately after an image and introducing a risk of data loss. + +This issue has been resolved in {productname} {release-version}. The selection logic has been updated to correctly identify and handle content within `
` elements such that it does not try to insert content within itself. As a result, pressing `Enter` after an image inside a `
` element no longer triggers errors or causes unintended behavior. + +=== In inline mode, pressing the back button dismissed the context toolbar and moved focus from the selected image to the editor. +// #TINY-12118 + +In inline mode, the context form toolbar incorrectly shifted focus from the selected image to the editor container. As a result, the plugin which relies on the current selection to apply changes was unable to apply attributes like `alt` text to the correct image. + +{productname} {release-version} resolves this by ensuring that the focus is preserved properly, ensuring that the plugin now applies changes such as `alt` text to the correct image in inline mode. + +=== Toolbar drawer now closes when the editor loses focus. +// #TINY-12125 -=== -// #TINY-vwxyz1 +In previous versions of {productname}, the floating toolbar remained visible even when the editor lost focus. This behavior introduced potential accessibility concerns, as the toolbar was not properly associated with the editor it controlled, potentially causing confusion for users. -// CCFR here. +In {productname} {release-version}, the toolbar now automatically closes when the editor loses focus. This ensures it is only displayed within the appropriate context, enhancing accessibility and improving the overall user experience. +=== Arrow key navigation between buttons was blocked when a button is hovered by the mouse. +// #TINY-12163 -[[security-fixes]] -== Security fixes +In previous versions of {productname}, if a button was hovered by the mouse while using arrow keys for navigation, focus would return to the previously focused element. This caused the selection to become stuck on the button with the `mouseover` event. -{productname} {release-version} includes : +{productname} {release-version} resolves this issue by preventing the `mouseover` event from being triggered again during arrow key navigation, ensuring a seamless user experience when navigating with arrow keys. -=== -// #TINY-vwxyz1 +=== It was not possible to navigate out of a `figcaption` using the left and right arrow keys in Firefox. +// #TINY-11982 -// CCFR here. +Prior to {productname} {release-version}, Firefox exhibited an issue where pressing the left or right arrow keys would not allow users to exit the image `figcaption` element. This behavior was inconsistent with other major browsers such as Google Chrome and Safari, where the arrow keys functioned as expected and allowed seamless navigation out of the `figcaption`. + +With {productname} {release-version}, this issue has been resolved. Users can now exit the `figcaption` using the left/right arrow keys in Firefox, aligning its behavior with that of other browsers. This update ensures a more consistent user experience. + +=== Decorative images would lose their decorative status when the `alt` subtoolbar was closed when using the apply button. +// #TINY-11912 + +Previously, an issue was identified where closing the `alt` subtoolbar using the `Apply` button would inadvertently remove the decorative status from an image that was already marked as decorative. This occurred because the `onSetup` API disabled the input group, leading the `Apply` logic to incorrectly interpret the image as non-decorative. + +This issue has been resolved in {productname} {release-version}. The `onSetup` and `Apply` behavior has been aligned to consistently evaluate the input group, ensuring the decorative status is preserved as expected. + +=== The HTML schema did not treat `colgroup` or `col` elements as block elements. +// #TINY-12092 + +In previous versions of {productname}, whitespace between `` and `` tags was not removed by {productname}'s HTML schema. This behavior caused the editor to preserve unnecessary whitespace characters in the table markup. + +This issue has been resolved in `{release-version}`. Both `colgroup` and `col` elements are now treated as block-level elements, ensuring that extraneous whitespace is stripped from these areas. As a result, the editor now produces cleaner and more concise table markup. + +=== Deleting a whole element would sometimes modify nearby content +// #TINY-11868 + +Previously, deleting a complete HTML element using the "Source Code" editor either by cutting `+Ctrl+X+` or `+Cmd+X+`, backspacing, or pressing delete could inadvertently modify surrounding content. For example, removing a `
` element that precedes a `

` tag could cause the `

` to be transformed into a `

`, resulting in unintended structural changes. + +This issue has been resolved in {productname} {release-version} by improving how element deletions are handled within the editor's DOM logic. Cutting or deleting a selected element now preserves the integrity and tag type of adjacent elements. Users can expect consistent and predictable editing behavior when modifying HTML source content. + +=== Strikethrough format could be added outside font size format, which renders incorrectly in some browsers. +// #TINY-12004 + +Previously, there was an issue where combining strikethrough (`s` element) and font size formatting could lead to incorrect rendering in some browsers, particularly Chrome. This occurred when the `s` (strikethrough) element was placed outside a font size `span`, causing the strikethrough line to render incorrectly. + +The fix ensures that the `s` element is now properly nested inside the font size element, improving visual consistency across browsers. + +=== Long tooltips could overflow narrow browser windows +// #TINY-11884 + +Previously, long tooltips could overflow narrow browser windows, causing the tooltip text to be partially cut off and unreadable. This issue was most noticeable with the `...` ellipsis toolbar for the "reveal or hide additional toolbar options" button, where the tooltip text would not fit within the narrow viewport. + +In {productname} {release-version}, the tooltip behavior has been improved to ensure that long tooltips are properly positioned and wrapped within narrow browser windows. This prevents overflow and ensures that the full tooltip text is visible, even in smaller viewports. + +=== Tab to create a new row in tables with a non-editable final row would freeze the editor. +// #TINY-12018 + +Previously, when tabbing into a non-editable final row of a table, the editor failed to recognize that the row was read-only and instead duplicated it. This resulted in an attempt to place the cursor in the duplicated row, which again could not be edited. The process would repeat indefinitely, creating a loop that ultimately froze the editor. + +{productname} {release-version} resolves this issue by detecting when the final row of a table is read-only and preventing the insertion of a new row in such cases. This ensures stable behavior when navigating through tables using the Tab key. + +=== Nested font sizes no longer cause excessive line spacing +// #TINY-12073 + +Previously, applying different font sizes to nested elements could result in inconsistent and overly large line heights. This affected users by introducing visual clutter and making text blocks appear misaligned or difficult to read. + +{productname} {release-version} introduces a fix that ensures line heights are calculated based on the font size visible in the new line, rather than inheriting line-height from parent element. This means that when different font sizes are applied within the same content, the line heights will be consistent and proportional to the visible font size. + +=== Improved structure retention when inserting invalid HTML fragments +// #TINY-8205 + +Previously, when inserting HTML fragments through APIs or pasting, the parser would attempt to fix invalid structures, which could lead to the loss of valid structures like tables and lists. This behavior was inconsistent and could result in unexpected outcomes when handling complex HTML content. + +In {productname} {release-version}, the parser has been updated to improve the retention of structures when inserting HTML fragments. It now validates these fragments in the context of their parent structure, ensuring that valid structures are preserved even if the fragment contains invalid HTML resulting in more predictable and desirable outcomes when handling complex HTML content. + +=== New list item was not inserted correctly when existing list item had a block element inside +// #TINY-12102 + +{productname} {release-version} resolves an issue affecting list management when parent list items contain block-level formatting. Previously, when a list item containing a sub-list was formatted as a block element (such as `

` or `

`), pressing Enter at the end of that item would incorrectly position the new list item alongside the sub-list items instead of as a sibling of the parent item. + +{productname} {release-version} addresses this by ensuring that when a user presses Enter at the end of a list item that contains block-level formatting, the new list item is correctly inserted as a sibling of the parent list item, rather than being placed incorrectly alongside the sub-list items. [[deprecated]] == Deprecated -{productname} {release-version} includes the following deprecation: +{productname} {release-version} includes the following deprecations: + +=== Java application server-based server components have been deprecated +// #DOC-3231 + +{productname} {release-version} deprecates the Java application server-based deployment method for self-hosted services. Docker containers are now the **only** supported deployment method for self-hosted services. The Java `.war` files (`ephox-spelling.war`, `ephox-image-proxy.war`, `ephox-hyperlinking.war`) will be removed in a future release. + +For information about setting up containerized server-side components using Docker containers, see: xref:bundle-intro-setup.adoc[Introduction and initial setup for containerized server-side services from the premium self-hosted bundles] + +=== Docker container services + +* Enhanced Media Embed and Link Checker service, see: xref:individual-hyperlinking-container.adoc[Deploy the {productname} hyperlinking server-side component using Docker]. +* Image Editing and Export image proxy service, see: xref:individual-image-proxy-container.adoc[Deploy the {productname} image proxy server-side component using Docker]. +* Spell Checker service, see: xref:individual-spelling-container.adoc[Deploy the {productname} spelling service server-side component using Docker]. +* Export to PDF service, see: xref:individual-export-to-pdf-on-premises.adoc[Deploy the {productname} Export to PDF service server-side component using Docker]. +* Import from Word and Export to Word services, see: xref:individual-import-from-word-and-export-to-word-on-premises.adoc[Deploy the {productname} Import from Word and Export to Word service server-side component using Docker]. + +=== The `ToggleToolbarDrawer` command's `skipFocus` option has been deprecated +// #TINY-12044 + +The `ToggleToolbarDrawer` command previously accepted a non-standard `skipFocus` option to prevent the editor from receiving focus when the command was executed. This behavior differed from other editor commands, which use the generic `skip_focus` option for the same purpose. As a result, the inconsistency lead to confusion when implementing or maintaining command logic. + +To resolve this, support for the standard `skip_focus` option has been added to the `ToggleToolbarDrawer` command. The `skipFocus` option is now **deprecated** in {productname} {release-version} and will be removed in a future release. + +.Before deprecation using the `skipFocus` option +[source] +---- +editor.execCommand('ToggleToolbarDrawer', false, { skipFocus: true }); +---- + +.After deprecation using the `skip_focus` option +[source] +---- +editor.execCommand('ToggleToolbarDrawer', false, null, { skip_focus: true }); +---- + +This change improves consistency across commands and aligns with expected usage patterns in {productname} {release-version}. + +=== Deprecated `fire()` method for event handling +// #TINY-12012 -=== The `` configuration property, ``, has been deprecated +The `fire()` method was originally deprecated in {productname} 6 in favor of the `dispatch()` method for event handling, but was never removed from the codebase. {productname} {release-version} now displays a deprecation warning when the `fire()` method is used, serving as a final notice before its removal in {productname} 9. -// placeholder here. +This long-overdue cleanup aligns the API with modern event handling conventions and removes confusion around method naming. Integrators should migrate to the `dispatch()` method to ensure compatibility with future releases. +.Deprecated approach for dispatching custom events +[source,javascript] +---- +// Deprecated in TinyMCE 8, will be removed in 9 +editor.fire('someEvent'); +---- -[[known-issues]] -== Known issues +.New approach for dispatching custom events +[source,javascript] +---- +// Use dispatch() instead +editor.dispatch('someEvent'); +---- -This section describes issues that users of {productname} {release-version} may encounter and possible workarounds for these issues. +For more information on the `dispatch()` method, see: xref:apis/tinymce.util.eventdispatcher.adoc[EventDispatcher]. -There known issue in {productname} {release-version}. +=== Deprecated `editor.selection.setContent` API +// TINY-11692 -=== -// #TINY-vwxyz1 +The `editor.selection.setContent` method has been deprecated in {productname} {release-version}. This low-level API was never intended for external use and does not guarantee consistent behavior when inserting complex content structures, such as lists. Integrators are advised to use the supported `editor.insertContent` method for reliable content insertion. -// CCFR here. \ No newline at end of file +This deprecation helps reduce confusion and aligns content insertion with {productname}'s supported APIs. diff --git a/modules/ROOT/pages/a11ychecker.adoc b/modules/ROOT/pages/a11ychecker.adoc index 240c08ac70..6abb3f1fda 100644 --- a/modules/ROOT/pages/a11ychecker.adoc +++ b/modules/ROOT/pages/a11ychecker.adoc @@ -46,300 +46,59 @@ Each rule has a severity level, which will be one of the following, listed in or * Warning * Error -[[D1]] -=== D1 - Usage of paragraphs as headings +[[document-structure-rules]] +=== Document structure rules -*Rule description:* this rule checks that `+h1+`-`+h6+` tags are used for heading content, not `+p+` tags. Not using correct heading markup will make it difficult for assistive technologies to represent and navigate through your content. +include::partial$a11y-rules/d1.adoc[] -==== {pluginname} rule details - D1 +include::partial$a11y-rules/d2.adoc[] -Notification level (severity):: Warning +include::partial$a11y-rules/d3.adoc[] -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA +include::partial$a11y-rules/d4o.adoc[] -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 +include::partial$a11y-rules/d4u.adoc[] -WCAG 2.1 specifications:: +include::partial$a11y-rules/d5.adoc[] -* https://www.w3.org/WAI/WCAG21/Techniques/html/H42.html[H42 - Using h1-h6 to identify headings]. -* https://www.w3.org/WAI/WCAG21/Techniques/html/H69.html[H69 - Providing heading elements at the beginning of each section of content]. +''' -[[D2]] -=== D2 - Sequential headings +[[html-rules]] +=== HTML rules -*Rule description:* this rule checks that headings tags are used sequentially. +include::partial$a11y-rules/h93.adoc[] -For example: A `+h1+` heading should be followed by a `+h2+` heading, not a `+h3+` heading. Using sequential headings will make it easier for assistive technology to parse your content. +''' -==== {pluginname} rule details - D2 +[[image-rules]] +=== Image rules -Notification level (severity):: Error +include::partial$a11y-rules/i1.adoc[] -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA +include::partial$a11y-rules/i2.adoc[] -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 +include::partial$a11y-rules/i3.adoc[] -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/general/G141.html[G141 - Organizing a page using headings]. +include::partial$a11y-rules/i4.adoc[] -[[D3]] -=== D3 - Adjacent links +''' -*Rule description:* this rule checks that links next to other links do not have the same `+href+` attribute. +[[table-rules]] +=== Table rules -For example: If an image link and a text link have the same `+href+` attribute, both elements should be in the same `+a+` element. If an image link and a text link point to the same URL but are two separate elements, it can be confusing for users of screen readers and other assistive technologies. +include::partial$a11y-rules/t1.adoc[] -==== {pluginname} rule details - D3 +include::partial$a11y-rules/t2.adoc[] -Notification level (severity):: Error +include::partial$a11y-rules/t3.adoc[] -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA +include::partial$a11y-rules/t4a.adoc[] -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 +include::partial$a11y-rules/t4b.adoc[] -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/html/H2.html[H2 - Combining adjacent image and text links for the same resource]. +include::partial$a11y-rules/t4c.adoc[] -[[D4O]] -=== D4O - Ordered list structure - -*Rule description:* this rule checks that an `+ol+` element is used for ordered lists. Do not use paragraphs beginning with numbers or roman numerals instead of an `+ol+` element containing `+li+` items. This is to simplify navigation and parsing of the content for users of assistive technologies. - -==== {pluginname} rule details - D4O - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/html/H48.html[H48 - Using ol, ul and dl for lists or groups of links]. - -[[D4U]] -=== D4U - Unordered list structure - -*Rule description:* this rule checks that a `+ul+` element is used for unordered lists. Do not use paragraphs beginning with `+*+` or `+-+` or some similar character instead of an `+ol+` element containing `+li+` items. This is to simplify navigation and parsing of the content for users of assistive technologies. - -==== {pluginname} rule details - D4U - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/html/H48.html[H48 - Using ol, ul and dl for lists or groups of links]. - -[[D5]] -=== D5 - Contrast ratio of the text (D5A, D5B, and D5C) - -*Rule description:* this rule checks that the contrast ratio of the text is above the following values: - -* When the compliance level is set to AA, -** 4.5:1 for normal text -** 3:1 for large text -* When the compliance level is set to AAA, -** 7:1 for any text - -Text with a low contrast ratio is hard to read for users with impaired vision. - -[[D5A]] -==== {pluginname} rule details - D5A - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level AA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/general/G145.html[G145 - Ensuring that a contrast ratio of at least 3:1 exists between text (and images of text) and background behind the text]. - -[[D5B]] -==== {pluginname} rule details - D5B - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level AA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/general/G18.html[G18 - Ensuring that a contrast ratio of at least 4.5:1 exists between text (and images of text) and background behind the text]. - -[[D5C]] -==== {pluginname} rule details - D5C - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/general/G17.html[G17 - Ensuring that a contrast ratio of at least 7:1 exists between text (and images of text) and background behind the text]. - -[[H93]] -=== H93 - IDs must be unique - -*Rule description:* this rule checks that all `+id+` attributes are unique in the editor. Duplicate `+id+` attributes are known to cause problems for assistive technologies when parsing the content. - -==== {pluginname} rule details - H93 - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/html/H93.html[H93 - Ensuring that id attributes are unique on a Web page]. - -[[I1]] -=== I1 - Image `+alt+` text - -*Rule description:* this rule checks that all images have alternative (`+alt+`) text for screen readers and other assistive technologies. - -==== {pluginname} rule details - I1 - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/general/G95.html[G95 - Providing short text alternatives that provide a brief description of the non-text content]. - -include::partial$misc/admon-accessibility-rule-i3-can-also-be-applied.adoc[] - -[[I2]] -=== I2 - Image `+alt+` text is not the image filename - -*Rule description:* this rule checks that the `+alt+` attribute text of the image is not the same as the filename of the image. - -==== {pluginname} rule details - I2 - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/general/G95.html[G95 - Providing short text alternatives that provide a brief description of the non-text content]. - -include::partial$misc/admon-accessibility-rule-i3-can-also-be-applied.adoc[] - -[[I3]] -=== I3 - Image `+alt+` text is not greater than 100 characters - -*Rule description:* this rule checks that the `+alt+` attribute text of the image is not more than 100 characters. - -An _Image alternative text should be less than 100 characters_ warning dialog presents if the alternative (`+alt+`) text is longer than 100 characters. This dialog also presents the alternative text in an editable field, for immediate repair. - -==== {pluginname} rule details - I3 - -Notification level (severity):: Warning - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: n/a. - - -[[T1]] -=== T1 - Table caption - -*Rule description:* this rule checks that all `+table+` elements have a `+caption+` element describing the data inside the table to simplify parsing and navigation of the content for users of assistive technologies. - -==== {pluginname} rule details - T1 - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/html/H39.html[H39 - Using caption elements to associate data table captions with data tables]. - -[[T2]] -=== T2 - Complex table summary - -*Rule description:* this rule checks that all complex tables must have a `+summary+` attribute explaining to users of assistive technologies how to navigate through the data inside of the table. - -[NOTE] -==== -This rule only applies to HTML 4 content and is not checked when `+a11ychecker_html_version+` is set to `+html5+`. -==== - -==== {pluginname} rule details - T2 - -Notification level (severity):: Warning - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/html/H73.html[H73 - Using the summary attribute of the table element to give an overview of data tables]. - -[[T3]] -=== T3 - Table caption and summary - -*Rule description:* this rule checks that the table caption and summary does not have the same text content. The caption should explain *what* the table is about while the summary should explain *how* to navigate the data inside of the table. - -[NOTE] -==== -The table `+summary+` attribute was deprecated in HTML 5, both the *what* and *how* information should be moved to the table caption. -==== - -==== {pluginname} rule details - T3 - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/html/H73.html[H73 - Using the summary attribute of the table element to give an overview of data tables]. - -[[T4A]] -=== T4A - Table markup - -*Rule description:* this rule checks that all `+tables+` contain both `+tr+` and `+td+` elements. - -==== {pluginname} rule details - T4A - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/html/H51.html[H51 - Using table markup to present tabular information]. - -[[T4B]] -=== T4B - Table headers - -*Rule description:* this rule checks that all `+table+` elements contain at least one table header (`+th+`) element. - -==== {pluginname} rule details - T4B - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/html/H51.html[H51 - Using table markup to present tabular information]. - -[[T4C]] -=== T4C - Table heading scope - -*Rule description:* this rule checks that all table header (`+th+`) elements have a `+scope+` attribute clarifying what scope the heading has inside of the `+table+`. The allowed values are `+row+`, `+col+`, `+rowgroup+`, and `+colgroup+`. This is important for users of assistive technologies to be able to parse table data. - -==== {pluginname} rule details - T4C - -Notification level (severity):: Error - -https://www.w3.org/TR/WCAG21/#conformance[WCAG level]:: Level A ; Level AA ; Level AAA - -xref:a11ychecker_html_version[HTML version]:: HTML4 and HTML5 - -WCAG 2.1 specification:: https://www.w3.org/WAI/WCAG21/Techniques/html/H63.html[H63 - Using the scope attribute to associate header cells and data cells in data tables]. +''' == Options @@ -347,6 +106,8 @@ The following configuration options affect the behavior of the {pluginname} plug include::partial$configuration/a11y_advanced_options.adoc[leveloffset=+1] +include::partial$configuration/a11ychecker_alt_text_max_length.adoc[leveloffset=+1] + include::partial$configuration/a11ychecker_allow_decorative_images.adoc[leveloffset=+1] include::partial$configuration/a11ychecker_filter_issue.adoc[leveloffset=+1] diff --git a/modules/ROOT/pages/apis/tinymce.dom.domutils.adoc b/modules/ROOT/pages/apis/tinymce.dom.domutils.adoc index f524798708..4dff25f656 100644 --- a/modules/ROOT/pages/apis/tinymce.dom.domutils.adoc +++ b/modules/ROOT/pages/apis/tinymce.dom.domutils.adoc @@ -269,7 +269,7 @@ Creates HTML string for element. The element will be closed unless an empty inne [source, javascript] ---- // Creates a html chunk and inserts it at the current selection/caret location -tinymce.activeEditor.selection.setContent(tinymce.activeEditor.dom.createHTML('a', { href: 'test.html' }, 'some line')); +tinymce.activeEditor.insertContent(tinymce.activeEditor.dom.createHTML('a', { href: 'test.html' }, 'some line')); ---- ==== Parameters diff --git a/modules/ROOT/pages/apis/tinymce.dom.selection.adoc b/modules/ROOT/pages/apis/tinymce.dom.selection.adoc index 575cb1dfdb..2a2c7cd0a3 100644 --- a/modules/ROOT/pages/apis/tinymce.dom.selection.adoc +++ b/modules/ROOT/pages/apis/tinymce.dom.selection.adoc @@ -48,7 +48,9 @@ node the parent element will be returned.|`xref:apis/tinymce.dom.selection.adoc[ |xref:#select[select()]|Selects the specified element. This will place the start and end of the selection range around the element.|`xref:apis/tinymce.dom.selection.adoc[Selection]` |xref:#selectorChanged[selectorChanged()]|Executes callback when the current selection starts/stops matching the specified selector. The current state will be passed to the callback as it's first argument.|`xref:apis/tinymce.dom.selection.adoc[Selection]` -|xref:#setContent[setContent()]|Sets the current selection to the specified content. If any contents is selected it will be replaced +|xref:#setContent[setContent()]|This method has been deprecated. Use "editor.insertContent" instead. + +Sets the current selection to the specified content. If any contents is selected it will be replaced with the contents passed in to this function. If there is no selection the contents will be inserted where the caret is placed in the editor/page.|`xref:apis/tinymce.dom.selection.adoc[Selection]` |xref:#setCursorLocation[setCursorLocation()]|Move the selection cursor range to the specified node and offset. @@ -356,6 +358,8 @@ state will be passed to the callback as it's first argument. ---- setContent(content: String, args: Object) ---- +This method has been deprecated. Use "editor.insertContent" instead. + Sets the current selection to the specified content. If any contents is selected it will be replaced with the contents passed in to this function. If there is no selection the contents will be inserted where the caret is placed in the editor/page. diff --git a/modules/ROOT/pages/apis/tinymce.editor.adoc b/modules/ROOT/pages/apis/tinymce.editor.adoc index 0aa6ddec6f..3d8f8a5cc2 100644 --- a/modules/ROOT/pages/apis/tinymce.editor.adoc +++ b/modules/ROOT/pages/apis/tinymce.editor.adoc @@ -1,7 +1,7 @@ = tinymce.Editor :navtitle: tinymce.Editor :description: This class contains the core logic for a TinyMCE editor. -:keywords: Editor, addCommand, addCommandCallback, addQueryStateHandler, addQueryStateHandlerCallback, addQueryValueHandler, addQueryValueHandlerCallback, addShortcut, addVisual, baseURI, contentCSS, contentStyles, convertURL, destroy, dispatch, documentBaseURI, dom, editorUpload, execCommand, fire, focus, formatter, getBody, getContainer, getContent, getContentAreaContainer, getDoc, getElement, getParam, getWin, hasEditableRoot, hasEventListeners, hasFocus, hasPlugin, hide, id, initialized, insertContent, isDirty, isHidden, load, mode, nodeChanged, notificationManager, off, on, once, options, parser, plugins, queryCommandState, queryCommandSupported, queryCommandValue, remove, render, resetContent, save, schema, selection, serializer, setContent, setDirty, setEditableRoot, setProgressState, show, theme, translate, ui, undoManager, uploadImages, windowManager +:keywords: Editor, addCommand, addCommandCallback, addQueryStateHandler, addQueryStateHandlerCallback, addQueryValueHandler, addQueryValueHandlerCallback, addShortcut, addVisual, baseURI, contentCSS, contentStyles, convertURL, destroy, dispatch, documentBaseURI, dom, editorUid, editorUpload, execCommand, fire, focus, formatter, getBody, getContainer, getContent, getContentAreaContainer, getDoc, getElement, getParam, getWin, hasEditableRoot, hasEventListeners, hasFocus, hasPlugin, hide, id, initialized, insertContent, isDirty, isHidden, load, mode, nodeChanged, notificationManager, off, on, once, options, parser, plugins, queryCommandState, queryCommandSupported, queryCommandValue, remove, render, resetContent, save, schema, selection, serializer, setContent, setDirty, setEditableRoot, setProgressState, show, theme, translate, ui, undoManager, uploadImages, userLookup, windowManager :moxie-type: api This class contains the core logic for a TinyMCE editor. @@ -37,6 +37,7 @@ ed.render(); |contentStyles|`Array`|Array of CSS styles to add to head of document when the editor loads.|`xref:apis/tinymce.editor.adoc[Editor]` |documentBaseURI|`xref:apis/tinymce.util.uri.adoc[URI]`|URI object to document configured for the TinyMCE instance.|`xref:apis/tinymce.editor.adoc[Editor]` |dom|`xref:apis/tinymce.dom.domutils.adoc[DOMUtils]`|DOM instance for the editor.|`xref:apis/tinymce.editor.adoc[Editor]` +|editorUid|`String`|A uuid string to uniquely identify an editor across any page.|`xref:apis/tinymce.editor.adoc[Editor]` |editorUpload|`xref:apis/tinymce.editorupload.adoc[EditorUpload]`|Editor upload API|`xref:apis/tinymce.editor.adoc[Editor]` |formatter|`xref:apis/tinymce.formatter.adoc[Formatter]`|Formatter instance.|`xref:apis/tinymce.editor.adoc[Editor]` |id|`String`|Editor instance id, normally the same as the div/textarea that was replaced.|`xref:apis/tinymce.editor.adoc[Editor]` @@ -52,6 +53,7 @@ ed.render(); |theme|`xref:apis/tinymce.theme.adoc[Theme]`|Reference to the theme instance that was used to generate the UI.|`xref:apis/tinymce.editor.adoc[Editor]` |ui|`xref:apis/tinymce.editor.ui.ui.adoc[Ui]`|Editor ui components|`xref:apis/tinymce.editor.adoc[Editor]` |undoManager|`xref:apis/tinymce.undomanager.adoc[UndoManager]`|Undo manager instance, responsible for handling undo levels.|`xref:apis/tinymce.editor.adoc[Editor]` +|userLookup|`xref:apis/tinymce.userlookup.adoc[UserLookup]`|Editor user lookup API|`xref:apis/tinymce.editor.adoc[Editor]` |windowManager|`xref:apis/tinymce.windowmanager.adoc[WindowManager]`|Window manager reference, use this to open new windows and dialogs.|`xref:apis/tinymce.editor.adoc[Editor]` |=== @@ -723,7 +725,7 @@ Returns true/false if the editor is hidden or not. === load() [source, javascript] ---- -load(args: Object): String +load(args: Object) ---- Loads contents from the textarea, input or other element that got converted into an editor instance. This method will move the contents from that textarea, input or other element into the editor by using setContent @@ -733,10 +735,6 @@ so all events etc that method has will get dispatched as well. * `args (Object)` - Optional content object, this gets passed around through the whole load process. -==== Return value - -* `String` - HTML string that got set into the editor. - ''' [[nodeChanged]] @@ -949,7 +947,7 @@ so all events etc that method has will get dispatched as well. === setContent() [source, javascript] ---- -setContent(content: String, args: Object): String +setContent(content: String, args: Object) ---- Sets the specified content to the editor instance, this will cleanup the content before it gets set using the different cleanup rules options. @@ -975,10 +973,6 @@ tinymce.activeEditor.setContent('

Some html

', { format: 'html' }); * `content (String)` - Content to set to editor, normally HTML contents but can be other formats as well. * `args (Object)` - Optional content object, this gets passed around through the whole set process. -==== Return value - -* `String` - HTML string that got set into the editor. - ''' [[setDirty]] diff --git a/modules/ROOT/pages/apis/tinymce.root.adoc b/modules/ROOT/pages/apis/tinymce.root.adoc index 65c46cc4c4..60db0f8dd3 100644 --- a/modules/ROOT/pages/apis/tinymce.root.adoc +++ b/modules/ROOT/pages/apis/tinymce.root.adoc @@ -1,7 +1,7 @@ = tinymce :navtitle: tinymce :description: TinyMCE core class. -:keywords: DOM, PluginManager, ScriptLoader, ThemeManager, activeEditor, add, addI18n, baseURI, baseURL, createEditor, defaultOptions, documentBaseURL, each, execCommand, explode, get, grep, hasOwnProperty, i18n, inArray, init, is, isArray, majorVersion, makeMap, map, minorVersion, overrideDefaults, releaseDate, remove, resolve, setActive, suffix, toArray, translate, triggerSave, trim, walk +:keywords: DOM, PluginManager, ScriptLoader, ThemeManager, activeEditor, add, addI18n, baseURI, baseURL, createEditor, defaultOptions, documentBaseURL, each, execCommand, explode, get, grep, hasOwnProperty, i18n, inArray, init, is, isArray, majorVersion, makeMap, map, minorVersion, overrideDefaults, pageUid, releaseDate, remove, resolve, setActive, suffix, toArray, translate, triggerSave, trim, walk :moxie-type: api TinyMCE core class. @@ -26,6 +26,7 @@ TinyMCE core class. |i18n|`Object`|Collection of language pack data.|`xref:apis/tinymce.root.adoc[tinymce]` |majorVersion|`String`|Major version of TinyMCE build.|`xref:apis/tinymce.root.adoc[tinymce]` |minorVersion|`String`|Minor version of TinyMCE build.|`xref:apis/tinymce.root.adoc[tinymce]` +|pageUid|`String`|A uuid string to anonymously identify the page tinymce is loaded in|`xref:apis/tinymce.root.adoc[tinymce]` |releaseDate|`String`|Release date of TinyMCE build.|`xref:apis/tinymce.root.adoc[tinymce]` |suffix|`String`|Current suffix to add to each plugin/theme that gets loaded for example ".min".|`xref:apis/tinymce.root.adoc[tinymce]` |=== diff --git a/modules/ROOT/pages/apis/tinymce.userlookup.adoc b/modules/ROOT/pages/apis/tinymce.userlookup.adoc new file mode 100644 index 0000000000..02890d9ddb --- /dev/null +++ b/modules/ROOT/pages/apis/tinymce.userlookup.adoc @@ -0,0 +1,69 @@ += tinymce.UserLookup +:navtitle: tinymce.UserLookup +:description: TinyMCE User Lookup API Handles user information retrieval and caching. +:keywords: fetchUsers, userId +:moxie-type: api + +TinyMCE User Lookup API Handles user information retrieval and caching. + +[[examples]] +== Examples +[source, javascript] +---- +// Get the current user's ID from the editor options, or defaults to 'Anonymous'. +tinymce.activeEditor.userLookup.userId; + +// Fetch user information by IDs which returns a record of promises +const userPromises = tinymce.activeEditor.userLookup.fetchUsers(['user-1', 'user-2']); + +// Access individual promises by user ID +userPromises['user-1'].then(user => console.log('User 1:', user)); +userPromises['user-2'].then(user => console.log('User 2:', user)); + +// Or wait for all promises +Promise.all(Object.values(userPromises)).then((users) => { + users.forEach(user => console.log('User found:', user)); +}).catch((error) => { + console.error('Error fetching users:', error); +}); +---- + +[[summary]] +== Summary + +[[properties]] +=== Properties +[cols="2,1,4,1",options="header"] +|=== +|Name|Type|Summary|Defined by +|userId|`String`|The current user's ID retrieved from the editor options, or defaults to 'Anonymous'.|`xref:apis/tinymce.userlookup.adoc[UserLookup]` +|=== + +[[methods-summary]] +=== Methods +[cols="2,5,1",options="header"] +|=== +|Name|Summary|Defined by +|xref:#fetchUsers[fetchUsers()]|Fetches user information using a provided array of userIds.|`xref:apis/tinymce.userlookup.adoc[UserLookup]` +|=== + +[[methods]] +== Methods + +[[fetchUsers]] +=== fetchUsers() +[source, javascript] +---- +fetchUsers(userIds: string[]): Record> +---- +Fetches user information using a provided array of userIds. + +==== Parameters + +* `userIds (string[])` - - A list of user IDs to fetch information for. + +==== Return value + +* `Record>` - An object where each key is a user ID and its value is a Promise that resolves to the user's data or rejects if the user is not found. + +''' diff --git a/modules/ROOT/pages/available-toolbar-buttons.adoc b/modules/ROOT/pages/available-toolbar-buttons.adoc index aeb089d9cd..0a95913626 100644 --- a/modules/ROOT/pages/available-toolbar-buttons.adoc +++ b/modules/ROOT/pages/available-toolbar-buttons.adoc @@ -239,6 +239,13 @@ include::partial$misc/plugin-toolbar-button-id-boilerplate.adoc[] include::partial$misc/plugin-toolbar-button-id-boilerplate.adoc[] :!pluginpage: +:plugincategory: premium +:pluginname: Suggested Edits +:plugincode: suggestededits +:pluginpage: suggestededits.adoc +include::partial$misc/plugin-toolbar-button-id-boilerplate.adoc[] +:!pluginpage: + :plugincategory: opensource :pluginname: Table :plugincode: table diff --git a/modules/ROOT/pages/basic-setup.adoc b/modules/ROOT/pages/basic-setup.adoc index 6072eefccb..5f11695e13 100644 --- a/modules/ROOT/pages/basic-setup.adoc +++ b/modules/ROOT/pages/basic-setup.adoc @@ -198,7 +198,8 @@ The following example is a basic {productname} configuration. + ---- In this case, the editor will be in a **read-only** state, and a notification appears stating: **"A valid API key is required to continue using {productname}. Please alert the admin to check the current API key. Click here to learn more."** @@ -47,7 +47,7 @@ For example: if your API is `+abcd1234+`: + [source,html,subs="attributes+"] ---- - + ---- + . Check the `+apiKey+` provided in the script tag: @@ -82,7 +82,7 @@ For example: if your API is `+abcd1234+`: [source,html,subs="attributes+"] ---- - + ---- To retrieve your API key, or to sign up for an API key, visit: link:{accountsignup}/[{cloudname}]. diff --git a/modules/ROOT/pages/comments-callback-mode.adoc b/modules/ROOT/pages/comments-callback-mode.adoc index 85275ec9fc..a5725f33bd 100644 --- a/modules/ROOT/pages/comments-callback-mode.adoc +++ b/modules/ROOT/pages/comments-callback-mode.adoc @@ -67,6 +67,10 @@ include::partial$configuration/tinycomments_resolve.adoc[leveloffset=+1] include::partial$configuration/tinycomments_fetch.adoc[leveloffset=+1] +include::partial$configuration/user_id.adoc[leveloffset=+1] + +include::partial$configuration/fetch_users.adoc[leveloffset=+1] + include::partial$configuration/tinycomments_fetch_author_info.adoc[leveloffset=+1] include::partial$plugins/comments-open-sidebar.adoc[] diff --git a/modules/ROOT/pages/comments-embedded-mode.adoc b/modules/ROOT/pages/comments-embedded-mode.adoc index f0f8c1fa6f..280630f5bf 100644 --- a/modules/ROOT/pages/comments-embedded-mode.adoc +++ b/modules/ROOT/pages/comments-embedded-mode.adoc @@ -11,17 +11,33 @@ To add the {pluginname} plugin in embedded mode to the {productname}, configure [source,js] ---- +const userDb = { + 'author': { + id: 'author', + name: 'Name of the commenter', + avatar: 'https://example.com/avatar/john.png' + }, +}; + tinymce.init({ selector: 'textarea', // change this value according to your html plugins: 'tinycomments', toolbar: 'addcomment showcomments', - tinycomments_author: 'author', - tinycomments_author_name: 'Name of the commenter', + user_id: 'author', + fetch_users: (userIds) => { + return Promise.all( + userIds.map( + (userId) => new Promise( + (resolve) => resolve(userDb[userId] || { id: userId }) + ) + ) + ) + }, tinycomments_mode: 'embedded' }); ---- -This is the minimum recommended setup for the {pluginname} plugin in embedded mode. If the `+tinycomments_author+` and `+tinycomments_author_name+` options are not configured, all users will be assigned the name "_ANON_". +This is the minimum recommended setup for the {pluginname} plugin in embedded mode. If the `+user_id+` or `+tinycomments_author+` options are not configured, all users will be assigned the name "_ANON_". [[comments-embedded-live-demo]] == Interactive example @@ -30,6 +46,10 @@ liveDemo::comments-embedded[] == Options +include::partial$configuration/user_id.adoc[leveloffset=+1] + +include::partial$configuration/fetch_users.adoc[leveloffset=+1] + include::partial$configuration/tinycomments_author.adoc[leveloffset=+1] include::partial$configuration/tinycomments_author_avatar.adoc[leveloffset=+1] diff --git a/modules/ROOT/pages/comments-with-mentions.adoc b/modules/ROOT/pages/comments-with-mentions.adoc index 950c59350b..f648a0ddea 100644 --- a/modules/ROOT/pages/comments-with-mentions.adoc +++ b/modules/ROOT/pages/comments-with-mentions.adoc @@ -64,9 +64,18 @@ include::partial$configuration/tinycomments_lookup.adoc[leveloffset=+1] When using embedded mode, the {pluginname} plugin requires the following options: +* xref:user_id[`+user_id+`] +* xref:fetch_users[`+fetch_users+`] + +Or: + * xref:tinycomments_author[`+tinycomments_author+`] * xref:tinycomments_author_name[`+tinycomments_author_name+`] +include::partial$configuration/user_id.adoc[leveloffset=+1] + +include::partial$configuration/fetch_users.adoc[leveloffset=+1] + include::partial$configuration/tinycomments_author.adoc[leveloffset=+1] include::partial$configuration/tinycomments_author_name.adoc[leveloffset=+1] diff --git a/modules/ROOT/pages/configure-logging-services.adoc b/modules/ROOT/pages/configure-logging-services.adoc deleted file mode 100644 index 2722f9807e..0000000000 --- a/modules/ROOT/pages/configure-logging-services.adoc +++ /dev/null @@ -1,45 +0,0 @@ -= Activity logging -:description: Setting up logging for the premium server-side components. - -It may be useful to make the {productname} server-side components write to their own log file. This can assist in troubleshooting and make it easier to provide logs as part of a support ticket. - -The {productname} server-side components use the http://logback.qos.ch/manual/configuration.html[Logback] logging format. - -To write the logs to a specific file: - -. Create a logging configuration XML file. Save the snippet below as `+logback.xml+` after replacing `+{$LOG_LOCATION}+` with the full path to the destination log file (e.g. /var/log/tinymce_server_components.log). -+ -[source,xml,subs="attributes+"] ----- - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger\{36} - %msg%n - - - - - {$LOG_LOCATION} - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger\{36} - %msg%n - - - - - - - - - - - - - ----- -. Pass the configuration file to the Java application server. Assuming you've saved your `+logback.xml+` file in `+/etc/opt/tinymce+`, follow xref:introduction-to-premium-selfhosted-services.adoc#pass-the-configuration-file-to-the-java-application-server[step 4] and xref:introduction-to-premium-selfhosted-services.adoc#restart-the-java-application-server[step 5] on the _Install Server-side Components_ page to set the following JVM system property on your Java application server: -+ -.... --Dlogback.configurationFile=/etc/opt/tinymce/logback.xml -.... diff --git a/modules/ROOT/pages/content-filtering.adoc b/modules/ROOT/pages/content-filtering.adoc index 2a5896970a..e65bc480a3 100644 --- a/modules/ROOT/pages/content-filtering.adoc +++ b/modules/ROOT/pages/content-filtering.adoc @@ -5,6 +5,8 @@ include::partial$configuration/allow_conditional_comments.adoc[] +include::partial$configuration/allow_html_in_comments.adoc[] + include::partial$configuration/allow_html_in_named_anchor.adoc[] include::partial$configuration/allow_mathml_annotation_encodings.adoc[] diff --git a/modules/ROOT/pages/contextform.adoc b/modules/ROOT/pages/contextform.adoc index c0fa210bb6..5d4dbd571b 100644 --- a/modules/ROOT/pages/contextform.adoc +++ b/modules/ROOT/pages/contextform.adoc @@ -4,7 +4,7 @@ :keywords: contextforms, context forms, contextformsbarapi :contextItemType: form -A context form consists of an input field, and a series of related buttons. Context forms can be shown wherever a context toolbar can be shown. Also, when a context form is registered containing a `+launch+` configuration, a special context toolbar button with name `+form:${name}+` is registered which will launch that particular context form. Context forms are a generalization of the `+Insert Link+` form that existed in the original link:https://www.tiny.cloud/docs-4x/themes/inlite/#quicklink[inlite] theme from TinyMCE 4. +A context form consists of an input field, and a series of related buttons. Context forms can be shown wherever a context toolbar can be shown. Also, when a context form is registered containing a `+launch+` configuration, a special context toolbar button with name `+form:${name}+` is registered which will launch that particular context form. Context forms are a generalization of the `+Insert Link+` form that existed in the original link:https://www.tiny.cloud/docs-4x/themes/inlite/#quicklink[inlite] theme from {productname} 4. == Registering a context form diff --git a/modules/ROOT/pages/creating-a-plugin.adoc b/modules/ROOT/pages/creating-a-plugin.adoc index a738b15d4c..82df704005 100644 --- a/modules/ROOT/pages/creating-a-plugin.adoc +++ b/modules/ROOT/pages/creating-a-plugin.adoc @@ -1,4 +1,4 @@ -= Create a plugin for TinyMCE += Create a plugin for {productname} :navtitle: Create a plugin :description_short: Introducing plugin creation, with an example. :description: A short introduction to creating plugins for TinyMCE along with an example plugin. @@ -20,7 +20,7 @@ The plugin identifier passed to `+PluginManager.add()+` is used by {productname} * Be an alphanumeric string, * Not contain any spaces or other whitespaces, -* Be a unique identifier that does not match the IDs of plugins provided with TinyMCE or any other TinyMCE plugin in use. +* Be a unique identifier that does not match the IDs of plugins provided with {productname} or any other {productname} plugin in use. If multiple plugins have the same identifier, one will override the others. diff --git a/modules/ROOT/pages/creating-a-skin.adoc b/modules/ROOT/pages/creating-a-skin.adoc index 5ff800439f..19f20e2fbe 100644 --- a/modules/ROOT/pages/creating-a-skin.adoc +++ b/modules/ROOT/pages/creating-a-skin.adoc @@ -67,6 +67,8 @@ _Do not edit these files directly_. Instead, use them as references when creatin * `+modules/oxide/src/less/theme/components/toolbar-button/toolbar-button.less+` The typical workflow involves copying variables from the theme folder into the skins `skin.less` file and modifying them as needed. + +NOTE: Split buttons have undergone structural changes in {productname} {productmajorversion}. Previously they used a single component structure, but now use separate components for the main button and chevron areas. See xref:migration-from-7x.adoc#split-button-css-breaking-change[split button css breaking change] ==== [NOTE] diff --git a/modules/ROOT/pages/custom-basic-menu-items.adoc b/modules/ROOT/pages/custom-basic-menu-items.adoc index e31ef12f80..17c5d8cfa8 100644 --- a/modules/ROOT/pages/custom-basic-menu-items.adoc +++ b/modules/ROOT/pages/custom-basic-menu-items.adoc @@ -17,7 +17,7 @@ include::partial$misc/admon-predefined-icons-only.adoc[] |enabled |boolean |optional |default: `true` - Represents the menu item's state. When `false`, the menu item is unclickable. Toggled by the menu item's API. |onSetup |`+(api) => (api) => void+` |optional |default: `+() => () => {}+` - Function invoked when the menu item is rendered, each time its menu is opened. For details, see: xref:using-onsetup[Using `+onSetup+`]. |onAction |`+(api) => void+` |required |Function invoked when the menu item is clicked. -|shortcut |string |optional |Text to display in the shortcut label. To register a shortcut, see: xref:shortcuts.adoc[Add custom shortcuts to TinyMCE]. +|shortcut |string |optional |Text to display in the shortcut label. To register a shortcut, see: xref:shortcuts.adoc[Add custom shortcuts to {productname}]. 4+| |context |string |optional |default: `mode:design` - The context property dynamically enables or disables the menu item based on the editor's current state. For details, see: xref:context.adoc[Context]. |=== diff --git a/modules/ROOT/pages/custom-basic-toolbar-button.adoc b/modules/ROOT/pages/custom-basic-toolbar-button.adoc index 37ac782589..823d06eacf 100644 --- a/modules/ROOT/pages/custom-basic-toolbar-button.adoc +++ b/modules/ROOT/pages/custom-basic-toolbar-button.adoc @@ -18,7 +18,7 @@ include::partial$misc/admon-predefined-icons-only.adoc[] |onSetup |`+(api) => (api) => void+` |optional |default: `+() => () => {}+` - Function invoked when the button is rendered. For details, see: xref:using-onsetup[Using `+onSetup+`]. |onAction |`+(api) => void+` |required |Function invoked when the button is clicked. 4+| -|shortcut |string |optional |Shortcut to display in the tooltip. To register a shortcut, see: xref:shortcuts.adoc[Add custom shortcuts to TinyMCE]. +|shortcut |string |optional |Shortcut to display in the tooltip. To register a shortcut, see: xref:shortcuts.adoc[Add custom shortcuts to {productname}]. 4+| |context |string |optional |default: `mode:design` - The context property dynamically enables or disables the button based on the editor's current state. For details, see: xref:context.adoc[Context]. |=== diff --git a/modules/ROOT/pages/custom-dictionaries-for-tiny-spellchecker.adoc b/modules/ROOT/pages/custom-dictionaries-for-tiny-spellchecker.adoc index 1fe8d8cacc..65bb8f1e08 100644 --- a/modules/ROOT/pages/custom-dictionaries-for-tiny-spellchecker.adoc +++ b/modules/ROOT/pages/custom-dictionaries-for-tiny-spellchecker.adoc @@ -7,7 +7,7 @@ One custom dictionary can be created for each language already supported by the spell checker (see xref:introduction-to-tiny-spellchecker.adoc#supported-languages[supported languages]) or any arbitrary language added by additional Hunspell dictionary files included in Hunspell Dictionary Path (See xref:self-hosting-hunspell.adoc[Add Hunspell dictionaries to Spell Checker]). It's also possible to define an additional "global" dictionary that contains words that are valid across all languages, such as trademarks. -A custom dictionary file for a particular language must be named with the language code of the language (see xref:introduction-to-tiny-spellchecker.adoc#supported-languages[supported languages] for language code examples), plus the suffix `+.txt+`: E.g. `+en.txt+`, `+en_gb.txt+`, `+fr.txt+`, `+de.txt+` etc. +A custom dictionary file for a particular language must be named with the language code of the language (see xref:introduction-to-tiny-spellchecker.adoc#supported-languages[supported languages] for language code examples), plus the suffix `+.txt+`: E.g. `+en.txt+`, `+en-GB.txt+`, `+fr.txt+`, `+de.txt+` etc. The "global" dictionary file for language-independent words must be called "global.txt". diff --git a/modules/ROOT/pages/custom-nested-menu-items.adoc b/modules/ROOT/pages/custom-nested-menu-items.adoc index bd0a388225..d04f083732 100644 --- a/modules/ROOT/pages/custom-nested-menu-items.adoc +++ b/modules/ROOT/pages/custom-nested-menu-items.adoc @@ -14,7 +14,7 @@ include::partial$misc/admon-predefined-icons-only.adoc[] |value |string |optional |A value to associate with the menu item. |onSetup |`+(api) => (api) => void+` |optional |default: `+() => () => {}+` - Function invoked when the menu item is rendered, each time its menu is opened. For details, see: xref:using-onsetup[Using `+onSetup+`]. |getSubmenuItems |`+() => string+` or `+MenuItem[]+` |required |Function invoked when the menu item is clicked to open its submenu. Must return either a space separated string of registered menu names or an array of basic, toggle or nested menu items specifications. -|shortcut |string |optional |Text to display in the shortcut label. To register a shortcut, see: xref:shortcuts.adoc[Add custom shortcuts to TinyMCE]. +|shortcut |string |optional |Text to display in the shortcut label. To register a shortcut, see: xref:shortcuts.adoc[Add custom shortcuts to {productname}]. 4+| |context |string |optional |default: `mode:design` - The context property dynamically enables or disables the menu item based on the editor's current state. For details, see: xref:context.adoc[Context]. |=== diff --git a/modules/ROOT/pages/custom-split-toolbar-button.adoc b/modules/ROOT/pages/custom-split-toolbar-button.adoc index 63fafd4f00..c379abd2ba 100644 --- a/modules/ROOT/pages/custom-split-toolbar-button.adoc +++ b/modules/ROOT/pages/custom-split-toolbar-button.adoc @@ -13,6 +13,8 @@ A split button contains a basic button and a menu button, wrapped up into one to |text |string |Primary button |optional |Text displayed if no icon is found. |icon |string |Primary button |optional | include::partial$misc/admon-predefined-icons-only.adoc[] +|tooltip |string |Primary button |optional |Text displayed when hovering over the main button. +|chevronTooltip |string |Chevron button |optional |Text displayed when hovering over the chevron button. If not provided, it will be generated using the tooltip of the main button. ie: `+"{tooltip} menu"+`. |onAction |`+(api) => void+` |Primary button |required |Function invoked when the basic button section is clicked. |select |`+(value: string) => boolean+` |Choice menu items |optional |default: `false` - Function run on each option when the menu is opened to determine if it should be highlighted as active. |columns |number or `+'auto'+` |Drop-down menu |optional |default: `1` - Number of columns for the list of options. When set to more than 1 column, only the icon for each item will be shown. @@ -35,6 +37,7 @@ include::partial$misc/admon-predefined-icons-only.adoc[] 3+| |setText |`+(text: string) => void+` |Sets the text label to display. |setIcon |`+(icon: string) => void+` |Sets the icon of the button. +|setTooltip |`+(tooltip: string) => void+` |Sets the main button tooltip. |=== [[split-button-example-and-explanation]] diff --git a/modules/ROOT/pages/custom-toggle-toolbar-button.adoc b/modules/ROOT/pages/custom-toggle-toolbar-button.adoc index 4b95394a4f..46007bfc22 100644 --- a/modules/ROOT/pages/custom-toggle-toolbar-button.adoc +++ b/modules/ROOT/pages/custom-toggle-toolbar-button.adoc @@ -19,7 +19,7 @@ include::partial$misc/admon-predefined-icons-only.adoc[] |onSetup |`+(api) => (api) => void+` |optional |default: `+() => () => {}+` - Function invoked when the button is rendered. For details, see: xref:using-onsetup[Using `+onSetup+`]. |onAction |`+(api) => void+` |required |Function invoked when the button is clicked. 4+| -|shortcut |string |optional |Shortcut to display in the tooltip. To register a shortcut, see: xref:shortcuts.adoc[Add custom shortcuts to TinyMCE]. +|shortcut |string |optional |Shortcut to display in the tooltip. To register a shortcut, see: xref:shortcuts.adoc[Add custom shortcuts to {productname}]. 4+| |context |string |optional |default: `mode:design` - The context property dynamically enables or disables the button based on the editor's current state. For details, see: xref:context.adoc[Context]. |=== diff --git a/modules/ROOT/pages/django-cloud.adoc b/modules/ROOT/pages/django-cloud.adoc index 77ba5493cc..d04eab2c04 100644 --- a/modules/ROOT/pages/django-cloud.adoc +++ b/modules/ROOT/pages/django-cloud.adoc @@ -1,4 +1,4 @@ -= Using TinyMCE from the Tiny Cloud CDN with the Django framework += Using {productname} from the Tiny Cloud CDN with the Django framework :navtitle: Django :description: A guide on integrating TinyMCE from the Tiny Cloud into the Django framework. :keywords: integration, integrate, Django, django, django-tinymce, python diff --git a/modules/ROOT/pages/django-zip.adoc b/modules/ROOT/pages/django-zip.adoc index 0d510eb1dc..d4202bc85c 100644 --- a/modules/ROOT/pages/django-zip.adoc +++ b/modules/ROOT/pages/django-zip.adoc @@ -1,4 +1,4 @@ -= Using the TinyMCE .zip package with the Django framework += Using the {productname} .zip package with the Django framework :navtitle: Django :description: A guide on integrating a .zip version of TinyMCE into the Django framework. :keywords: integration, integrate, Django, django, django-tinymce, python diff --git a/modules/ROOT/pages/editor-and-features.adoc b/modules/ROOT/pages/editor-and-features.adoc index 96b67a2cac..8868ce3fe3 100644 --- a/modules/ROOT/pages/editor-and-features.adoc +++ b/modules/ROOT/pages/editor-and-features.adoc @@ -20,7 +20,7 @@ The following example adds a script tag into the application that inserts the co [source,html,subs="attributes+"] ---- - + ---- [cols="1,2,12",options="header"] @@ -28,6 +28,7 @@ The following example adds a script tag into the application that inserts the co | |Value |Description |1 |no-api-key |Replace with your api key |2 |origin |A `+referrerpolicy+` specifies how much of the current page's URL is sent in the `+Referer+` header when the browser fetches page resources (for example, the {productname} editor). The use of the `+Referer+` header ensures API keys are only used on domains registered to their owners. We only care about the domain portion however (similar to the operation of `+Origin+` header), so for improved performance and privacy always set the `+referrerpolicy+` to `+origin+` when requesting {cloudname} resources. +|3 |anonymous |the `+crossorigin="anonymous"+` attribute is required on all script tags loading {productname} from {cloudname}. This attribute ensures consistent Origin header behavior for license key validation and improves security for cross-origin script loading. |=== image::scripttag.png[Script Tag Description] @@ -79,13 +80,15 @@ Migrating from a self-hosted environment to {cloudname} is easy. Remove the exis NOTE: The script tag typically references `+tinymce.min.js+` hosted within the application or available at a legacy CDN. -Replace the script tag with the following: +Replace the script tag with the following, making sure to include both required attributes (`referrerpolicy` and `crossorigin`): [source,html,subs="attributes+"] ---- - + ---- +NOTE: When migrating to {cloudname}, both the `referrerpolicy="origin"` and `crossorigin="anonymous"` attributes are required for optimal performance and cross-browser functionality. + === Step 2: Update custom plugin paths Reference xref:editor-important-options.adoc#external_plugins[external_plugins] to ensure custom plugins or modified plugins continue to function in the {cloudname} deployment. diff --git a/modules/ROOT/pages/editor-command-identifiers.adoc b/modules/ROOT/pages/editor-command-identifiers.adoc index 257c2bd58c..134b326630 100644 --- a/modules/ROOT/pages/editor-command-identifiers.adoc +++ b/modules/ROOT/pages/editor-command-identifiers.adoc @@ -1,4 +1,4 @@ -= Commands Available for TinyMCE += Commands Available for {productname} :navtitle: Available Commands :description_short: Complete list of editor commands. :description: The complete list of exposed editor commands. @@ -155,7 +155,7 @@ The commands in the following table are provided by the {productname} editor and |mceTogglePlainTextPaste |Toggles paste as plain text. |mceToggleVisualAid |Toggles the visual aids for: tables without borders and anchors. |ToggleSidebar |Closes the current sidebar, or toggles the sidebar if the sidebar name is provided as a value (`__`). -|ToggleToolbarDrawer |Toggles the Toolbar Drawer. For information on toolbars, see: xref:toolbar-configuration-options.adoc#toolbar[User interface options - Toolbar]. +|ToggleToolbarDrawer |Toggles the Toolbar Drawer. Can be used with the `skip_focus` option to prevent focusing the editor. For information on toolbars, see: xref:toolbar-configuration-options.adoc#toolbar[User interface options - Toolbar]. |=== .Examples @@ -199,7 +199,7 @@ tinymce.activeEditor.execCommand('mceTogglePlainTextPaste'); tinymce.activeEditor.execCommand('mceToggleVisualAid'); tinymce.activeEditor.execCommand('ToggleSidebar'); /* OR */ tinymce.activeEditor.execCommand('ToggleSidebar', false, ''); -tinymce.activeEditor.execCommand('ToggleToolbarDrawer'); +tinymce.activeEditor.execCommand('ToggleToolbarDrawer', false, null, { skip_focus: true }); ---- [[core-table-commands]] @@ -614,7 +614,7 @@ The following command states can be queried using the xref:apis/tinymce.editor.a |Strikethrough |Returns `+true+` if the content is formatted using the same markup as the {productname} `+Strikethrough+` command. |Subscript |Returns `+true+` if the content is formatted using the same markup as the {productname} `+Subscript+` command. |Superscript |Returns `+true+` if the content is formatted using the same markup as the {productname} `+Superscript+` command. -|ToggleToolbarDrawer |Returns `+true+` if the Toolbar Drawer is open. The state can be controlled by the {productname} `+ToggleToolbarDrawer+` command. +|ToggleToolbarDrawer |Returns `+true+` if the Toolbar Drawer is open. The state can be controlled by the {productname} `+ToggleToolbarDrawer+` command. When controlling the state, use the `skip_focus` option (recommended) instead of the deprecated `skipFocus` option. |Underline |Returns `+true+` if the content is formatted using the same markup as the {productname} `+Underline+` command. |=== diff --git a/modules/ROOT/pages/editor-content-css.adoc b/modules/ROOT/pages/editor-content-css.adoc index 5a8eb9ba80..efe0e61191 100644 --- a/modules/ROOT/pages/editor-content-css.adoc +++ b/modules/ROOT/pages/editor-content-css.adoc @@ -1,4 +1,4 @@ -= CSS for rendering TinyMCE content outside the editor += CSS for rendering {productname} content outside the editor :navtitle: CSS for rendering content :description: CSS for rendering TinyMCE content outside the editor, such as on a webpage. :keywords: css, content_css diff --git a/modules/ROOT/pages/editor-context-menu-identifiers.adoc b/modules/ROOT/pages/editor-context-menu-identifiers.adoc index 2583cc84a6..a8d0f9c6f9 100644 --- a/modules/ROOT/pages/editor-context-menu-identifiers.adoc +++ b/modules/ROOT/pages/editor-context-menu-identifiers.adoc @@ -1,4 +1,4 @@ -= Context Menu Items Available for TinyMCE += Context Menu Items Available for {productname} :navtitle: Available Context Menu Items :description_short: Complete list of available context menu sections. :description: Complete list of available context menu sections. diff --git a/modules/ROOT/pages/editor-icon-identifiers.adoc b/modules/ROOT/pages/editor-icon-identifiers.adoc index 15f97202db..8109b55c09 100644 --- a/modules/ROOT/pages/editor-icon-identifiers.adoc +++ b/modules/ROOT/pages/editor-icon-identifiers.adoc @@ -1,4 +1,4 @@ -= Icons Available for TinyMCE += Icons Available for {productname} :navtitle: Available Icons :description_short: Complete list of icon identifiers. :description: Complete list of icon identifiers. diff --git a/modules/ROOT/pages/editor-icons.adoc b/modules/ROOT/pages/editor-icons.adoc index 7ea80ae4a3..874167075d 100644 --- a/modules/ROOT/pages/editor-icons.adoc +++ b/modules/ROOT/pages/editor-icons.adoc @@ -1,4 +1,4 @@ -= TinyMCE Icon options += {productname} Icon options :navtitle: Icons :description: Configure the editor's toolbar button and menu item icons. diff --git a/modules/ROOT/pages/editor-important-options.adoc b/modules/ROOT/pages/editor-important-options.adoc index 6266d36056..d0b0d45b8b 100644 --- a/modules/ROOT/pages/editor-important-options.adoc +++ b/modules/ROOT/pages/editor-important-options.adoc @@ -1,4 +1,4 @@ -= Key editor options for adding TinyMCE to an application += Key editor options for adding {productname} to an application :navtitle: Integration options :description: Key editor options for integrating TinyMCE to an application :keywords: diff --git a/modules/ROOT/pages/editor-model.adoc b/modules/ROOT/pages/editor-model.adoc index bd31b4ba18..675f8ed77b 100644 --- a/modules/ROOT/pages/editor-model.adoc +++ b/modules/ROOT/pages/editor-model.adoc @@ -1,4 +1,4 @@ -= TinyMCE Model options += {productname} Model options :navtitle: Editor content models :description: Configure the editor's content model. diff --git a/modules/ROOT/pages/editor-plugin-version.adoc b/modules/ROOT/pages/editor-plugin-version.adoc index 34356c31a5..25113309f6 100644 --- a/modules/ROOT/pages/editor-plugin-version.adoc +++ b/modules/ROOT/pages/editor-plugin-version.adoc @@ -10,21 +10,21 @@ The example below shows the default way to load {productname} {productmajorversi [source,html,subs="attributes+"] ---- - + ---- To load a specific version of {productname} {productmajorversion} other than the latest release, replace the `{productmajorversion}` in the URL with the desired version. For example, to load a minor version such as {productname} `{productmajorversion}.1`, use the following URL: [source,html,subs="attributes+"] ---- - + ---- To load a specific patch version, replace the `{productmajorversion}` in the URL with the desired patch version. For example, to load {productname} `{productmajorversion}.1.2`, use the following URL: [source,html,subs="attributes+"] ---- - + ---- [TIP] @@ -55,7 +55,7 @@ This channel deploys the latest release of {productname} that has passed our qua [source,html,subs="attributes+"] ---- - + ---- [#{productmajorversion}-testing-release-channel] @@ -68,7 +68,7 @@ This channel deploys the current *release candidate* for the `{productmajorversi [source,html,subs="attributes+"] ---- - + ---- [#{productmajorversion}-dev-release-channel] @@ -86,7 +86,7 @@ The current version of {productname} available on the `{productmajorversion}-dev [source,html,subs="attributes+"] ---- - + ---- For more details, check out the xref:editor-and-features.adoc[{productname} editor via {cloudname}]. @@ -106,7 +106,7 @@ The `plugins.min.js` script loads every premium plugin the API key is entitled t [source,html,subs="attributes+"] ---- - + ---- === `plugins.min.js` with Exclusions for Specific Plugins @@ -115,7 +115,7 @@ To exclude specific premium plugins from `plugins.min.js` because you are self-h [source,html,subs="attributes+"] ---- - + ---- [NOTE] @@ -131,7 +131,7 @@ The `cloud-plugins.min.js` script allows loading of specific premium plugins fro [source,html,subs="attributes+"] ---- - + ---- [NOTE] diff --git a/modules/ROOT/pages/editor-skin.adoc b/modules/ROOT/pages/editor-skin.adoc index 0014317d05..6517c6698f 100644 --- a/modules/ROOT/pages/editor-skin.adoc +++ b/modules/ROOT/pages/editor-skin.adoc @@ -1,4 +1,4 @@ -= TinyMCE Skin options += {productname} Skin options :navtitle: Skins :description: Configure the editor's overall appearance. diff --git a/modules/ROOT/pages/editor-theme.adoc b/modules/ROOT/pages/editor-theme.adoc index c82088d9dd..1effb2941d 100644 --- a/modules/ROOT/pages/editor-theme.adoc +++ b/modules/ROOT/pages/editor-theme.adoc @@ -1,4 +1,4 @@ -= TinyMCE Theme options += {productname} Theme options :navtitle: Themes :description: Configure the editor's theme. diff --git a/modules/ROOT/pages/enhanced-skins-and-icon-packs.adoc b/modules/ROOT/pages/enhanced-skins-and-icon-packs.adoc index 2daf4befc0..089cfe5210 100644 --- a/modules/ROOT/pages/enhanced-skins-and-icon-packs.adoc +++ b/modules/ROOT/pages/enhanced-skins-and-icon-packs.adoc @@ -7,7 +7,7 @@ The {prem_skins_icons} lets you quickly give {productname} a new look. Just choo == How to use a Enhanced skins -NOTE: Enhanced skins are only available with a link:{pricingpage}/[paid TinyMCE subscriptions]. +NOTE: Enhanced skins are only available with a link:{pricingpage}/[paid {productname} subscriptions]. Use the xref:editor-skin.adoc#skin[skin] option, in combination with the xref:add-css-options.adoc#content_css[content_css] option and the values listed below. diff --git a/modules/ROOT/pages/events.adoc b/modules/ROOT/pages/events.adoc index 912f25a988..22f2175abb 100644 --- a/modules/ROOT/pages/events.adoc +++ b/modules/ROOT/pages/events.adoc @@ -1,4 +1,4 @@ -= Events Available for TinyMCE += Events Available for {productname} :navtitle: Available Events :description_short: List of common editor events :description: List of common editor events diff --git a/modules/ROOT/pages/export-to-pdf-with-jwt-authentication-nodejs.adoc b/modules/ROOT/pages/export-to-pdf-with-jwt-authentication-nodejs.adoc index e597a4f8d9..f30e8a5dae 100644 --- a/modules/ROOT/pages/export-to-pdf-with-jwt-authentication-nodejs.adoc +++ b/modules/ROOT/pages/export-to-pdf-with-jwt-authentication-nodejs.adoc @@ -30,7 +30,8 @@ Inside the `public` folder where you created the `index.html` file add the HTML TinyMCE with Export to PDF + ---- == Step 2: Specify purchased TinyMCE plugins and toolbar buttons @@ -37,7 +37,7 @@ The following is a complete example, where: - + diff --git a/modules/ROOT/pages/filter-content.adoc b/modules/ROOT/pages/filter-content.adoc index 00704f99ac..4358901de6 100644 --- a/modules/ROOT/pages/filter-content.adoc +++ b/modules/ROOT/pages/filter-content.adoc @@ -1,4 +1,4 @@ -= Filtering TinyMCE content += Filtering {productname} content :navtitle: Content filtering :description: Learn how to create clean, maintainable and readable content. diff --git a/modules/ROOT/pages/getting-started.adoc b/modules/ROOT/pages/getting-started.adoc index b2e35f2f86..d1b13094ef 100644 --- a/modules/ROOT/pages/getting-started.adoc +++ b/modules/ROOT/pages/getting-started.adoc @@ -12,7 +12,7 @@ [.lead] xref:introduction-to-tinymce.adoc[What is TinyMCE?] -What is TinyMCE and how do I add it to my project. +What is {productname} and how do I add it to my project. | [.lead] diff --git a/modules/ROOT/pages/how-to-guides.adoc b/modules/ROOT/pages/how-to-guides.adoc index dc0e56b8d3..cedb48ce9d 100644 --- a/modules/ROOT/pages/how-to-guides.adoc +++ b/modules/ROOT/pages/how-to-guides.adoc @@ -11,13 +11,13 @@ a| [.lead] xref:accessibility.adoc[Accessibility] -Learn how TinyMCE works with screen readers and how screen readers work with TinyMCE. +Learn how {productname} works with screen readers and how screen readers work with {productname}. a| [.lead] xref:security.adoc[Security] -Security information for TinyMCE. +Security information for {productname}. a| [.lead] @@ -41,7 +41,7 @@ a| [.lead] xref:annotations.adoc[Annotations] -TinyMCE Annotations provides the ability to describe particular features or add general information to a... +{productname} Annotations provides the ability to describe particular features or add general information to a... a| [.lead] @@ -99,9 +99,9 @@ Complete list of keyboard shortcuts. a| [.lead] -xref:introduction-to-bundling-tinymce.adoc[Bundling TinyMCE] +xref:introduction-to-bundling-tinymce.adoc[Bundling {productname}] -Bundling TinyMCE with Webpack, rollup.js, or Browserify. +Bundling {productname} with Webpack, rollup.js, or Browserify. a| [.lead] diff --git a/modules/ROOT/pages/ie-template-creation.adoc b/modules/ROOT/pages/ie-template-creation.adoc index d63d877197..27713a85a9 100644 --- a/modules/ROOT/pages/ie-template-creation.adoc +++ b/modules/ROOT/pages/ie-template-creation.adoc @@ -29,21 +29,21 @@ xref:content-behavior-options.adoc#newdocument_content[`newdocument_content`] Sets the content a new editor contains when the xref:available-menu-items.adoc#the-core-menu-items[File -> New document] menu item is invoked. -Used here to show a TinyMCE instance loading an entire almost-ready-to-send-out standard document. +Used here to show a {productname} instance loading an entire almost-ready-to-send-out standard document. a| [.lead] xref:non-editable-content-options.adoc#editable_root[`editable_root`] -Sets the initial editable state of a TinyMCE instance’s root. +Sets the initial editable state of a {productname} instance’s root. -Used here to show a TinyMCE being used to present an almost-ready-to-send-out standard document that cannot, in the main, be changed by an end-user. +Used here to show a {productname} being used to present an almost-ready-to-send-out standard document that cannot, in the main, be changed by an end-user. a| [.lead] xref:non-editable-content-options#editable_class[`editable_class`] -Specifies the class name that TinyMCE will use to determine which areas of content are editable. +Specifies the class name that {productname} will use to determine which areas of content are editable. Used here to setup the portions of an almost-ready-to-send-out standard document that end-users can (and should) edit. diff --git a/modules/ROOT/pages/import-from-word-with-jwt-authentication-nodejs.adoc b/modules/ROOT/pages/import-from-word-with-jwt-authentication-nodejs.adoc index 51078cfd4d..13cd80da77 100644 --- a/modules/ROOT/pages/import-from-word-with-jwt-authentication-nodejs.adoc +++ b/modules/ROOT/pages/import-from-word-with-jwt-authentication-nodejs.adoc @@ -30,7 +30,8 @@ Inside the `public` folder where you created the `index.html` file add the HTML TinyMCE with Import from Word diff --git a/modules/ROOT/pages/mediaembed-server-config.adoc b/modules/ROOT/pages/mediaembed-server-config.adoc index 8098f3b665..34b0da8f54 100644 --- a/modules/ROOT/pages/mediaembed-server-config.adoc +++ b/modules/ROOT/pages/mediaembed-server-config.adoc @@ -2,7 +2,7 @@ :description: Instructions for getting the Enhanced Media Embed server configured. :keywords: enterprise, pricing, video, youtube, vimeo, mp3, mp4, mov, movie, clip, film, link, linkchecking, linkchecker, mediaembed, media -Once you have the xref:introduction-to-premium-selfhosted-services.adoc[server-side component] installed, additional configuration to your `+application.conf+` file is required. (Don't forget to restart the Java application server after updating the configuration.) +Once you have the xref:individual-hyperlinking-container.adoc[Deploy the {productname} hyperlinking server-side component using Docker] installed, additional configuration to your `+application.conf+` file is required. (Don't forget to restart the Docker container after updating the configuration.) The Enhanced Media Embed service allows you to choose between using your own https://iframely.com/[Iframely] account, configuring custom http://oembed.com/[oEmbed] endpoints or using a combination of both. diff --git a/modules/ROOT/pages/migration-from-7x.adoc b/modules/ROOT/pages/migration-from-7x.adoc index e7c6054b80..cce056d84a 100644 --- a/modules/ROOT/pages/migration-from-7x.adoc +++ b/modules/ROOT/pages/migration-from-7x.adoc @@ -3,5 +3,594 @@ :description: Guidance for migrating from TinyMCE 7 to TinyMCE 8 :keywords: migration, considerations, premigration, pre-migration :release-version: 8.0 +:page-toclevels: 3 -// TODO \ No newline at end of file +== Introduction + +This guide provides a comprehensive overview of the breaking changes introduced in {productname} {release-version}, along with the necessary steps to migrate from {productname} 7.x. It covers key updates to APIs, plugins, and service configurations, including deprecated methods, renamed components, and removed features. These changes are designed to enhance performance, simplify configuration, and align with modern web standards, ensuring a smoother transition and continued compatibility for your integrations. + +[IMPORTANT] +.Breaking Changes Quick Reference +==== +The following table summarizes all breaking changes in {productname} {release-version}. Each item links to detailed information further in the guide. + +Any items marked **"High"** level require immediate attention during migration. + +[cols="2,3,1",options="header"] +|=== +|Breaking Change |Impact |Level +|xref:license-key-system-update[License Key System] +|Self-hosted deployments now require a new license key format and license key manager. Old keys are **not compatible**. +|High + +|xref:dompurify-update-breaking-change[DOMPurify Update] +|Sanitization is now stricter; content previously allowed may be stripped or altered. +|High + +|xref:editor-selection-setcontent-deprecated[editor.selection.setContent] +|Method deprecated. Use `editor.insertContent` instead. +|Medium + +|xref:fire-method-deprecation[fire()] +|Method deprecated. Use `dispatch()` for event handling. +|Medium + +|xref:editor-documentbaseurl-removal[editor.documentBaseUrl] +|Undocumented property removed. Use `editor.editorManager.documentBaseURI` instead. +|Low + +|xref:skipfocus-consolidation[skipFocus and skip_focus] +|Options consolidated to `skipFocus` in `ToggleToolbarDrawer`. +|Low + +|xref:split-button-css-breaking-change[Split buttons] +|Split button CSS classes and structure have changed. +|Low +|=== +==== + +[[license-key-system-update]] +=== Transition from Version 7 License Keys to Version {release-version} License Keys +// #EPIC-192 + +[IMPORTANT] +==== +This section applies to self-hosted installations only. For cloud deployments, license key management is handled automatically. +==== + +{productname} {release-version} introduces an enhanced license key system that requires specific attention during migration. + +The complete licensing documentation xref:license-key.adoc[License Key Management] covers: + +* Detailed explanations of all license types (GPL, Commercial, GPL with Premium Features) +* Time-based vs Version-locked license key differences +* License states (Active, Grace Period, Expired, Invalid) +* Deployment options (Cloud-only, Self-hosted, Hybrid) +* Step-by-step configuration examples for each setup +* Commercial License Key Manager setup and requirements +* Troubleshooting and FAQ + +**Impact**: The new license key system introduces breaking changes that require updates to your configuration and code. + +**Key Migration Considerations**: + +* *License Key Format Change:* +** Version 7 keys are: Not compatible with {productname} {release-version}. +** New keys use the prefix `T8LK:` for commercial licenses or `GPL+T8LK:` for GPL with Premium Features. +* *Mandatory Key Requirement:* Self-hosted deployments **now require** a valid license key; without one, the editor will be set to `readonly`. +* *Commercial License Manager:* Self-hosted commercial deployments **require** the new License Key Manager addon. + +**Migration Steps:** + +. *Obtain New License Key:* ++ +* link:https://www.tiny.cloud/contact/[Contact us] to obtain a new {productname} {release-version} license key, or use `gpl` for the open source version. See: xref:license-key.adoc#setting-the-license[setting the license] for details. ++ +. *Update Configuration:* ++ +[source, javascript] +---- +// Old TinyMCE 7 configuration +tinymce.init({ + selector: '#editor', + // No license key required +}); + +// New TinyMCE 8 configuration +tinymce.init({ + selector: '#editor', + license_key: 'T8LK:...' // New format required +}); +---- + +==== License Key Manager Setup + +When migrating to {productname} {release-version} with a commercial license, the License Key Manager addon is required for the editor to operate. The setup varies based on your deployment method: + +*CDN/Static Hosting:* + +* Ensure the supplied `licensekeymanager` folder is in your {productname} plugins directory: + +[tree] +---- +your-site/ +├── tinymce/ +│ └── plugins/ +│ ├── licensekeymanager/ # Add this folder +│ │ ├── plugin.min.js +│ │ └── index.js +│ └── ... other plugins +---- + +*NPM/Module Bundler:* + +Install {productname} and ensure the license key manager is imported: + +[source, javascript] +---- +// Import TinyMCE +import tinymce from 'tinymce'; + +// Import the license key manager +import 'tinymce/plugins/licensekeymanager'; + +tinymce.init({ + selector: '#editor', + license_key: 'T8LK:...' // New format required +}); +---- + +*React/Next.js:* + +When using the `@tinymce/tinymce-react` package: + +[source, javascript] +---- +import { Editor } from '@tinymce/tinymce-react'; +import 'tinymce/plugins/licensekeymanager'; + +export default function MyEditor() { + return ( + + ); +} +---- + +*PHP/Laravel:* + +Ensure the license key manager is included when publishing {productname} assets: + +[source, php] +---- + + + +---- + +[IMPORTANT] +==== +* The license key manager is automatically included when using {companyname} Cloud. +* The plugin does not need to be added to the `plugins` configuration option. +* For bundled applications, the license key manager must be loaded before {productname} initialization. +* For bundled applications, ensure the license key manager is not excluded during build optimization. +==== + +For complete details on license key manager setup and troubleshooting, see xref:license-key.adoc##setting-up-the-commercial-license-key-manager[Setting up the Commercial License Key Manager]. + +**License Migration checklist:** + +* [ ] Contact support for new {productname} {release-version} license key or use GPL for the open source version +* [ ] Install license key manager addon for commercial licenses +* [ ] Update configuration with new license key format +* [ ] Test editor functionality with new license +* [ ] Verify all premium features are working + +[[dompurify-update-breaking-change]] +=== DOMPurify Update and Stricter Sanitization (Breaking Change) +// #TINY-12056 + +{productname} {release-version} updates the DOMPurify dependency to version 3.2.6 and enables the `SAFE_FOR_XML` flag by default. This is a breaking change: content that previously passed sanitization in {productname} 7 may now be stripped or altered during the sanitization process. + +[IMPORTANT] +==== +This change improves security and aligns with DOMPurify's recommended defaults. However, existing content and integrations that relied on the previous, less strict sanitization behavior may be impacted. +==== + +**Key Changes**: + +* **DOMPurify upgraded to 3.2.6** +* **`SAFE_FOR_XML` enabled** — This setting enforces stricter handling of comments and attribute values, preventing certain XSS vectors. +* **Content Impact** — HTML comments containing tags, Internet Explorer conditional comments, and attributes with HTML-like values may now be removed during sanitization. Content that was previously allowed may be stripped. + +**Impact**: This change improves security by preventing potential XSS attacks through comments and attributes that were previously allowed. However, it may also result in content being stripped or altered unexpectedly. + +**Migration Steps:** + +* Review workflows and test content that previously relied on relaxed sanitization. +* {productname} now provides the xref:content-filtering.adoc#allow_html_in_comments[Content Filtering: allow_html_in_comments option] option. Enabling this option allows HTML tags in comments with sanitization still enabled. + +[WARNING] +Using `allow_html_in_comments` increases the risk of XSS vulnerabilities. xref:security.adoc#allow_html_in_comments[allow_html_in_comments] is not recommended for production use unless you fully understand the implications and have appropriate security measures in place. + +.Example: Content Differences +|=== +|Content |{productname} 7 Output |{productname} {release-version} Output + +|`
` +|`
` +|`
` + +|`` +|`

` +|`` + +|`` +|`` +|`` +|=== + +[NOTE] +==== +For information on disabling DOMPurify sanitization (not recommended), see xref:security.adoc#xss_sanitization-option[xss_sanitization option]. +==== + +== Core Changes + +[[split-button-css-breaking-change]] +=== Split Button Styling Update for Custom Skins +// #TINY-8665 + +{productname} {productmajorversion}, xref:custom-split-toolbar-button.adoc[split toolbar buttons] now render as two distinct components: one for the primary action and one for the dropdown chevron. + +This structural change modifies the DOM layout of split buttons and may break custom CSS rules that rely on the previous structure. + +**Impact**: This change only affects integrators using *custom skins*. + +**Migration Guide:** + +If your implementation includes a custom skin, follow these steps to ensure compatibility: + +* [ ] Confirm whether your project uses a custom skin. +* [ ] **Rebuild your custom skin** using the {productname} {productmajorversion} codebase. See xref:creating-a-skin.adoc[Creating a Skin] for instructions. +* [ ] **Update your split button usage** to align with the new structure, including support for the `chevronTooltip` option. Refer to xref:custom-split-toolbar-button.adoc[Split Toolbar Buttons] for updated configuration details. +* [ ] **Test the rendering and interaction** of split buttons in your editor to verify expected behavior. + +**Summary of Changes:** + +* Split buttons now render as two separate DOM elements. +* CSS selectors and styles that depended on the old structure may no longer apply correctly. +* The `chevronTooltip` API option is now supported. + + +== Core API Changes + +[discrete] +=== Breaking Changes Overview + +IMPORTANT: The following sections detail important changes that require updates to your code. Please review each section carefully. + +=== Updated Methods + +[[skipfocus-consolidation]] +==== skipFocus and skip_focus +// #TINY-12044 + +The `skipFocus` and `skip_focus` options for the `ToggleToolbarDrawer` command have been consolidated into a single, more consistent argument. This reduces API complexity and clarifies the intended behavior. + +**Impact**: This change simplifies focus management, reducing the risk of confusion and unexpected behavior. + +**Migration steps:** + +[source, javascript] +---- +// Old approach (Deprecated) in TinyMCE 8 +editor.execCommand('ToggleToolbarDrawer', false, { skipFocus: true }) + +// New approach (Recommended) +editor.execCommand('ToggleToolbarDrawer', false, null, { skip_focus: true }) +---- + +**Migration checklist:** + +* [ ] Locate all instances of `ToggleToolbarDrawer` command usage +* [ ] Replace `skip_focus` with `skipFocus` in command options +* [ ] Update any custom plugins using this command +* [ ] Test toolbar drawer behavior after changes + +''' + +=== Removed Methods + +[[editor-documentbaseurl-removal]] +==== editor.documentBaseUrl +[.discrete] +// #TINY-12182 + +The undocumented `editor.documentBaseUrl` property has been removed. + +.Example Usage +[source,javascript] +---- +// Removed in TinyMCE 8 +console.log('documentBaseUrl', editor.documentBaseUrl); + +// Use this instead +console.log('documentBaseURI', editor.documentBaseURI.getURI()); +---- + +TIP: Use `editor.documentBaseURI.getURI()` for all base URL operations. + +**Impact**: This change improves URL handling consistency by removing an undocumented API that was not aligned with the documented `documentBaseURI` property. + +**Migration steps:** + +To update all references of `documentBaseUrl` to the new API, replace any usage of `editor.documentBaseUrl` (or similar) with `editor.documentBaseURI.getURI()`. The property `documentBaseUrl` has been removed, and the correct way to access the document base URL is now through the `documentBaseURI` property, which is a URI object. You can then call `.getURI()` on it to get the string value of the URL. + +.For example, update this: +[source, js] +---- +const baseUrl = editor.documentBaseUrl; +---- + +to: + +[source, js] +---- +const baseUrl = tinymce.activeEditor.documentBaseURI.getURI(); +---- + +This change is necessary because the undocumented `editor.documentBaseUrl` API has been removed to improve URL handling consistency. The new approach uses the documented `documentBaseURI` property, which provides a URI object with methods such as `getURI()` to retrieve the full URL string. + +For more information see: link:https://www.tiny.cloud/docs/tinymce/latest/apis/tinymce.editor/#properties[tinymce.editor/#properties]. + +**Migration checklist:** + +* [ ] Search your codebase for all instances of `editor.documentBaseUrl`. +* [ ] Replace them with `tinymce.activeEditor.documentBaseURI.getURI()` (or `editor.documentBaseURI.getURI()` if you have an `editor` reference). + +''' + +=== Deprecated Methods + +[[editor-selection-setcontent-deprecated]] +==== editor.selection.setContent +[.discrete] +// #TINY-12109 + +The `editor.selection.setContent` API has been deprecated and will be removed in {productname} 9. + +*Impact*: This change simplifies content manipulation by consolidating insertion methods. + +**Migration steps:** + +To replace `editor.selection.setContent`, use `editor.insertContent` instead. The new method is more consistent with other content manipulation methods in {productname}. + +.Example Usage +[source,javascript] +---- +// Deprecated in TinyMCE 8, will be removed in 9 +editor.selection.setContent('

New content

'); + +// Recommended replacement +editor.insertContent('

New content

'); +---- + +**Migration checklist:** + +* [ ] Replace all instances of `editor.selection.setContent` with `editor.insertContent` +* [ ] Update custom plugins that use the old method +* [ ] Test content insertion in your editor instances + +[[fire-method-deprecation]] +==== `fire()` +// #TINY-12012, ref TINY-8102 + +The `fire()` method has been replaced by `dispatch()` for event handling. The `fire()` method will be removed in {productname} 9 to avoid confusion with its name. + +[source, javascript] +---- +// Deprecated in TinyMCE 8, will be removed in 9 +// Old approach for dispatching custom events +editor.fire('someEvent'); + +// New approach for dispatching custom events +editor.dispatch('someEvent'); +---- + +**Impact**: This change aligns {productname} with modern event handling conventions, making the API more intuitive for developers. + +**Migration checklist:** + +* [ ] Search codebase for all uses of the `fire()` method +* [ ] Replace each instance with `dispatch()` +* [ ] Review and update third-party plugins +* [ ] Test all custom event handling + +''' + +=== Plugin Updates + +==== Language Pack Filename Changes +// #TINY-12090 + +Language pack filenames have been standardized to follow the RFC5646 format. This update ensures consistent language handling across platforms and improves internationalization support. While both the legacy underscore format (e.g., `en_GB.js`) and the new hyphenated format (e.g., `en-GB.js`) are supported in {productname} {release-version}, the underscore format is deprecated and will be removed in {productname} 9. Migrating to the RFC5646 format now will ensure future compatibility and reduce maintenance overhead during upcoming upgrades. + +**Impact**: Custom language packs and configurations using the underscore-based format should update before upgrading to {productname} 9 to avoid loading failures. + +**Migration checklist:** + +* [ ] Identify all language pack files in your deployment +* [ ] Rename files to RFC5646 format (e.g., `en_GB.js` → `en-GB.js`) +* [ ] Update configuration references to language files +* [ ] Update build scripts that handle language files +* [ ] Test language switching in your application +* [ ] Update custom translation files to use the new format + +[source, javascript] +---- +// Deprecated format (supported only in versions 8.x) +language_url: '/langs/en_GB.js' + +// Recommended format +language_url: '/langs/en-GB.js' +---- + +[IMPORTANT] +Support for the underscore format will be removed in {productname} 9. Early migration is recommended. + +==== Update to Image and Accessibility Checker Plugins +// #TINY-12226 + +The Image and Accessibility Checker plugins now follow the latest W3C standards for decorative images, requiring an empty alt attribute rather than a `+role="presentation"+` attribute. This change helps improve accessibility support. + +**Impact**: Customers using these plugins will need to update their configurations to ensure continued compliance with accessibility standards. + +**Migration checklist:** + +* [ ] Identify images previously using `role="presentation"` for decorative purposes in your content +* [ ] Replace those with empty alt attributes `alt=""` +* [ ] Update image plugin configuration if customized +* [ ] Test accessibility checker with updated content + +For more information on the changes, see: xref:a11ychecker.adoc##image-rules[Accessibility Checker: Image rules]. + +=== Cross-Origin Resource Loading Configuration +// #TINY-12228, TINY-12326 + +When upgrading to {productname} 8, you will need to review and possibly update how your integration handles cross-origin resource loading. {productname} 8 provides a new configuration option for controlling the `crossorigin` attribute on loaded resources. + +**What to check:** + +. If you're using {cloudname}: ++ +* Ensure your script tag includes both required attributes: ++ +[source,html,subs="attributes+"] +---- + +---- ++ +. If your application loads resources (scripts or stylesheets) from different domains: ++ +* Configure the new crossorigin function to control the `crossorigin` attribute for all resources. ++ +.Example: Configuring the crossorigin function +[source, javascript] +---- +const crossOriginFunction = (url, resourceType) => { + // Returning 'anonymous' or 'use-credentials' here would explicitly set the attribute + return 'anonymous'; + // return 'use-credentials'; + // return undefined; // Omits the 'crossorigin' attribute for all resources by returning undefined +}; + +tinymce.init({ + selector: "textarea", + crossorigin: crossOriginFunction +}); +---- ++ +. If you're using content_css from a different domain: + +* The `content_css_cors` option takes precedence for stylesheets. +* Review your `content_css` configuration if you use cross-origin stylesheets. + +**Migration checklist:** + +* [ ] Verify script tag attributes for Cloud deployments. +* [ ] Configure `crossorigin` function if using cross-origin resources. +* [ ] Test resource loading in your deployment environment. +* [ ] Review `content_css` configuration if using cross-origin stylesheets. + +For complete details on the new crossorigin function API, see: xref:tinymce-and-cors.adoc#crossorigin[crossorigin configuration option]. + +=== Technical Cleanup + +==== Removed Empty Files +// #TINY-11287, #TINY-12084 + +Several empty files have been removed from the codebase to reduce clutter and improve maintenance: + +* Empty CSS file from the **Comments** plugin +* Empty LESS file from the **Mentions** plugin + +**Impact**: These changes have no functional impact but may affect custom build processes that explicitly reference these files. + +**Migration checklist:** + +* [ ] Check build processes for references to Comments plugin CSS +* [ ] Check build processes for references to Mentions plugin LESS +* [ ] Remove any imports of these empty files +* [ ] Test Comments and Mentions plugins after removal + +=== Service and Configuration Changes + +==== Discontinuation of Medical English (UK) +// #EPIC-255 + +[WARNING] +The "Medical English (UK)" dictionary has been removed due to licensing constraints. Customers using this feature must update their configurations accordingly. + +**Impact**: Users relying on this dictionary will need to make alternative arrangements for medical-specific spell checking. + +**Migration checklist:** + +* [ ] Remove "Medical English (UK)" from spellchecker configurations +* [ ] Remove any custom dictionary integrations related to Medical English +* [ ] Test spellchecker functionality with remaining dictionaries +* [ ] Configure alternative medical dictionary if required + +==== Decoupling of Service Versions from {productname} Editor +// #EPIC-247, #EPIC-265 + +Services previously bundled with the editor, such as Java Servlet services, are no longer included in {productname} {release-version} packages. Customers should migrate to xref:bundle-intro-setup.adoc[Containerized service deployments] or consider alternative hosting options. + +**Impact**: This reduces the dependency between the editor and backend services, simplifying updates and maintenance. + +**Migration steps:** + +* Update the applications deployment architecture to use xref:bundle-intro-setup.adoc[Containerized services] where applicable + +==== Transition from Java WAR Files to Containerized Services +// #EPIC-247 + +{productname} {release-version} no longer includes Java WAR files for backend services like the spell checker. Customers are required to migrate to modern Docker/OCI containers for self-hosted deployments. + +**Impact**: This reduces infrastructure complexity and aligns with modern DevOps practices. + +**Migration checklist:** + +* [ ] Inventory current WAR file deployments +* [ ] Review containerization requirements for your environment +* [ ] Plan transition timeline to containerized services +* [ ] Set up container infrastructure (Docker/Kubernetes) +* [ ] Deploy and test containerized services +* [ ] Update service connection configurations +* [ ] Contact link:{supporturl}/[{supportname}] if legacy WAR files are still needed + +For more information on deploying the server-side components using Docker, see: xref:bundle-intro-setup.adoc[Containerized service deployments]. + +== Conclusion + +Upgrading to {productname} {release-version} requires integrators to update their API calls, plugin configurations, and service integrations to accommodate the breaking changes introduced in this release. While some adjustments may involve significant code updates, these enhancements are intended to simplify development, and improve the overall editor experience. + +For more guidance, refer to the: + +* link:https://www.tiny.cloud/docs[{productname} Documentation]. +* link:https://www.tiny.cloud/contact/[Contact {supportname}] for assistance. +* Try {productname}’s new **Ask AI** widget is ready to assist you, just click the icon in the bottom-right corner of any documentation page. + +image::ask-ai/ask-ai-widget.png[Ask AI Widget, width=600, align="left"] + +== Additional Resources + +For additional details on {productname} {release-version} changes, see xref:8.0-release-notes.adoc#overview[{productname} {release-version} release notes]. diff --git a/modules/ROOT/pages/migration-from-froala.adoc b/modules/ROOT/pages/migration-from-froala.adoc index 2a9b4784a4..67c958f931 100644 --- a/modules/ROOT/pages/migration-from-froala.adoc +++ b/modules/ROOT/pages/migration-from-froala.adoc @@ -38,7 +38,7 @@ To migrate from a basic Froala 3 configuration to a basic {productname} {product + [source,html,subs="attributes+"] ---- - + ---- + Replace `+no-api-key+` with your link:{accountpageurl}/[{cloudname} API key]. @@ -119,7 +119,7 @@ The following examples show an initial Froala configuration and the migrated {pr - +
diff --git a/modules/ROOT/pages/multiple-editors.adoc b/modules/ROOT/pages/multiple-editors.adoc index dd395fb803..fc6eab32dc 100644 --- a/modules/ROOT/pages/multiple-editors.adoc +++ b/modules/ROOT/pages/multiple-editors.adoc @@ -14,7 +14,7 @@ The following example breaks the page into two separate editable areas. Each are - + + ---- diff --git a/modules/ROOT/pages/revisionhistory.adoc b/modules/ROOT/pages/revisionhistory.adoc index 287e7cd506..0df0343897 100644 --- a/modules/ROOT/pages/revisionhistory.adoc +++ b/modules/ROOT/pages/revisionhistory.adoc @@ -109,6 +109,10 @@ include::partial$configuration/revisionhistory_fetch_revision.adoc[leveloffset=+ include::partial$configuration/revisionhistory_allow_restore.adoc[leveloffset=+1] +include::partial$configuration/user_id.adoc[leveloffset=+1] + +include::partial$configuration/fetch_users.adoc[leveloffset=+1] + include::partial$configuration/revisionhistory_author.adoc[leveloffset=+1] include::partial$configuration/revisionhistory_display_author.adoc[leveloffset=+1] diff --git a/modules/ROOT/pages/security.adoc b/modules/ROOT/pages/security.adoc index 9a60891923..8ef888a7fb 100644 --- a/modules/ROOT/pages/security.adoc +++ b/modules/ROOT/pages/security.adoc @@ -1,7 +1,7 @@ = Security guide :navtitle: Security guide -:description_short: Security information for {productname}. -:description: Information on reporting security issues, what {productname} does to protect users, and what you can do to protect your users. +:description_short: Security information for TinyMCE. +:description: Information on reporting security issues, what TinyMCE does to protect users, and what you can do to protect your users. :keywords: security, xss, scripting, vulnerability, hack, hacker, csp, mitigation, protection, protect, hsts, https NOTE: The following is _general_ security advice that may be relevant to a website or application using {productname}. @@ -113,6 +113,8 @@ include::partial$configuration/sandbox_iframes.adoc[] include::partial$configuration/convert_unsafe_embeds.adoc[] +include::partial$configuration/allow_html_in_comments.adoc[] + [[insecure-transmission-and-storage-of-data]] === Insecure Transmission and Storage of data diff --git a/modules/ROOT/pages/suggestededits.adoc b/modules/ROOT/pages/suggestededits.adoc new file mode 100644 index 0000000000..0529de12f1 --- /dev/null +++ b/modules/ROOT/pages/suggestededits.adoc @@ -0,0 +1,163 @@ += Suggested Edits Plugin +:navtitle: Suggested Edits +:description: Suggested Edits keeps track of changes and review edits made by multiple authors in a document. +:description_short: A view of tracked changes made by multiple authors +:keywords: plugin, Suggested Edits, changes, diff, track changes, review changes, collaboration, multiple authors +:pluginname: Suggested Edits +:plugincode: suggestededits +:plugincategory: premium + +include::partial$misc/admon-paid-addon-pricing.adoc[] + +The {pluginname} plugin allows multiple users to collaborate on a document. The review window shows which user suggested which edits, whether they added, removed, modified, or replaced any content, and allows users to provide feedback on those suggestions or give a final review by accepting or rejecting them. + +== Interactive example + +liveDemo::{plugincode}[] + +include::partial$misc/admon-iframe-only.adoc[] + +== How it works + +The {pluginname} plugin keeps track of every edit made to the document by the current user and stores this metadata in an internal model of the document. These suggestions can then be reviewed in the Review Edits view, where each edit is highlighted in the document, and where users can accept, reject, or provide feedback. The Review Edits view is accessible via either the `suggestededits` toolbar button or menu button within the `View` menu. + +== The model + +The {pluginname} model is a JSON object representing the document along with all unreviewed edits and feedback. The model must be kept in sync with the editor content and loaded into the editor at the same time as the content. + +The structure of the model is not documented and should not be relied upon. + +The model can be retrieved from the plugin using the xref:#get_model[`+getModel+`] API, saved externally alongside the document, and loaded into the editor with the xref:#suggestededits_model[`+suggestededits_model+`] option. This ensures that the document and the model are in sync and every user's contributions are tracked correctly. If the model and content are out of sync when the editor loads, the difference between them will be applied as a suggested edit by the current user. If a model is not provided in the editor configuration or is set to `+undefined+`, the plugin will generate a new model from the initial content. + +Alternatively the xref:#suggestededits_content[`+suggestededits_content+`] option allows the model to generate the editor content, in which case the two do not need to be kept in sync. + +== Reviewing edits + +The Review Edits view can be used to view and reviewing edits made by multiple authors. The available actions in this view depends on the xref:#suggestededits_access[`+suggestededits_access+` option]. + +=== Header + +The view contains a few controls to manage the review process: + +* Show edits: Toggles whether suggested edits are shown. When hidden, the view shows what the document will look like if the review is completed. +* Complete review: Ends the review, applying resolved suggestions to the document. Unresolved suggestions remain in the document for future review. +* Cancel: Ignores any resolved suggestions and makes no change to the document. Feedback given on suggestions will be retained. + +=== Document + +The current editor document is displayed in a sandboxed iframe, with each suggested edit highlighted as they appear in the document. The following color coding is used to indicate the type of change: + +* Green: Added content. +* Blue: Modified attributes or formatting (e.g. bold, italic, etc.). +* Red: Removed content. + +Replaced content is represented as both added and removed content, and indicates that some content was removed and replaced with new content in a single edit. When a suggestion is selected, whether in the document or in the sidebar, the corresponding highlighted suggestion in the document will be outlined in blue and scrolled into view. + +=== Sidebar + +Each suggested edit is listed as a card in the sidebar and color coded by the type of change, along with the user who made the suggestion, when the edit was made, and any feedback provided on that suggestion. When selected, each suggestion can be handled in the following ways: + +* Accept: Resolves the suggestion, applying the edit to the document when the review is completed. +* Reject: Resolves the suggestion, turning back the edit to the original state. +* Revert: Reverts the current "Accept" or "Reject" resolution on the suggestion. +* Provide feedback: Opens a text area for users to provide feedback on the suggestion. + +Feedback is shown in chronological order beneath the card details when the card is selected. Feedback allows users to discuss suggestions before resolving them. The feedback author can edit or delete their own feedback, with the appropriate permissions in the xref:#suggestededits_access[`+suggestededits_access+`] option. + +At the top of the sidebar there is a dropdown menu to apply review actions in bulk to all suggested edits: + +* Accept all. +* Reject all. +* Revert all. + +=== Finishing a review + +When completing a review, resolved suggestions will be applied to the document and will no longer be tracked in the model as a suggestion. Feedback on resolved suggestions is discarded. Any "accepted" edits will remain in the document, and any "rejected" edits will be reverted to the state before the suggestion was made. + +Review actions will apply the following to the document: + +When added content is: + +* Accepted: The content will remain in the document. +* Rejected: The content will be removed from the document. + +When modified content is: + +* Accepted: The content will retain the current formats and attributes. +* Rejected: The content will revert to modified formats and attributes to match the content state before the edit was made. + +When removed content is: + +* Accepted: The content will be removed from the document. +* Rejected: The removed content will be restored to the document. + +If a review is canceled, no resolved suggestions will be applied to the document. All suggestions, including any feedback provided during that review session, will remain stored in the model. + +== Initial setup + +To setup the {pluginname} plugin in the editor: + +* add `{plugincode}` to the `plugins` option in the editor configuration; +* add `{plugincode}` to the `toolbar` option in the editor configuration; + +For example: + +[source,js] +---- +tinymce.init({ + selector: 'textarea#suggestededits', // change this value according to your HTML + plugins: 'suggestededits', + toolbar: 'suggestededits', +}); +---- + +This configuration adds {pluginname} to the editor toolbar, enabling access to the plugin features. To fully utilize the plugin, additional configuration options must be provided. + +== Options + +The following configuration options affect the behavior of the {pluginname} plugin. + +include::partial$configuration/suggestededits_model.adoc[leveloffset=+1] + +include::partial$configuration/suggestededits_content.adoc[leveloffset=+1] + +include::partial$configuration/suggestededits_access.adoc[leveloffset=+1] + +include::partial$configuration/user_id.adoc[leveloffset=+1] + +include::partial$configuration/fetch_users.adoc[leveloffset=+1] + +Both the xref:#user_id[`+user_id+`] and xref:#fetch_users[`+fetch_users+`] options are required to configure the xref:userlookup.adoc[User Lookup API]. This API is used in the {pluginname} plugin to provide user information for each user who has made a change, allowing other users to see who made which suggestion. + +* The current user ID should be set with the xref:#user_id[`+user_id+`] option. The user ID should be a unique string that identifies the user, such as a username or an email address. +* The xref:#fetch_users[`+fetch_users+`] option is required to provide the name and avatar for users who have made suggestions. This option can be configured to fetch data from a backend service. The `+fetch_users+` function is given an array of user IDs and should return a promise, which resolves to an array containing data for the requested user IDs. + +include::partial$misc/plugin-toolbar-button-id-boilerplate.adoc[] + +include::partial$misc/plugin-menu-item-id-boilerplate.adoc[] + +== Commands + +The {pluginname} plugin provides the following {productname} commands. + +include::partial$commands/{plugincode}-cmds.adoc[] + +== Events + +The {pluginname} plugin provides the following events. + +include::partial$events/{plugincode}-events.adoc[] + +== APIs + +The {pluginname} plugin provides the following APIs. + +include::partial$plugin-apis/{plugincode}-apis.adoc[] + +//// +== Known Limitations + +[NOTE] +Since the comparison is made against the original version of the document, if there are unresolved changes by multiple authors affecting the same part, the most recent change will be used for comparison. The `hasChanges` API can be used to implement a safeguard that prevents further edits on a document with unresolved changes. + +//// \ No newline at end of file diff --git a/modules/ROOT/pages/support.adoc b/modules/ROOT/pages/support.adoc index e6c5e79206..88b0b3631d 100644 --- a/modules/ROOT/pages/support.adoc +++ b/modules/ROOT/pages/support.adoc @@ -32,16 +32,24 @@ include::partial$misc/plugin-support-table.adoc[leveloffset=+1] include::partial$misc/support-powerpaste.adoc[leveloffset=+1] -=== Supported Application Servers: Self-hosted Enterprise +=== System Requirements: Self-hosted Enterprise -The {productname} spell checking server-side component requires a Java Web Application Server that supports Servlet Implementation API 3.0. +The {productname} server-side components are delivered as Docker/OCI container images. The following requirements must be met: -Java Development Kit:: -* JDK 8 -Java (J2EE) Application Servers:: -include::partial$misc/supported-application-servers.adoc[] - -Minimum Hardware Requirements:: -* CPU: Dual Core Processor ~ 2Ghz. For higher loads, a quad core or higher is recommended. +Host System Requirements:: +* Operating System: +** Linux +** macOS +** Windows with WSL 2 +* CPU: Dual Core Processor ~ 2Ghz. For higher loads, a quad core or higher is recommended * RAM: 4 Gigabytes of RAM available for services +* Storage: At least 10GB of free disk space for container images and data + +Container Platform Support:: +* Docker standalone +* Container orchestration platforms (such as Kubernetes) +* Cloud container services + +[NOTE] +For production deployments, we recommend using container orchestration platforms or cloud container services that provide built-in scaling and monitoring capabilities. diff --git a/modules/ROOT/pages/tiny-docs-ai.adoc b/modules/ROOT/pages/tiny-docs-ai.adoc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/modules/ROOT/pages/tinymce-and-cors.adoc b/modules/ROOT/pages/tinymce-and-cors.adoc index 5f0a7d2c8c..b08cfbccfc 100644 --- a/modules/ROOT/pages/tinymce-and-cors.adoc +++ b/modules/ROOT/pages/tinymce-and-cors.adoc @@ -5,6 +5,8 @@ == Cross-Origin Resource Sharing related options +include::partial$configuration/crossorigin.adoc[leveloffset=+1] + include::partial$configuration/referrer_policy.adoc[leveloffset=+1] include::partial$configuration/content_css_cors.adoc[leveloffset=+1] diff --git a/modules/ROOT/pages/troubleshoot-server.adoc b/modules/ROOT/pages/troubleshoot-server.adoc index 973197b1d9..81fc806924 100644 --- a/modules/ROOT/pages/troubleshoot-server.adoc +++ b/modules/ROOT/pages/troubleshoot-server.adoc @@ -4,17 +4,21 @@ == General troubleshooting advice -. Verify that that your JavaScript configuration is correct for the relevant {productname} plugin. -. Ensure that your firewall has the appropriate ports and rules configured correctly. Be sure that the server the service is running on is accessible from the browser via the port specified in the server configuration -. Check the logs of your Java server for information. When making changes to the configuration you will need to restart the application server each time a change is made for that change to take effect. Refresh your browser window and then try the service again. +. Verify that your JavaScript configuration is correct for the relevant {productname} plugin. +. Ensure that your firewall has the appropriate ports and rules configured correctly. Be sure Docker containers are accessible from the browser via the port specified in the container configuration. +. Check the Docker container logs for information using `docker logs `. When making configuration changes, you will need to restart the containers for changes to take effect. Refresh your browser window and try the service again. == Debug server configuration If a service does not appear to be working, this is generally caused by one of the following reasons. This guide will walk you through the debugging process to identify the specific problem and how to remedy the issue. -. The `+application.conf+` file is incorrect. Please go back and follow the steps listed in the xref:introduction-to-premium-selfhosted-services.adoc#create-a-configuration-file[installation guide]. This is the most common problem - often the origins are specified without the port numbers and this can cause things to fail, eg: use `+http://localhost:8080+` instead of `+http://localhost+`. After making changes to the `+application.conf+` file, please restart your Java web server (e.g. Jetty or Tomcat). -. The `+application.conf+` file is correct, but something is wrong with one of the services. See the section below to debug the services. -. The `+application.conf+` file is correct, and the services are working, but the server-side component URLs that the editor uses are not quite right. Refer to the xref:introduction-to-tiny-spellchecker.adoc[Spell Checker], xref:editimage.adoc[Image Editing], xref:introduction-to-mediaembed.adoc[Enhanced Media Embed] and xref:linkchecker.adoc[Link Checker] plugin pages for help. +. The `+application.conf+` configuration file is incorrect. Please go back and follow the steps listed in the individual service setup guides: +* xref:individual-spelling-container.adoc[Spelling service configuration] +* xref:individual-image-proxy-container.adoc[Image Proxy service configuration] +* xref:individual-hyperlinking-container.adoc[Hyperlinking service configuration] +This is most commonly due to incorrect origin configuration - make sure to include port numbers (e.g., use `+http://localhost:8080+` instead of `+http://localhost+`). After making configuration changes, restart the Docker containers. +. The configuration is correct, but one of the services is not responding properly. See the section below to debug the services. +. The services are working, but the component URLs that the editor uses are not quite right. Refer to the xref:introduction-to-tiny-spellchecker.adoc[Spell Checker], xref:editimage.adoc[Image Editing], xref:introduction-to-mediaembed.adoc[Enhanced Media Embed] and xref:linkchecker.adoc[Link Checker] plugin pages for help. . The browser is sending a different origin than expected (and configured in `+application.conf+`). Refer to step 6 of xref:using-browser-tooling-to-investigate-services-issues[Using browser tooling to investigate services issues] == Browser-specific issues @@ -42,73 +46,46 @@ If the server is not running on a standard HTTP or HTTPS port (80 or 443) then C image::spell-checking-browser-tools.png[Using browser tools to investigate services issues] -NOTE: The value of the Origin header sent by the must match the origin specified in the `+application.conf+` server configuration. If it does not match, you must make the server configuration match the browser. +NOTE: The value of the Origin header sent by the browser must match the origin specified in the `+application.conf+` for your Docker container configuration. If it does not match, you must update your container configuration to match the browser. == Windows Server specific issues Sometimes the Origin header is never sent to the services, which results in the editor and services not working as intended. Refer to step 6 of xref:using-browser-tooling-to-investigate-services-issues[Using browser tooling to investigate services issues] and determine what the origin is - if you do not see an Origin header at all, please do the following: . Try accessing the editor web page using your machine's fully qualified domain name (FQDN) and keep the network tools open so you can see if the Origin header is sent back to the services. For example, if you have been testing on port 8080 on your local machine and the editor is instantiated on a page with path `+tinymce/index.html+`, navigate to `+http://myserver.example.com:8080/tinymce/index.html+` rather than `+http://localhost:8080/tinymce/index.html+`. -. If you now see an Origin header being sent across, alter your `+application.conf+` and replace all instances of 'localhost' with the domain name of your machine. -. Restart the Tomcat / Jetty service. +. If you now see an Origin header being sent across, update your Docker container `+application.conf+` to replace all instances of 'localhost' with the domain name of your machine. +. Restart the Docker containers. . Reload the browser page and all should work well. -[[out-of-memory-errors]] -== Out of memory errors +[[container-troubleshooting]] +== Container Troubleshooting -=== The Java application server throws "Out of Memory" errors +=== Debugging Service Issues -Even though you may have a large amount of RAM available, the Java Virtual Machine doesn't get to see all of that - by default it is limited to only 256Mb. +If you encounter issues with the containerized services: -For example, if you are using Tomcat, you can view how much memory is being consumed by apps. To do this you need to have the management console enabled. - -On a vanilla install this is done by editing the file /tomcat/install/directory/conf/tomcat-users.xml adding these lines in: - -[source,xml] +. Check the container status: ++ +[source,sh] ---- - - +docker ps ---- -Then, restart the server and go to a browser and open the default tomcat page `+http://localhost:8080+`. On the top right hand side are three buttons, the first of which should be "Server Status". Click that link, login with the details you set above and you should be able to see the memory consumption (see the figure below for an example). - -image::spell-checking-server-status.png[Spell Checking Server Status] - -=== To increase the amount of memory - -==== Tomcat - -Edit the setenv.sh (Unix) or setenv.bat (Windows) to read as follows: - -On Windows, please prefix each line with 'set' and remove the quotes . So the configuration would look like: - +. View service logs for errors or warnings: ++ [source,sh] ---- -set CATALINA_OPTS= -Dephox.config.file=/config/file/location/application.conf -set JAVA_OPTS= -Xms2048m -Xmx2048m -XX:PermSize=64m -XX:MaxPermSize=512m -Dfile.encoding=utf-8 -Djava.awt.headless=true -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 - -CATALINA_OPTS=" -Dephox.config.file=/config/file/location/application.conf" -JAVA_OPTS=" -Xms2048m -Xmx2048m -XX:PermSize=64m -XX:MaxPermSize=512m -Dfile.encoding=utf-8 -Djava.awt.headless=true -XX:+UseParallelGC -XX:MaxGCPauseMillis=100" +docker logs ---- -==== Jetty - -Edit the start.ini file to read as follows: - +. For persistent issues, collect logs for support: ++ [source,sh] ---- -#=========================================================== -# Jetty start.jar arguments -# Each line of this file is prepended to the command line -# arguments # of a call to: -# java -jar start.jar [arg...] -#=========================================================== --Xms2048m -Xmx2048m -XX:PermSize=64m -XX:MaxPermSize=512m -Dephox.config.file=/config/file/location/application.conf +docker logs > service-logs.txt ---- -Restart the service and confirm the settings have been applied like so: - -image::spell-checking-jetty-settings.png[Spell Checking Server Settings on Jetty] +TIP: When contacting support, include the service logs and details about your deployment configuration to help diagnose the issue. == Troubleshooting tools: curl diff --git a/modules/ROOT/pages/understanding-editor-loads.adoc b/modules/ROOT/pages/understanding-editor-loads.adoc index 7719b0e6ec..83a0376d6e 100644 --- a/modules/ROOT/pages/understanding-editor-loads.adoc +++ b/modules/ROOT/pages/understanding-editor-loads.adoc @@ -6,7 +6,7 @@ == Understanding editor loads for {productname} [IMPORTANT] -This information is only relevant to Tiny Cloud users. Users who self-host the open source version of {productname} are not subject to editor load restrictions, but must comply with the https://github.com/tinymce/tinymce/blob/master/LICENSE.TXT[open source license]. +This information is only relevant to Tiny Cloud users. Users who self-host the open source version of {productname} are not subject to editor load restrictions, but must comply with the link:https://github.com/tinymce/tinymce/blob/release/{productmajorversion}/LICENSE.md[open source license]. An editor load is the event that occurs each time {productname} is initialized in your application. The editor dispatches the 'init' event to indicate a successful load. For example, if 100 users load {productname} 10 times each, the result would be 1,000 editor loads. diff --git a/modules/ROOT/pages/use-tinymce-inline.adoc b/modules/ROOT/pages/use-tinymce-inline.adoc index 35add3adf8..c3b580a7b5 100644 --- a/modules/ROOT/pages/use-tinymce-inline.adoc +++ b/modules/ROOT/pages/use-tinymce-inline.adoc @@ -26,7 +26,7 @@ Inline mode only works on content within a block element (such as: `+div+` or `+ - + + +---- +==== + +[TIP] +==== +* The crossorigin function runs for both scripts and stylesheets loaded by {productname}. +* Using `+'anonymous'+` sends the Origin header without credentials. +* Using `+'use-credentials'+` sends the Origin header with credentials (not recommended for most cases). +* Returning `undefined` means no crossorigin attribute will be set, as it omits the attribute entirely for the resource. +* For stylesheet resources, the `+content_css_cors+` option takes precedence over the `+crossorigin+` function. See: xref:add-css-options.adoc#content_css_cors[content_css_cors] for details about cross-origin stylesheet loading. +==== + + +For more details on the crossorigin attribute, see: link:https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin[MDN Web Docs - HTML attribute: crossorigin]. diff --git a/modules/ROOT/partials/configuration/defaultmenuitems.adoc b/modules/ROOT/partials/configuration/defaultmenuitems.adoc index dd2c811ce9..eaf1063a98 100644 --- a/modules/ROOT/partials/configuration/defaultmenuitems.adoc +++ b/modules/ROOT/partials/configuration/defaultmenuitems.adoc @@ -5,7 +5,7 @@ tinymce.init({ menu: { file: { title: 'File', items: 'newdocument restoredraft | preview | importword exportpdf exportword | print | deleteallconversations' }, edit: { title: 'Edit', items: 'undo redo | cut copy paste pastetext | selectall | searchreplace' }, - view: { title: 'View', items: 'code revisionhistory | visualaid visualchars visualblocks | spellchecker | preview fullscreen | showcomments' }, + view: { title: 'View', items: 'code suggestededits revisionhistory | visualaid visualchars visualblocks | spellchecker | preview fullscreen | showcomments' }, insert: { title: 'Insert', items: 'image link media addcomment pageembed codesample inserttable | math | charmap emoticons hr | pagebreak nonbreaking anchor tableofcontents | insertdatetime' }, format: { title: 'Format', items: 'bold italic underline strikethrough superscript subscript codeformat | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | language | removeformat' }, tools: { title: 'Tools', items: 'spellchecker spellcheckerlanguage | a11ycheck code wordcount' }, diff --git a/modules/ROOT/partials/configuration/fetch_users.adoc b/modules/ROOT/partials/configuration/fetch_users.adoc new file mode 100644 index 0000000000..e904ba7ec9 --- /dev/null +++ b/modules/ROOT/partials/configuration/fetch_users.adoc @@ -0,0 +1,55 @@ +[[fetch_users]] +== `fetch_users` + +A **required callback function** that fetches user data. This function is called with an array of user IDs and should return a `Promise` that resolves to an array of user objects. The callback is used by the xref:userlookup.adoc[`UserLookup`] API. If the returned array does not include all requested user IDs, promises for the missing users will be rejected with a "User \{id} not found" error. + +*Type:* `Function` + +*Parameters:* +- `ids` (`Array`): An array of user IDs to fetch. + +*Returns:* +- `Promise>`: A promise that resolves to an array of user objects. + +.Example: using `fetch_users` option +[source,javascript] +---- +tinymce.init({ + selector: '#editor', + user_id: 'alextaylor', + fetch_users: (userIds) => Promise.all(userIds + .map((userId) => + fetch(`/users/${userId}`) // Fetch user data from the server + .then((response) => response.json()) + .catch(() => ({ id: userId })) // Still return a valid user object even if the fetch fails + )), +}); +---- + +.Example: returning user array with validation +[source,javascript] +---- +tinymce.init({ + selector: '#editor', + user_id: 'alextaylor', + fetch_users: async (userIds) => { + const users = await fetch('/users', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ userIds }) + }) + .then((response) => response.json()) + .catch(() => + userIds.map((userId) => + ({ id: userId }) // Still returns valid users even if the fetch fails + ) + ); + + return userIds.map( + (userId) => + users.find((user) => user.id === userId) + || ({ id: userId }) // Still returns a valid user even if it wasn't returned from the server + ) + } +}); +---- \ No newline at end of file diff --git a/modules/ROOT/partials/configuration/icon_list.adoc b/modules/ROOT/partials/configuration/icon_list.adoc index c8a777f63d..69835f6847 100644 --- a/modules/ROOT/partials/configuration/icon_list.adoc +++ b/modules/ROOT/partials/configuration/icon_list.adoc @@ -37,11 +37,13 @@ | `+checklist-rtl+` | image:icons/checklist-rtl.svg[checklist-rtl.svg] | `+checklist-rtl.svg+` | `+checklist+` | image:icons/checklist.svg[checklist.svg] | `+checklist.svg+` | `+checkmark+` | image:icons/checkmark.svg[checkmark.svg] | `+checkmark.svg+` +| `+checkmark-filled+` | image:icons/checkmark-filled.svg[checkmark-filled.svg] | `+checkmark-filled.svg+` | `+chevron-down+` | image:icons/chevron-down.svg[chevron-down.svg] | `+chevron-down.svg+` | `+chevron-left+` | image:icons/chevron-left.svg[chevron-left.svg] | `+chevron-left.svg+` | `+chevron-right+` | image:icons/chevron-right.svg[chevron-right.svg] | `+chevron-right.svg+` | `+chevron-up+` | image:icons/chevron-up.svg[chevron-up.svg] | `+chevron-up.svg+` | `+close+` | image:icons/close.svg[close.svg] | `+close.svg+` +| `+close-filled+` | image:icons/close-filled.svg[close-filled.svg] | `+close-filled.svg+` | `+code-sample+` | image:icons/code-sample.svg[code-sample.svg] | `+code-sample.svg+` | `+color-levels+` | image:icons/color-levels.svg[color-levels.svg] | `+color-levels.svg+` | `+color-picker+` | image:icons/color-picker.svg[color-picker.svg] | `+color-picker.svg+` @@ -71,6 +73,7 @@ | `+export-word+` | image:icons/export-word.svg[export-word.svg] | `+export-word.svg+` | `+exposure+` | image:icons/exposure.svg[exposure.svg] | `+exposure.svg+` | `+fb+` | image:icons/fb.svg[fb.svg] | `+fb.svg+` +| `+feedback+` | image:icons/feedback.svg[feedback.svg] | `+feedback.svg+` | `+fill+` | image:icons/fill.svg[fill.svg] | `+fill.svg+` | `+flickr+` | image:icons/flickr.svg[flickr.svg] | `+flickr.svg+` | `+flip-horizontally+` | image:icons/flip-horizontally.svg[flip-horizontally.svg] | `+flip-horizontally.svg+` @@ -176,6 +179,8 @@ | `+strike-through+` | image:icons/strike-through.svg[strike-through.svg] | `+strike-through.svg+` | `+subscript+` | image:icons/subscript.svg[subscript.svg] | `+subscript.svg+` | `+superscript+` | image:icons/superscript.svg[superscript.svg] | `+superscript.svg+` +| `+suggestededits+` | image:icons/suggestededits.svg[suggestededits.svg] | `+suggestededits.svg+` +| `+suggestededits-badge+` | image:icons/suggestededits-badge.svg[suggestededits-badge.svg] | `+suggestededits-badge.svg+` | `+table-caption+` | image:icons/table-caption.svg[table-caption.svg] | `+table-caption.svg+` | `+table-cell-classes+` | image:icons/table-cell-classes.svg[table-cell-classes.svg] | `+table-cell-classes.svg+` | `+table-cell-properties+` | image:icons/table-cell-properties.svg[table-cell-properties.svg] | `+table-cell-properties.svg+` diff --git a/modules/ROOT/partials/configuration/image_proxy_service_url.adoc b/modules/ROOT/partials/configuration/image_proxy_service_url.adoc index 6787a6c76d..6227d3dba5 100644 --- a/modules/ROOT/partials/configuration/image_proxy_service_url.adoc +++ b/modules/ROOT/partials/configuration/image_proxy_service_url.adoc @@ -11,7 +11,7 @@ endif::[] == `{proxy_setting_name}` -This option configures the URL to the server-side proxy service which allows remote images hosted on different domains to be retrieved by the {pluginname} plugin. If a proxy is not configured, then remote images may not be able to be {plugin_proxy_action}. Check the xref:introduction-to-premium-selfhosted-services.adoc[Install Server-side Components] guide for details on configuring the self-hosted Java proxy service. +This option configures the URL to the server-side proxy service which allows remote images hosted on different domains to be retrieved by the {pluginname} plugin. If a proxy is not configured, then remote images may not be able to be {plugin_proxy_action}. Check the xref:individual-image-proxy-container.adoc[Deploy the {productname} image proxy server-side component using Docker] guide for details on configuring the Docker container that you will get as a part of your link:{pricingpage}/[{enterpriseversion} subscription]. NOTE: `{proxy_setting_name}` is *not* required when enabling this plugin via xref:editor-and-features.adoc[{cloudname}]. diff --git a/modules/ROOT/partials/configuration/language.adoc b/modules/ROOT/partials/configuration/language.adoc index 9045a957f7..7915ca95c4 100644 --- a/modules/ROOT/partials/configuration/language.adoc +++ b/modules/ROOT/partials/configuration/language.adoc @@ -19,20 +19,20 @@ For information on: === Example: using `+language+` -In this example we will set the editor language to Swedish. +In this example we will set the editor language to Swedish using the RFC5646 format: [source,js] ---- tinymce.init({ selector: 'textarea', // change this value according to your HTML - language: 'sv_SE' + language: 'sv-SE' }); ---- [[using-the-premium-language-packs]] === Using the premium language packs -The following professionally localized language packs are provided to paid {cloudname} and premium self-hosted deployments. To use these language packs, set the `+language+` option to the corresponding language code. No additional configuration is required. +The following professionally localized language packs are provided to paid {cloudname} and premium self-hosted deployments. To use these language packs, set the `+language+` option to the corresponding language code in RFC5646 format. No additional configuration is required. include::partial$misc/ui-languages.adoc[leveloffset=+2] diff --git a/modules/ROOT/partials/configuration/list_max_depth.adoc b/modules/ROOT/partials/configuration/list_max_depth.adoc new file mode 100644 index 0000000000..d75ef8bd43 --- /dev/null +++ b/modules/ROOT/partials/configuration/list_max_depth.adoc @@ -0,0 +1,24 @@ +[[list_max_depth]] +== `list_max_depth` + +The `list_max_depth` option sets the maximum nesting depth for lists (ordered and unordered). + +*Type:* `Number` + +*Default value:* No limit + +=== Required plugins + +This option requires the `lists` plugin to be enabled. + +=== Example + +[source,js] +---- +tinymce.init({ + selector: 'textarea', + plugins: 'lists', + toolbar: 'bullist numlist', + list_max_depth: 2 // limits list nesting to maximum of 2 levels +}); +---- diff --git a/modules/ROOT/partials/configuration/mediaembed_service_url.adoc b/modules/ROOT/partials/configuration/mediaembed_service_url.adoc index 6c448daa60..09b04dc09c 100644 --- a/modules/ROOT/partials/configuration/mediaembed_service_url.adoc +++ b/modules/ROOT/partials/configuration/mediaembed_service_url.adoc @@ -1,7 +1,7 @@ [[mediaembed_service_url]] == `+mediaembed_service_url+` -This option specifies the URL to the service that will handle your requests and return the embeddable snippets used by the *Media Embed* plugin. Please follow these xref:introduction-to-premium-selfhosted-services.adoc[instructions] to configure the *WAR* file that you will get as a part of your link:{pricingpage}/[{enterpriseversion} subscription]. This option is not required for xref:editor-and-features.adoc[{cloudname}]. +This option specifies the URL to the service that will handle your requests and return the embeddable snippets used by the *Media Embed* plugin. The service is available as a Docker container with your link:{pricingpage}/[{enterpriseversion} subscription]. For deployment instructions, see xref:individual-hyperlinking-container.adoc[Deploy the {productname} hyperlinking server-side component using Docker]. This option is not required for xref:editor-and-features.adoc[{cloudname}]. *Type:* `+String+` diff --git a/modules/ROOT/partials/configuration/revisionhistory_author.adoc b/modules/ROOT/partials/configuration/revisionhistory_author.adoc index 5e9d867c4e..35978bbf7d 100644 --- a/modules/ROOT/partials/configuration/revisionhistory_author.adoc +++ b/modules/ROOT/partials/configuration/revisionhistory_author.adoc @@ -1,6 +1,8 @@ [[revisionhistory_author]] == `revisionhistory_author` +include::partial$DEPRECATED/generic_8_userlookup.adoc[] + This option configures the author for the `initial` and `draft` revisions. *Type:* xref:#author[Author] `+Object+` diff --git a/modules/ROOT/partials/configuration/revisionhistory_fetch_revision.adoc b/modules/ROOT/partials/configuration/revisionhistory_fetch_revision.adoc index d729459112..56116855b8 100644 --- a/modules/ROOT/partials/configuration/revisionhistory_fetch_revision.adoc +++ b/modules/ROOT/partials/configuration/revisionhistory_fetch_revision.adoc @@ -65,15 +65,15 @@ tinymce.init({ toolbar: 'revisionhistory', revisionhistory_fetch: () => Promise.resolve(lightRevisions), revisionhistory_fetch_revision: (_editor, revision) => new Promise((resolve) => { - let newRevision = null; - for (let i = 0; i < revisions.length; i++) { - const temp = revisions[i]; - if (temp.revisionId === revision.revisionId) { - newRevision = temp; - break; + let newRevision = null; + for (let i = 0; i < revisions.length; i++) { + const temp = revisions[i]; + if (temp.revisionId === revision.revisionId) { + newRevision = temp; + break; + } } - } - resolve(newRevision); + resolve(newRevision); }) }); ---- diff --git a/modules/ROOT/partials/configuration/spellchecker_language.adoc b/modules/ROOT/partials/configuration/spellchecker_language.adoc index 4314739a62..7161900fc3 100644 --- a/modules/ROOT/partials/configuration/spellchecker_language.adoc +++ b/modules/ROOT/partials/configuration/spellchecker_language.adoc @@ -5,7 +5,7 @@ This option specifies the default language used by Spell Checker. *Type:* `+String+` -*Default value:* `+'en_US'+` +*Default value:* `+'en-US'+` === Example: using `+spellchecker_language+` @@ -14,6 +14,6 @@ This option specifies the default language used by Spell Checker. tinymce.init({ selector: 'textarea', plugins: 'tinymcespellchecker', - spellchecker_language: 'de' + spellchecker_language: 'de' // Use ISO language code, hyphenated for regional variants (e.g., 'de-DE') }); ---- diff --git a/modules/ROOT/partials/configuration/spellchecker_languages.adoc b/modules/ROOT/partials/configuration/spellchecker_languages.adoc index d44edd128f..d366506b6d 100644 --- a/modules/ROOT/partials/configuration/spellchecker_languages.adoc +++ b/modules/ROOT/partials/configuration/spellchecker_languages.adoc @@ -8,7 +8,7 @@ This option specifies the spellchecker languages that are available to the user, *Default value:* [source,js] ---- -'English (United States)=en_US,English (United Kingdom)=en_GB,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Norwegian Bokmål=nb_NO,Norwegian Nynorsk=nn,Brazilian Portuguese=pt_BR,Portuguese=pt,Portuguese (Portugal)=pt_PT,Spanish=es,Swedish=sv,Swedish (Finland)=sv_FI,Afrikaans (South Africa)=af_ZA,English (Australia)=en_AU,English (Canada)=en_CA,English (United Kingdom)=en_GB,English (United States)=en_US,Medical English (US)=en_US-medical,Medical English (UK)=en_GB-medical,Maori (New Zealand)=mi_NZ' +'English (United States)=en-US,English (United Kingdom)=en-GB,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Norwegian Bokmål=nb-NO,Norwegian Nynorsk=nn,Brazilian Portuguese=pt-BR,Portuguese=pt,Portuguese (Portugal)=pt-PT,Spanish=es,Swedish=sv,Swedish (Finland)=sv-FI,Afrikaans (South Africa)=af-ZA,English (Australia)=en-AU,English (Canada)=en-CA,English (United Kingdom)=en-GB,English (United States)=en-US,Medical English (US)=en-US-medical,Maori (New Zealand)=mi-NZ' ---- @@ -19,6 +19,6 @@ This option specifies the spellchecker languages that are available to the user, tinymce.init({ selector: 'textarea', plugins: 'tinymcespellchecker', - spellchecker_languages: 'US English=en_US,UK English=en_GB,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Norwegian Bokmål=nb_NO,Norwegian Nynorsk=nn,Brazilian Portuguese=pt_BR,Portuguese=pt,Portuguese (Portugal)=pt_PT,Spanish=es,Swedish=sv,Swedish (Finland)=sv_FI,Afrikaans (South Africa)=af_ZA,English (Australia)=en_AU,English (Canada)=en_CA,English (United Kingdom)=en_GB,English (United States)=en_US,Medical English (US)=en_US-medical,Medical English (UK)=en_GB-medical,Maori (New Zealand)=mi_NZ' + spellchecker_languages: 'US English=en-US,UK English=en-GB,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Norwegian Bokmål=nb-NO,Norwegian Nynorsk=nn,Brazilian Portuguese=pt-BR,Portuguese=pt,Portuguese (Portugal)=pt-PT,Spanish=es,Swedish=sv,Swedish (Finland)=sv-FI,Afrikaans (South Africa)=af-ZA,English (Australia)=en-AU,English (Canada)=en-CA,English (United Kingdom)=en-GB,English (United States)=en-US,Medical English (US)=en-US-medical,Maori (New Zealand)=mi-NZ' }); ---- \ No newline at end of file diff --git a/modules/ROOT/partials/configuration/spellchecker_rpc_url.adoc b/modules/ROOT/partials/configuration/spellchecker_rpc_url.adoc index a64ba2f882..bd29189f79 100644 --- a/modules/ROOT/partials/configuration/spellchecker_rpc_url.adoc +++ b/modules/ROOT/partials/configuration/spellchecker_rpc_url.adoc @@ -1,7 +1,7 @@ [[spellchecker_rpc_url]] == `+spellchecker_rpc_url+` -This option specifies the URL of the server-side `+ephox-spelling+` service. For instructions on how to set up a Spell Checker server-side component, see: the xref:introduction-to-premium-selfhosted-services.adoc[server-side component installation guide]. +This option specifies the URL of the server-side `+ephox-spelling+` service. For instructions on how to set up a Spell Checker server-side component, see: the xref:individual-spelling-container.adoc[Deploy the {productname} spelling service server-side component using Docker]. NOTE: `+spellchecker_rpc_url+` is *not* required when enabling this plugin via xref:editor-and-features.adoc[{cloudname}] @@ -16,4 +16,4 @@ tinymce.init({ plugins: 'tinymcespellchecker', spellchecker_rpc_url: 'localhost/ephox-spelling' }); ----- +---- \ No newline at end of file diff --git a/modules/ROOT/partials/configuration/suggestededits_access.adoc b/modules/ROOT/partials/configuration/suggestededits_access.adoc new file mode 100644 index 0000000000..f011afb87e --- /dev/null +++ b/modules/ROOT/partials/configuration/suggestededits_access.adoc @@ -0,0 +1,44 @@ +[[suggestededits_access]] +== `suggestededits_access` + +The `suggestededits_access` option determines the level of access a user has to the {pluginname} view. This setting is crucial for controlling who can accept or reject suggestions, add feedback, or view suggestions in read-only mode. The `suggestededits_access` option does not control access to the editor content, however when used in conjunction with the xref:editor-important-options#readonly[`readonly`] option it allows for fine-grained control over user permissions. + +When set to: + +* `'full'`: The user has full access to the {pluginname} view, with permission to accept or reject suggestions. +* `'feedback'`: The user has access to the {pluginname} view, with permission to add feedback to suggestions. +* `'read'`: The user has read-only access to the {pluginname} view. +* `'none'`: The user has no access to the {pluginname} view. + +*Type:* `+String+` + +*Possible Values*: `'full'`, `'feedback'`, `'read'`, `'none'` + +*Default Value*: `'full'` + +.Example: `suggestededits_access: 'feedback'` +[source,javascript] +---- +tinymce.init({ + selector: 'textarea', // Change this value according to your HTML + plugins: 'suggestededits', + toolbar: 'suggestededits', + suggestededits_access: 'feedback', // Change this value to set the {pluginname} view permissions + readonly: false // Set to true to restrict editing +}); +---- + +liveDemo::suggestededits-access-feedback[] + +.Example: `suggestededits_access: 'read'` +[source,javascript] +---- +tinymce.init({ + selector: 'textarea', // Change this value according to your HTML + plugins: 'suggestededits', + toolbar: 'suggestededits', + suggestededits_access: 'read', // Change this value to set the {pluginname} view permissions + readonly: false // Set to true to restrict editing +}); +---- +liveDemo::suggestededits-access-read[] \ No newline at end of file diff --git a/modules/ROOT/partials/configuration/suggestededits_content.adoc b/modules/ROOT/partials/configuration/suggestededits_content.adoc new file mode 100644 index 0000000000..76ffd87f55 --- /dev/null +++ b/modules/ROOT/partials/configuration/suggestededits_content.adoc @@ -0,0 +1,29 @@ +[[suggestededits_content]] +== `suggestededits_content` + +The `suggestededits_content` option controls whether the content is loaded from the editor or the `suggestededits_model` model, allowing you to define the source of truth for the document. In either case, if a model is not provided, the plugin will generate a new model from the initial editor content. + +When set to: + +* `'html'`: the editor loads content normally, and the plugin synchronises the model with the content. This simplifies the configuration of {pluginname} in an existing integration, by attaching the {pluginname} model as an additional field alongside the editor content. +* `'model'`: the editor uses the `suggestededits_model` option to generate the initial content, ignoring any pre-existing content in the editor. With this configuration, you only need to store and provide the model, simplifying the integration of the {pluginname} plugin. + +NOTE: You are responsible for saving the model and providing it on the next load using the `suggestededits_model` option. + +*Type:* `+String+` + +*Possible Values*: `'html'`, `'model'` + +*Default value:* `'html'` + +.Example: using `suggestededits_content` +[source,js] +---- +tinymce.init({ + selector: 'textarea', // Change this value according to your HTML + plugins: 'suggestededits', + toolbar: 'suggestededits', + suggestededits_model, // Load the saved model into the editor + suggestededits_content: 'model' // Set to 'model' if you want to load the initial content from the `suggestededits_model` option +}); +---- diff --git a/modules/ROOT/partials/configuration/suggestededits_model.adoc b/modules/ROOT/partials/configuration/suggestededits_model.adoc new file mode 100644 index 0000000000..6e6b1a70aa --- /dev/null +++ b/modules/ROOT/partials/configuration/suggestededits_model.adoc @@ -0,0 +1,22 @@ +[[suggestededits_model]] +== `suggestededits_model` + +The `suggestededits_model` option loads an existing model into the {pluginname} plugin. This model contains all current suggested edits and is used to maintain continuity across sessions. If a model is not provided, the plugin generates a new model from the initial editor content. + +*Type:* `+Object+` + +.Example: using `{plugincode}_model` + +[source,js] +---- +await fetch(`/models/${documentId}`) + .then((response) => response.json()) + .then((model) => { + tinymce.init({ + selector: 'textarea', // Change this value according to your HTML + plugins: 'suggestededits', + toolbar: 'suggestededits', + suggestededits_model: model // Load the saved model into the editor + }); + }); +---- diff --git a/modules/ROOT/partials/configuration/tinycomments_author.adoc b/modules/ROOT/partials/configuration/tinycomments_author.adoc index 2fd8b7f9a1..cd88b59723 100644 --- a/modules/ROOT/partials/configuration/tinycomments_author.adoc +++ b/modules/ROOT/partials/configuration/tinycomments_author.adoc @@ -1,6 +1,8 @@ [[tinycomments_author]] == `+tinycomments_author+` +include::partial$DEPRECATED/generic_8_userlookup.adoc[] + This option sets the author id to be used when creating or replying to comments. *Type:* `+String+` diff --git a/modules/ROOT/partials/configuration/tinycomments_author_avatar.adoc b/modules/ROOT/partials/configuration/tinycomments_author_avatar.adoc index a36c4b990c..e9cb63768d 100644 --- a/modules/ROOT/partials/configuration/tinycomments_author_avatar.adoc +++ b/modules/ROOT/partials/configuration/tinycomments_author_avatar.adoc @@ -1,6 +1,8 @@ [[tinycomments_author_avatar]] == `+tinycomments_author_avatar+` +include::partial$DEPRECATED/generic_8_userlookup.adoc[] + _Optional_: This option sets the URL for the author's avatar to be used when creating or replying to comments. If this option is omitted, a generated avatar will be used instead. The avatar, if provided: * will be scaled to a 36px diameter circle; and diff --git a/modules/ROOT/partials/configuration/tinycomments_author_name.adoc b/modules/ROOT/partials/configuration/tinycomments_author_name.adoc index e13792222b..d1e459b17c 100644 --- a/modules/ROOT/partials/configuration/tinycomments_author_name.adoc +++ b/modules/ROOT/partials/configuration/tinycomments_author_name.adoc @@ -1,6 +1,8 @@ [[tinycomments_author_name]] == `+tinycomments_author_name+` +include::partial$DEPRECATED/generic_8_userlookup.adoc[] + _Optional_: This option sets the author's display name to be used when creating or replying to comments. If this option is omitted, the author `+id+` is used instead. *Type:* `+String+` diff --git a/modules/ROOT/partials/configuration/tinycomments_can_delete.adoc b/modules/ROOT/partials/configuration/tinycomments_can_delete.adoc index d4b795e43c..b47abe902d 100644 --- a/modules/ROOT/partials/configuration/tinycomments_can_delete.adoc +++ b/modules/ROOT/partials/configuration/tinycomments_can_delete.adoc @@ -1,7 +1,7 @@ [[tinycomments_can_delete]] == `+tinycomments_can_delete+` -_Optional_: This option sets the author permissions for _deleting comment conversations_. If the `+tinycomments_can_delete+` option is not included, the current author (`+tinycomments_author+`) cannot delete comment conversations created by other authors. +_Optional_: This option sets the author permissions for _deleting comment conversations_. If the `+tinycomments_can_delete+` option is **not included**, the current author (`+user_id+`) cannot delete comment conversations created by other authors. *Type:* `+Function+` @@ -9,7 +9,7 @@ _Optional_: This option sets the author permissions for _deleting comment conver [source,js] ---- (req, done, fail) => { - const allowed = req.comments.length > 0 && req.comments[0].author === ; + const allowed = req.comments.length > 0 && req.comments[0].author === ; done({ canDelete: allowed }); @@ -30,7 +30,7 @@ tinymce.init({ plugins: 'tinycomments', toolbar: 'addcomment showcomments', tinycomments_mode: 'embedded', - tinycomments_author: currentAuthor, + user_id: currentAuthor, tinycomments_can_delete: (req, done, fail) => { const allowed = req.comments.length > 0 && req.comments[0].author === currentAuthor; done({ diff --git a/modules/ROOT/partials/configuration/tinycomments_can_delete_comment.adoc b/modules/ROOT/partials/configuration/tinycomments_can_delete_comment.adoc index 3d7f0e6be5..dbd7cf1cd6 100644 --- a/modules/ROOT/partials/configuration/tinycomments_can_delete_comment.adoc +++ b/modules/ROOT/partials/configuration/tinycomments_can_delete_comment.adoc @@ -1,7 +1,7 @@ [[tinycomments_can_delete_comment]] == `+tinycomments_can_delete_comment+` -_Optional_: This option sets the author permissions for _deleting comments_. If the `+tinycomments_can_delete_comment+` option is not included, the current author (`+tinycomments_author+`) **cannot** delete comments added by other authors. +_Optional_: This option sets the author permissions for _deleting comments_. If the `+tinycomments_can_delete_comment+` option is **not included**, the current author (`+user_id+`) **cannot** delete comments added by other authors. *Type:* `+Function+` @@ -9,7 +9,7 @@ _Optional_: This option sets the author permissions for _deleting comments_. If [source,js] ---- (req, done, fail) => { - const allowed = req.comment.author === ; + const allowed = req.comment.author === ; done({ canDelete: allowed }); @@ -30,7 +30,7 @@ tinymce.init({ plugins: 'tinycomments', toolbar: 'addcomment showcomments', tinycomments_mode: 'embedded', - tinycomments_author: currentAuthor, + user_id: currentAuthor, tinycomments_can_delete_comment: (req, done, fail) => { const allowed = req.comment.author === currentAuthor; done({ diff --git a/modules/ROOT/partials/configuration/tinycomments_can_edit_comment.adoc b/modules/ROOT/partials/configuration/tinycomments_can_edit_comment.adoc index dcf1339e64..1b0d7d2e0b 100644 --- a/modules/ROOT/partials/configuration/tinycomments_can_edit_comment.adoc +++ b/modules/ROOT/partials/configuration/tinycomments_can_edit_comment.adoc @@ -1,7 +1,7 @@ [[tinycomments_can_edit_comment]] == `+tinycomments_can_edit_comment+` -_Optional_: This option sets the author permissions for _editing comments_. If the `+tinycomments_can_edit_comment+` option is not included, the current author (`+tinycomments_author+`) cannot edit comments added by other authors. +_Optional_: This option sets the author permissions for _editing comments_. If the `+tinycomments_can_edit_comment+` option is **not included**, the current author (`+user_id+`) cannot edit comments added by other authors. *Type:* `+Function+` @@ -9,7 +9,7 @@ _Optional_: This option sets the author permissions for _editing comments_. If t [source,js] ---- (req, done, fail) => { - const allowed = req.comment.author === ; + const allowed = req.comment.author === ; done({ canEdit: allowed }); @@ -29,7 +29,7 @@ tinymce.init({ plugins: 'tinycomments', toolbar: 'addcomment showcomments', tinycomments_mode: 'embedded', - tinycomments_author: currentAuthor, + user_id: currentAuthor, tinycomments_can_edit_comment: (req, done, fail) => { const allowed = req.comment.author === currentAuthor; done({ diff --git a/modules/ROOT/partials/configuration/tinycomments_can_resolve.adoc b/modules/ROOT/partials/configuration/tinycomments_can_resolve.adoc index 491cb7ba97..5873857e0b 100644 --- a/modules/ROOT/partials/configuration/tinycomments_can_resolve.adoc +++ b/modules/ROOT/partials/configuration/tinycomments_can_resolve.adoc @@ -16,7 +16,7 @@ tinymce.init({ plugins: 'tinycomments', toolbar: 'addcomment showcomments', tinycomments_mode: 'embedded', - tinycomments_author: currentAuthor, + user_id: currentAuthor, tinycomments_can_resolve: (req, done, fail) => { const allowed = req.comments.length > 0 && req.comments[0].author === currentAuthor; done({ diff --git a/modules/ROOT/partials/configuration/tinycomments_fetch_author_info.adoc b/modules/ROOT/partials/configuration/tinycomments_fetch_author_info.adoc index 077409d53d..801ebe839d 100644 --- a/modules/ROOT/partials/configuration/tinycomments_fetch_author_info.adoc +++ b/modules/ROOT/partials/configuration/tinycomments_fetch_author_info.adoc @@ -1,6 +1,8 @@ [[tinycomments_fetch_author_info]] == `+tinycomments_fetch_author_info+` +include::partial$DEPRECATED/generic_8_userlookup.adoc[] + The {pluginname} plugin uses the `+tinycomments_fetch_author_info+` callback function to provide author details for comments displayed in the editor. This function is called whenever a comment is rendered, and it allows the editor to display the author’s name and avatar based on the current user. diff --git a/modules/ROOT/partials/configuration/tinycomments_mentions_enabled.adoc b/modules/ROOT/partials/configuration/tinycomments_mentions_enabled.adoc index 45e0df05ce..23d0a186fd 100644 --- a/modules/ROOT/partials/configuration/tinycomments_mentions_enabled.adoc +++ b/modules/ROOT/partials/configuration/tinycomments_mentions_enabled.adoc @@ -13,14 +13,29 @@ The {pluginname} plugin offers the `+tinycomments_mentions_enabled+` option to c [source,js] ---- +const userDb = { + 'johnsmith': { + id: 'johnsmith', + name: 'John Smith' + }, +}; + tinymce.init({ selector: 'textarea', // change this value according to your html plugins: 'tinycomments mentions', toolbar: 'addcomment showcomments', tinycomments_mentions_enabled: true, tinycomments_mode: 'embedded', - tinycomments_author: 'johnsmith', - tinycomments_author_name: 'John Smith', + user_id: 'johnsmith', + fetch_users: (userIds) => { + return Promise.all( + userIds.map( + (userId) => new Promise( + (resolve) => resolve(userDb[userId] || { id: userId }) + ) + ) + ) + }, mentions_fetch, mentions_menu_complete, mentions_menu_hover, diff --git a/modules/ROOT/partials/configuration/user_id.adoc b/modules/ROOT/partials/configuration/user_id.adoc new file mode 100644 index 0000000000..2bcc3552bf --- /dev/null +++ b/modules/ROOT/partials/configuration/user_id.adoc @@ -0,0 +1,17 @@ +[[user_id]] +== `+user_id+` + +This option sets the unique identifier for the current user in the editor. It is used in the the xref:userlookup.adoc[`UserLookup`] API. + +*Type:* `+String+` + +*Default value:* `'Anonymous'` + +.Example: using `user_id` option +[source,javascript] +---- +tinymce.init({ + selector: 'textarea', // Change this value according to your HTML + user_id: 'alextaylor' // replace this with a unique string to identify the user +}); +---- \ No newline at end of file diff --git a/modules/ROOT/partials/docker/spelling-service/spelling-service-installation.adoc b/modules/ROOT/partials/docker/spelling-service/spelling-service-installation.adoc index afcc0d41ef..061d819be1 100644 --- a/modules/ROOT/partials/docker/spelling-service/spelling-service-installation.adoc +++ b/modules/ROOT/partials/docker/spelling-service/spelling-service-installation.adoc @@ -175,11 +175,11 @@ spelling-tiny-1 | 2025-02-11 09:51:11.104Z [io-compute-blocker-6] INFO c.e.i.d spelling-tiny-1 | 2025-02-11 09:51:11.161Z [io-compute-blocker-6] INFO com.ephox.nectar.data.Bees$ - Loading all dictionaries from WinterTree spelling-tiny-1 | 2025-02-11 09:51:11.482Z [io-compute-blocker-5] INFO c.e.nectar.hunspell.HunspellLoader$ - Loading hunspell dictionary from path: /app/resources/hunspell-dictionaries and locale es spelling-tiny-1 | 2025-02-11 09:51:11.536Z [io-compute-blocker-5] INFO c.e.nectar.hunspell.HunspellLoader$ - Finished loading hunspell for es -spelling-tiny-1 | 2025-02-11 09:51:11.537Z [io-compute-blocker-5] INFO c.e.nectar.hunspell.HunspellLoader$ - Loading hunspell dictionary from path: /app/resources/hunspell-dictionaries and locale pt_BR -spelling-tiny-1 | 2025-02-11 09:51:11.881Z [io-compute-blocker-5] INFO c.e.nectar.hunspell.HunspellLoader$ - Finished loading hunspell for pt_BR +spelling-tiny-1 | 2025-02-11 09:51:11.537Z [io-compute-blocker-5] INFO c.e.nectar.hunspell.HunspellLoader$ - Loading hunspell dictionary from path: /app/resources/hunspell-dictionaries and locale pt-BR +spelling-tiny-1 | 2025-02-11 09:51:11.881Z [io-compute-blocker-5] INFO c.e.nectar.hunspell.HunspellLoader$ - Finished loading hunspell for pt-BR ... -spelling-tiny-1 | 2025-02-11 09:51:13.593Z [io-compute-blocker-5] INFO c.e.nectar.hunspell.HunspellLoader$ - Loading hunspell dictionary from path: /app/resources/hunspell-dictionaries and locale de_DE -spelling-tiny-1 | 2025-02-11 09:51:13.651Z [io-compute-blocker-5] INFO c.e.nectar.hunspell.HunspellLoader$ - Finished loading hunspell for de_DE +spelling-tiny-1 | 2025-02-11 09:51:13.593Z [io-compute-blocker-5] INFO c.e.nectar.hunspell.HunspellLoader$ - Loading hunspell dictionary from path: /app/resources/hunspell-dictionaries and locale de-DE +spelling-tiny-1 | 2025-02-11 09:51:13.651Z [io-compute-blocker-5] INFO c.e.nectar.hunspell.HunspellLoader$ - Finished loading hunspell for de-DE spelling-tiny-1 | 2025-02-11 09:51:14.142Z [io-compute-9] INFO o.h.b.c.nio1.NIO1SocketServerGroup - Service bound to address /0:0:0:0:0:0:0:0:18080 spelling-tiny-1 | 2025-02-11 09:51:14.146Z [io-compute-9] INFO o.h.blaze.server.BlazeServerBuilder - spelling-tiny-1 | _ _ _ _ _ @@ -215,7 +215,7 @@ To confirm that a request is being sent to the {pluginname} service, use: + [source, sh] ---- -curl http://localhost:18080/2/check -d '{"words": ["teh"], "language": "en_US"}' -H "Origin: http://good.com" -H "Content-Type: application/json" +curl http://localhost:18080/2/check -d '{"words": ["teh"], "language": "en-US"}' -H "Origin: http://good.com" -H "Content-Type: application/json" ---- + @@ -224,7 +224,7 @@ Finally, to verify if a request is unauthorized and originates from an incorrect + [source, sh] ---- -curl http://localhost:18080/2/check -d '{"words": ["teh"], "language": "en_US"}' -H "Origin: http://bad.com" -H "Content-Type: application/json" +curl http://localhost:18080/2/check -d '{"words": ["teh"], "language": "en-US"}' -H "Origin: http://bad.com" -H "Content-Type: application/json" ---- + @@ -245,12 +245,7 @@ tinymce.init({ selector: 'textarea#spellchecker', // change this value according to your HTML plugins: 'code tinymcespellchecker link', toolbar: 'spellchecker language spellcheckdialog', - spellchecker_language: 'en_US', + spellchecker_language: 'en-US', // Note: Using RFC5646 format with hyphen spellchecker_rpc_url: "http://localhost:18080" }); ---- - - - - - diff --git a/modules/ROOT/partials/events/suggestededits-events.adoc b/modules/ROOT/partials/events/suggestededits-events.adoc new file mode 100644 index 0000000000..094c76c1fc --- /dev/null +++ b/modules/ROOT/partials/events/suggestededits-events.adoc @@ -0,0 +1,10 @@ +The following events are provided by the xref:{plugincode}.adoc[{pluginname} plugin]. + +[cols="2,1,3",options="header"] +|=== +|Name |Data |Description +|SuggestedEditsBeginReview |N/A |The Suggested Edits view has opened. +|SuggestedEditsReviewComplete |N/A |A review in the Suggested Edits view has been completed. +|SuggestedEditsReviewCancelled |N/A |A review in the Suggested Edits view was cancelled. +|SuggestedEditsHasChangesUpdate |`+hasChanges+` |The Suggested Edits model is updated. The `+hasChanges+` data is a boolean value indicating whether there are suggestions to review. +|=== \ No newline at end of file diff --git a/modules/ROOT/partials/index-pages/premium-plugins.adoc b/modules/ROOT/partials/index-pages/premium-plugins.adoc index 3d76fe8cc5..75cc78e56a 100644 --- a/modules/ROOT/partials/index-pages/premium-plugins.adoc +++ b/modules/ROOT/partials/index-pages/premium-plugins.adoc @@ -177,6 +177,12 @@ xref:autocorrect.adoc[Spelling Autocorrect] Add autocorrection of common typos and spelling errors as well as capitalization correction to {productname}. +a| +[.lead] +xref:suggestededits.adoc[Suggested Edits] + +Collaborate with multiple users on a document by drafting edits, providing feedback, and reviewing suggestions. + a| [.lead] xref:advanced-templates.adoc[Templates] @@ -202,6 +208,4 @@ Cloud-based file and image management for {productname}. // 2. Prepend the inline comment markup to this element // when the number of cells in the table is even. a| - |=== - diff --git a/modules/ROOT/partials/individually-licensed-components/export-to-pdf/export-to-pdf-installation.adoc b/modules/ROOT/partials/individually-licensed-components/export-to-pdf/export-to-pdf-installation.adoc index e2b5727b8a..bba40324ba 100644 --- a/modules/ROOT/partials/individually-licensed-components/export-to-pdf/export-to-pdf-installation.adoc +++ b/modules/ROOT/partials/individually-licensed-components/export-to-pdf/export-to-pdf-installation.adoc @@ -32,7 +32,7 @@ Pull the Docker image: [source, sh, subs="attributes+"] ---- -docker pull registry.containers.tiny.cloud/{dockerimageexporttopdf}:[version] +docker pull {dockerimageexporttopdf}:[version] ---- [TIP] diff --git a/modules/ROOT/partials/individually-licensed-components/import-from-word-and-export-to-word/import-from-word-and-export-to-word-autorization-on-premises.adoc b/modules/ROOT/partials/individually-licensed-components/import-from-word-and-export-to-word/import-from-word-and-export-to-word-autorization-on-premises.adoc index 444a629f2d..180ceac642 100644 --- a/modules/ROOT/partials/individually-licensed-components/import-from-word-and-export-to-word/import-from-word-and-export-to-word-autorization-on-premises.adoc +++ b/modules/ROOT/partials/individually-licensed-components/import-from-word-and-export-to-word/import-from-word-and-export-to-word-autorization-on-premises.adoc @@ -17,7 +17,7 @@ If the `SECRET_KEY` is not setup during the installation, then {pluginname} On-P * it is not issued in the future (e.i. the `iat` timestamp cannot be newer than the current time), * it has not expired yet. -Tokens for the {pluginname} On-Premises do not require any additional claims such as the environment ID (which is specific for Collaboration Server On-Premises), the token can be created with an empty payload. +Tokens for the {pluginname} On-Premises do not require any additional claims such as the `aud` (which is specific to cloud-based document converters), so the token can be created with an empty payload. If the specific use case involves sending requests from a backend server, then JWT tokens can be generated locally, as shown in the below request example. diff --git a/modules/ROOT/partials/individually-licensed-components/import-from-word-and-export-to-word/import-from-word-and-export-to-word-installation-on-premises.adoc b/modules/ROOT/partials/individually-licensed-components/import-from-word-and-export-to-word/import-from-word-and-export-to-word-installation-on-premises.adoc index efb79a5ae1..aa508f7c14 100644 --- a/modules/ROOT/partials/individually-licensed-components/import-from-word-and-export-to-word/import-from-word-and-export-to-word-installation-on-premises.adoc +++ b/modules/ROOT/partials/individually-licensed-components/import-from-word-and-export-to-word/import-from-word-and-export-to-word-installation-on-premises.adoc @@ -32,7 +32,7 @@ Pull the Docker image: [source, sh, subs="attributes+"] ---- -docker pull registry.containers.tiny.cloud/{dockerimageimportfromwordexporttoword}:[version] +docker pull {dockerimageimportfromwordexporttoword}:[version] ---- [TIP] diff --git a/modules/ROOT/partials/install/basic-quickstart-base.adoc b/modules/ROOT/partials/install/basic-quickstart-base.adoc index 4138ee9009..79c886efbd 100644 --- a/modules/ROOT/partials/install/basic-quickstart-base.adoc +++ b/modules/ROOT/partials/install/basic-quickstart-base.adoc @@ -131,7 +131,7 @@ Include the following line of code in the `++` of an HTML page. [source,html,subs="attributes+"] ---- - + ---- == Initialize {productname} as part of a web form @@ -151,7 +151,7 @@ ifeval::["{productSource}" == "cloud"] - + + diff --git a/modules/ROOT/partials/integrations/angular-tech-ref.adoc b/modules/ROOT/partials/integrations/angular-tech-ref.adoc index 4a7261db9f..e98c75b2cf 100644 --- a/modules/ROOT/partials/integrations/angular-tech-ref.adoc +++ b/modules/ROOT/partials/integrations/angular-tech-ref.adoc @@ -8,6 +8,7 @@ ** xref:licensekey[`+licenseKey+`] ** xref:cloudchannel[`+cloudChannel+`] ** xref:disabled[`+disabled+`] +** xref:readonly[`+readonly+`] ** xref:id[`+id+`] ** xref:init[`+init+`] ** xref:initialvalue[`+initialValue+`] @@ -97,6 +98,7 @@ The editor accepts the following properties: apiKey="no-api-key" cloudChannel="{productmajorversion}" [disabled]="false" + [readonly]="false" id="" [init]="{ }" initialValue="" @@ -130,21 +132,18 @@ include::partial$misc/get-an-api-key.adoc[] [[licensekey]] === `+licenseKey+` -{cloudname} License key. +include::partial$integrations/common/license-key-property.adoc[] -Use this when self-hosting {productname} instead of loading from {cloudname}. For more information, see: xref:license-key.adoc[License Key]. - -*Type:* `+String+` - -*Default value:* `+undefined+` - -*Possible values:* `undefined`, `'gpl'` or a valid {productname} license key - -==== Example: using `+licenseKey+` +==== Example: Commercial license +[source,html] +---- + +---- -[source,jsx] +==== Example: using `+licenseKey+` with GPL +[source,html] ---- - + ---- [[cloudchannel]] @@ -193,6 +192,24 @@ The `+disabled+` property can dynamically switch the editor between a "disabled" ---- +[[readonly]] +=== `+readonly+` + +The `+readonly+` property can dynamically switch the editor between a "read-only" mode (`+true+`) and the standard editable mode (`+false+`). + +*Type:* `+Boolean+` + +*Default value:* `+false+` + +*Possible values:* `+true+`, `+false+` + +==== Example: using `+readonly+` + +[source,html] +---- + +---- + [[id]] === `+id+` diff --git a/modules/ROOT/partials/integrations/blazor-postinstall.adoc b/modules/ROOT/partials/integrations/blazor-postinstall.adoc index 212562feaf..05f04d7bf4 100644 --- a/modules/ROOT/partials/integrations/blazor-postinstall.adoc +++ b/modules/ROOT/partials/integrations/blazor-postinstall.adoc @@ -1,4 +1,4 @@ -. Verify the installation by checking the `+ItemGroup+` references in the project file. For example, if the project is named _BlazorApp_, the relevant file would be `+BlazorApp.csproj+` with the dependency referenced as follows: +. Verify the installation by checking the `+ItemGroup+` references in the project file. For example, if the project is named _BlazorApp_, the relevant file would be `+BlazorApp.csproj+` with the dependency referenced as follows: + [source,xml] ---- @@ -16,9 +16,9 @@ + [NOTE] ==== -The location of the script depends on the type of Blazor app, including Blazor Server and Blazor WebAssembly (WASM) which are not covered in this guide. +The location of the script depends on the type of Blazor app, including Blazor Server and Blazor WebAssembly (WASM) which are not covered in this guide. -* If using Blazor Server, add the script in `+Pages/_Host.cshtml+`, for example: +* If using Blazor Server, add the script in `+Pages/_Host.cshtml+`, for example: + [source,html] ---- @@ -35,8 +35,8 @@ The location of the script depends on the type of Blazor app, including Blazor S ==== + -. Add the `+Editor+` component to a page by either: -* Using the `+using+` directive: +. Add the `+Editor+` component to a page by either: +* Using the `+using+` directive: + [source,cs] ---- @@ -44,7 +44,7 @@ The location of the script depends on the type of Blazor app, including Blazor S ---- + -For example: +For example: + _File:_ `+Pages/Index.razor+` + @@ -58,7 +58,7 @@ _File:_ `+Pages/Index.razor+`

Welcome to your new app.

---- -* Using the component with its namespace: +* Using the component with its namespace: + [source,cs] ---- @@ -78,10 +78,10 @@ _File:_ `+Pages/Index.razor+`

Hello, world!

Welcome to your new app.

----- +---- + [IMPORTANT] -In a Blazor Web App, different render modes determine how components are rendered and how interactivity is handled. To enable JavaScript interactivity, ensure that `+@rendermode InteractiveServer+` is specified in a page component. +In a Blazor Web App, different render modes determine how components are rendered and how interactivity is handled. To enable JavaScript interactivity, ensure that `+@rendermode InteractiveServer+` is specified in a page component. + ifeval::["{productSource}" == "cloud"] @@ -91,7 +91,7 @@ ifeval::["{productSource}" == "cloud"] [source,cs] ---- ----- +---- + endif::[] @@ -101,8 +101,8 @@ ifeval::["{productSource}" != "cloud"] + [source,cs] ---- - ----- + +---- + . To load {productname} from the self-hosted package instead of the {cloudname}, configure the `+ScriptSrc+` property: diff --git a/modules/ROOT/partials/integrations/blazor-tech-ref.adoc b/modules/ROOT/partials/integrations/blazor-tech-ref.adoc index bf708c773c..bafa0b8e13 100644 --- a/modules/ROOT/partials/integrations/blazor-tech-ref.adoc +++ b/modules/ROOT/partials/integrations/blazor-tech-ref.adoc @@ -19,7 +19,7 @@ The `+TinyMCE.Blazor+` `+Editor+` component accepts the following properties: JsConfSrc="path_to_jsObj" Conf="@yourConf" ApiKey="no-api-key" - LicenseKey="your-license-key" + LicenseKey="gpl" ScriptSrc="/path/to/tinymce.min.js" ClassName="tinymce-wrapper" /> @@ -188,19 +188,29 @@ In your component: === `LicenseKey` -Specifies the {productname} license key. Required for self-hosted deployments of {productname}. This property is not required for deployments using the {cloudname}. For more information on licensing, see: xref:license-key.adoc[License key]. -*Type:* `+String+` +include::partial$integrations/common/license-key-property.adoc[] + +==== Example: Commercial license (TinyMCE 8+) +[source,cs] +---- + +---- -==== Example using LicenseKey +Use this example when you have a commercial license for TinyMCE 8 or newer. The T8LK prefix is required. +==== Example: Open source GPL license [source,cs] ---- ---- +Use this example when you're using TinyMCE under the open source GPL license in a self-hosted environment. + === `ScriptSrc` Use the `+ScriptSrc+` property to specify the location of {productname} to lazy load when the application is not using {cloudname}. This setting is required if the application uses a self-hosted version of {productname}, such as the https://www.nuget.org/packages/TinyMCE/[{productname} NuGet package] or a .zip package of {productname}. diff --git a/modules/ROOT/partials/integrations/bootstrap-quick-start.adoc b/modules/ROOT/partials/integrations/bootstrap-quick-start.adoc index 3093086244..28be9a336e 100644 --- a/modules/ROOT/partials/integrations/bootstrap-quick-start.adoc +++ b/modules/ROOT/partials/integrations/bootstrap-quick-start.adoc @@ -20,7 +20,7 @@ ifeval::["{productSource}" == "cloud"] + [source,html,subs="attributes+"] ---- - + ---- + Replace `+no-api-key+` in the source script (`+ + ---- + Replace `no-api-key` in the source script (` @@ -139,7 +139,7 @@ ifeval::["{productSource}" == "package-manager"] - + @@ -149,7 +149,7 @@ ifeval::["{productSource}" == "package-manager"] + + + ---- The default {productname} editor will load at this location if the page is opened in a web browser. diff --git a/modules/ROOT/partials/integrations/webcomponent-tech-ref.adoc b/modules/ROOT/partials/integrations/webcomponent-tech-ref.adoc index 1640dc3354..a6a2da8379 100644 --- a/modules/ROOT/partials/integrations/webcomponent-tech-ref.adoc +++ b/modules/ROOT/partials/integrations/webcomponent-tech-ref.adoc @@ -3,11 +3,14 @@ * xref:use-a-cloud-version-of-the-tinymce-web-component-integration[Use a cloud version of the {productname} Web Component integration] * xref:installing-the-tinymce-web-component-integration[Installing the {productname} Web Component integration] * xref:loading-tinymce[Loading {productname}] +* xref:licensekey[Setting the license key] * xref:configuring-the-editor[Configuring the editor] ** xref:setting-the-initial-content[Setting the initial content] ** xref:loading-plugins[Loading plugins] ** xref:setting-the-editor-width[Setting the editor width] ** xref:setting-the-editor-height[Setting the editor height] +** xref:setting-readonly[Setting the editor to readonly] +** xref:setting-disabled[Setting the editor to disabled] ** xref:setting-the-toolbar[Setting the toolbar] ** xref:setting-the-toolbar-mode[Setting the toolbar mode] ** xref:setting-the-menu-bar[Setting the menu bar] @@ -75,6 +78,42 @@ To use {productname} Web Component with a self-hosted copy of {productname}, ens To use {productname} from the {cloudname}, add the `+api-key+` attribute to the `+tinymce-editor+` element with an API from link:{accountpageurl}/[{accountpage}]. +[[licensekey]] +== Setting the license key + +include::partial$integrations/common/license-key-property.adoc[] + +=== Example: Commercial license +[source,html] +---- + + +---- + +=== Example: Open source GPL license +[source,html] +---- + + +---- + +[IMPORTANT] +==== +* The license key is only required when self-hosting {productname} +* License keys starting with "T8LK:" use client-side validation. +* Do not use both an API key and a license key - use API key for cloud deployments, and license key for self-hosted deployments +==== + +For more information on licensing, see: xref:license-key.adoc[License Key]. + [[configuring-the-editor]] == Configuring the editor @@ -146,6 +185,26 @@ To set the height of the editor (content area and user interface), use the `+hei ---- + +[[setting-readonly]] +=== Setting readonly mode + +To set the editor's mode to `readonly`. + +[source,html] +---- + +---- + +[[setting-disaled-state]] +=== Setting disabled state +To set the editor to a disabled state. + +[source,html] +---- + +---- + [[setting-the-toolbar]] === Setting the toolbar diff --git a/modules/ROOT/partials/menu-item-ids/suggestededits-menu-items.adoc b/modules/ROOT/partials/menu-item-ids/suggestededits-menu-items.adoc new file mode 100644 index 0000000000..d45e2420b5 --- /dev/null +++ b/modules/ROOT/partials/menu-item-ids/suggestededits-menu-items.adoc @@ -0,0 +1,6 @@ +[cols="1,1,2",options="header"] +|=== +|Menu item identifier |xref:menus-configuration-options.adoc#example-the-tinymce-default-menu-items[Default Menu Location] |Description + +|`suggestededits` |View |Opens the Suggested Edits view. +|=== \ No newline at end of file diff --git a/modules/ROOT/partials/misc/admon-jwt-authentication-requirements.adoc b/modules/ROOT/partials/misc/admon-jwt-authentication-requirements.adoc index d4445f6186..d2858ee3e6 100644 --- a/modules/ROOT/partials/misc/admon-jwt-authentication-requirements.adoc +++ b/modules/ROOT/partials/misc/admon-jwt-authentication-requirements.adoc @@ -2,17 +2,34 @@ ==== The {pluginname} plugin **requires** JWT authentication when using the {companyname} Cloud service. -* Configure the `{plugincode}_token_provider` option to specify the endpoint for retrieving a valid JWT token. +**Authentication Setup:** -For more information on how to set up JWT authentication with {pluginname}, see examples: +. Create a JWT key in the Customer Portal +. Configure the `{plugincode}_token_provider` option to specify the endpoint for retrieving your JWT token -* xref:{pluginfilename}-with-jwt-authentication-nodejs.adoc[{pluginname} with JWT authentication (Node.js)] -* xref:{pluginfilename}-with-jwt-authentication-php.adoc[{pluginname} with JWT authentication (PHP)] +For more information on how to set up JWT authentication with {pluginname}, see examples: xref:{pluginfilename}-with-jwt-authentication-nodejs.adoc[{pluginname} with JWT authentication (Node.js)] or xref:{pluginfilename}-with-jwt-authentication-php.adoc[{pluginname} with JWT authentication (PHP)] -**Trial period behavior:** +**Trial period behavior** -* The {pluginname} plugin runs in evaluation mode and adds a watermark to exported content. -* The `{plugincode}_token_provider` option is "not required" during the trial period. -* If the trial period "expires" or the plugin lacks the necessary entitlement, it becomes _non-functional_. -* Attempting to use JWT authentication during the trial will result in an _error_. +ifeval::["{plugincode}" == "importword"] +* **Trial Period:** +** With JWT: Full functionality, unlimited usage, no watermarks. +** Without JWT: Full functionality with watermarks and page limits. + +* **After Trial:** +** With {pluginname} add-on + JWT: Full functionality (subscription-based) +** Without add-on: Plugin entitlements are `disabled` and functionality is no longer available. +endif::[] + +ifeval::["{plugincode}" != "importword"] +* **Trial Period:** +** With JWT: Full functionality, unlimited usage, no watermarks. +** Without JWT: Full functionality with watermarks. + +* **After Trial:** +** With {pluginname} add-on + JWT: Full functionality (subscription-based) +** Without add-on: Plugin entitlements are `disabled` and functionality is no longer available. +endif::[] + +Visit the link:https://www.tiny.cloud/auth/login/[account portal] to obtain your JWT key and test full functionality. ==== \ No newline at end of file diff --git a/modules/ROOT/partials/misc/admon-requires-8.0v.adoc b/modules/ROOT/partials/misc/admon-requires-8.0v.adoc new file mode 100644 index 0000000000..9160a25fc7 --- /dev/null +++ b/modules/ROOT/partials/misc/admon-requires-8.0v.adoc @@ -0,0 +1 @@ +NOTE: This feature is only available for {productname} 8.0 and later. diff --git a/modules/ROOT/partials/misc/custom-dictionaries-path.adoc b/modules/ROOT/partials/misc/custom-dictionaries-path.adoc index c0197fa035..83af6b5dae 100644 --- a/modules/ROOT/partials/misc/custom-dictionaries-path.adoc +++ b/modules/ROOT/partials/misc/custom-dictionaries-path.adoc @@ -2,7 +2,7 @@ The `+ephox.spelling.custom-dictionaries-path+` element is used to define the lo Requirements: -* The directory containing the custom dictionaries must be on same server machine as the java service. +* The directory containing the custom dictionaries must be mounted as a volume in the container. * The directory should not contain subdirectories or non-dictionary files. {companyname} recommends storing the custom dictionaries in a similar location to the `+application.conf+` file. For example, if `+application.conf+` is in a directory called `+/opt/ephox+`, the dictionary files could be stored in the subdirectory `+/opt/ephox/dictionaries+`. diff --git a/modules/ROOT/partials/misc/hunspell-dictionaries-path.adoc b/modules/ROOT/partials/misc/hunspell-dictionaries-path.adoc index d335cf899a..037c70c892 100644 --- a/modules/ROOT/partials/misc/hunspell-dictionaries-path.adoc +++ b/modules/ROOT/partials/misc/hunspell-dictionaries-path.adoc @@ -3,7 +3,7 @@ The `+ephox.spelling.hunspell-dictionaries-path+` setting is used to define the Requirements: * The directory containing the Hunspell dictionaries must conform to the file structure defined in xref:self-hosting-hunspell.adoc#hunspell-dictionary-storage-for-spell-checker[Hunspell dictionary storage for Spell Checker]. -* The directory containing the Hunspell dictionaries must be on the same server machine (or docker container) as the java service. +* The directory containing the Hunspell dictionaries must be mounted as a volume in the container. {companyname} recommends storing the Hunspell dictionaries in a similar location to the `+application.conf+` file. For example, if `+application.conf+` is in a directory called `+/opt/ephox+`, the Hunspell dictionaries should be stored in the subdirectory `+/opt/ephox/hunspell-dictionaries+`. diff --git a/modules/ROOT/partials/misc/setting-the-license.adoc b/modules/ROOT/partials/misc/setting-the-license.adoc index 57f6e78eb5..0ee4d87229 100644 --- a/modules/ROOT/partials/misc/setting-the-license.adoc +++ b/modules/ROOT/partials/misc/setting-the-license.adoc @@ -2,7 +2,7 @@ === Use {productname} with the GPLv2+ license -If the developer intends to self-host {productname} under the GPL license, they can set the `license_key` config option to 'gpl'. Case sensitivity does not matter. +If the developer intends to self-host {productname} under the GPL license and agree to its terms, they can set the `license_key` config option to 'gpl'. Case sensitivity does not matter. ==== Example @@ -18,12 +18,30 @@ tinymce.init({ If the developer intends to self-host {productname} under a commercial license, a valid license key must be provided. Customers who have purchased a self-hosted-eligible license for {productname} will find their license key in the link:https://www.tiny.cloud/auth/login/[account portal]. To purchase a commercial license, see available options on the link:{pricingpage}/[pricing page]. +The {productname} 8 commercial license key will have a `+T8LK:+` prefix. + ==== Example [source,js] ---- tinymce.init({ selector: 'textarea', // change this value according to your HTML - license_key: '' + license_key: 'T8LK:...' +}); +---- + +=== Use {productname} under GPL with Premium Features + +If the developer intends to self-host the {productname} editor under the GPL license and agree to its terms, whilst also self-hosting premium features, a valid license key must be provided. Customers who have purchased a self-hosted-eligible license for {productname} will find their license key in the link:https://www.tiny.cloud/auth/login/[account portal]. To purchase a commercial license, see available options on the link:{pricingpage}/[pricing page]. + +This type of license key is specifically for open source projects that need premium features while maintaining GPL compliance. + +The {productname} 8 commercial license key will have a `+GPL+T8LK:+` prefix. + +[source,js] +---- +tinymce.init({ + selector: 'textarea', // change this value according to your HTML + license_key: 'GPL+T8LK:...' }); ---- diff --git a/modules/ROOT/partials/misc/spellchecker-languages.adoc b/modules/ROOT/partials/misc/spellchecker-languages.adoc index e6bc1bb04a..707b30d592 100644 --- a/modules/ROOT/partials/misc/spellchecker-languages.adoc +++ b/modules/ROOT/partials/misc/spellchecker-languages.adoc @@ -6,27 +6,26 @@ The following languages are supported for the Spell Checker plugin. All of the l [cols="2,^1,^1,^1",options="header"] |=== |Language |Code |Pre-packaged with {productname} |Supported Hunspell dictionaries -|Afrikaans (South Africa) |af_ZA |{cross} |{tick} -|English (Australia) |en_AU |{cross} |{tick} -|English (Canada) |en_CA |{cross} |{tick} -|English (United Kingdom) |en_GB, en_UK, en_BR |{tick} |{tick} -|English (United States) |en, en_US |{tick} |{tick} -|Medical English (US) |en-medical, en_US-medical |{tick} |{tick} -|Medical English (UK) |en_GB-medical |{tick} |{cross} +|Afrikaans (South Africa) |af-ZA |{cross} |{tick} +|English (Australia) |en-AU |{cross} |{tick} +|English (Canada) |en-CA |{cross} |{tick} +|English (United Kingdom) |en-GB, en-UK, en-BR |{tick} |{tick} +|English (United States) |en, en-US |{tick} |{tick} +|Medical English (US) |en-medical, en-US-medical |{tick} |{tick} |Danish |da |{tick} |{tick} -|Dutch |nl, nl_NL |{tick} |{tick} +|Dutch |nl, nl-NL |{tick} |{tick} |Finnish |fi |{tick} |{cross} |French |fr |{tick} |{tick} -|German |de, de_DE |{tick} |{tick} +|German |de, de-DE |{tick} |{tick} |Hungarian |hu |{cross} |{tick} -|Italian |it, it_IT |{tick} |{tick} -|Maori (New Zealand) |mi_NZ |{cross} |{tick} -|Norwegian Bokmål |nb, nb_NO |{tick} |{tick} +|Italian |it, it-IT |{tick} |{tick} +|Maori (New Zealand) |mi-NZ |{cross} |{tick} +|Norwegian Bokmål |nb, nb-NO |{tick} |{tick} |Norwegian Nynorsk |nn |{cross} |{tick} |Polish |pl |{cross} |{tick} -|Portuguese (Brazil) |pt, pt_BR |{tick} |{tick} -|Portuguese (Portugal) |pt_PT |{tick} |{tick} +|Portuguese (Brazil) |pt, pt-BR |{tick} |{tick} +|Portuguese (Portugal) |pt-PT |{tick} |{tick} |Spanish |es |{tick} |{tick} -|Swedish |sv, sv_SE |{tick} |{tick} -|Swedish (Finland) |sv_FI |{cross} |{tick} +|Swedish |sv, sv-SE |{tick} |{tick} +|Swedish (Finland) |sv-FI |{cross} |{tick} |=== \ No newline at end of file diff --git a/modules/ROOT/partials/misc/supported-application-servers.adoc b/modules/ROOT/partials/misc/supported-application-servers.adoc index 1e8aa7c9cc..9cbf13afa7 100644 --- a/modules/ROOT/partials/misc/supported-application-servers.adoc +++ b/modules/ROOT/partials/misc/supported-application-servers.adoc @@ -1,14 +1,6 @@ -* Eclipse Jetty: -** 9.4+ (with extended support) -* WebSphere Application Server (WAS) 8 or later -* Apache Tomcat: -+ --- -** 10 (See note below) -** 9+ -** 8.5.12+ -** 8.0.42+ -** 7.0.76+ --- -+ -IMPORTANT: Tomcat 10 will require WAR files to be placed in the _webapp-javaee_ directory rather than the _webapps_ directory due to the change to Jakarta servlets. Jetty 11 is not currently supported due to this change. +* Docker Engine: +** 24.0 or later (latest stable version recommended) +* Kubernetes: +** 1.31.x or later (latest stable version recommended) + +IMPORTANT: As of {productname} {productmajorversion}, server-side components are only supported via Docker containers. Java-based WAR file deployments have been deprecated and are no longer supported. diff --git a/modules/ROOT/partials/misc/ui-languages.adoc b/modules/ROOT/partials/misc/ui-languages.adoc index abe916ab5d..20fff45c2c 100644 --- a/modules/ROOT/partials/misc/ui-languages.adoc +++ b/modules/ROOT/partials/misc/ui-languages.adoc @@ -1,45 +1,52 @@ [[supported-languages]] == Supported Languages +[IMPORTANT] +==== +From {productname} {productmajorversion}, language codes use RFC5646 format with hyphens (-) instead of underscores (_). For backward compatibility, both formats are supported in {productname} {productmajorversion}, but underscore format will show console warning. + +Example: "{productname} language code "en_US" is deprecated, please use "en-US" instead.". +==== + ifeval::[{forModuleLoaders} == true] [cols="<2,^1,^2",options="header"] |=== | Language | Code | Filename | Arabic | ar | `+./langs/ar.js+` | Basque | eu | `+./langs/eu.js+` -| Bulgarian (Bulgaria) | bg_BG | `+./langs/bg_BG.js+` +| Bulgarian (Bulgaria) | bg-BG | `+./langs/bg-BG.js+` | Catalan | ca | `+./langs/ca.js+` -| Chinese (China) | zh_CN | `+./langs/zh_CN.js+` -| Chinese (Taiwan) | zh_TW | `+./langs/zh_TW.js+` +| Chinese (China) | zh-CN | `+./langs/zh-CN.js+` +| Chinese (Taiwan) | zh-TW | `+./langs/zh-TW.js+` | Croatian | hr | `+./langs/hr.js+` | Czech | cs | `+./langs/cs.js+` | Danish | da | `+./langs/da.js+` | Dutch | nl | `+./langs/nl.js+` | Finnish | fi | `+./langs/fi.js+` -| French (France) | fr_FR | `+./langs/fr_FR.js+` +| French (France) | fr-FR | `+./langs/fr-FR.js+` | German | de | `+./langs/de.js+` | Greek | el | `+./langs/el.js+` -| Hebrew (Israel) | he_IL | `+./langs/he_IL.js+` +| Hebrew (Israel) | he-IL | `+./langs/he-IL.js+` | Hindi | hi | `+./langs/hi.js+` -| Hungarian (Hungary) | hu_HU | `+./langs/hu_HU.js+` +| Hungarian (Hungary) | hu-HU | `+./langs/hu-HU.js+` | Indonesian | id | `+./langs/id.js+` | Italian | it | `+./langs/it.js+` | Japanese | ja | `+./langs/ja.js+` | Kazakh | kk | `+./langs/kk.js+` -| Korean (Korea) | ko_KR | `+./langs/ko_KR.js+` +| Korean (Korea) | ko-KR | `+./langs/ko-KR.js+` | Malay | ms | `+./langs/ms.js+` -| Norwegian Bokmål (Norway) | nb_NO | `+./langs/nb_NO.js+` +| Norwegian Bokmål (Norway) | nb-NO | `+./langs/nb-NO.js+` | Persian | fa | `+./langs/fa.js+` | Polish | pl | `+./langs/pl.js+` -| Portuguese (Brazil) | pt_BR | `+./langs/pt_BR.js+` -| Portuguese (Portugal) | pt_PT | `+./langs/pt_PT.js+` +| Portuguese (Brazil) | pt-BR | `+./langs/pt-BR.js+` +| Portuguese (Portugal) | pt-PT | `+./langs/pt-PT.js+` | Romanian | ro | `+./langs/ro.js+` | Russian | ru | `+./langs/ru.js+` | Slovak | sk | `+./langs/sk.js+` -| Slovenian (Slovenia) | sl_SI | `+./langs/sl_SI.js+` +| Slovenian (Slovenia) | sl-SI | `+./langs/sl-SI.js+` | Spanish | es | `+./langs/es.js+` -| Swedish (Sweden) | sv_SE | `+./langs/sv_SE.js+` -| Thai (Thailand) | th_TH | `+./langs/th_TH.js+` +| Swedish (Sweden) | sv-SE | `+./langs/sv-SE.js+` +| Thai (Thailand) | th-TH | `+./langs/th-TH.js+` | Turkish | tr | `+./langs/tr.js+` | Ukrainian | uk | `+./langs/uk.js+` | Vietnamese | vi | `+./langs/vi.js+` @@ -51,39 +58,39 @@ ifeval::[{forModuleLoaders} != true] | Language | Code | Arabic | ar | Basque | eu -| Bulgarian (Bulgaria) | bg_BG +| Bulgarian (Bulgaria) | bg-BG | Catalan | ca -| Chinese (China) | zh_CN -| Chinese (Taiwan) | zh_TW +| Chinese (China) | zh-CN +| Chinese (Taiwan) | zh-TW | Croatian | hr | Czech | cs | Danish | da | Dutch | nl | Finnish | fi -| French (France) | fr_FR +| French (France) | fr-FR | German | de | Greek | el -| Hebrew (Israel) | he_IL +| Hebrew (Israel) | he-IL | Hindi | hi -| Hungarian (Hungary) | hu_HU +| Hungarian (Hungary) | hu-HU | Indonesian | id | Italian | it | Japanese | ja | Kazakh | kk -| Korean (Korea) | ko_KR +| Korean (Korea) | ko-KR | Malay | ms -| Norwegian Bokmål (Norway) | nb_NO +| Norwegian Bokmål (Norway) | nb-NO | Persian | fa | Polish | pl -| Portuguese (Brazil) | pt_BR -| Portuguese (Portugal) | pt_PT +| Portuguese (Brazil) | pt-BR +| Portuguese (Portugal) | pt-PT | Romanian | ro | Russian | ru | Slovak | sk -| Slovenian (Slovenia) | sl_SI +| Slovenian (Slovenia) | sl-SI | Spanish | es -| Swedish (Sweden) | sv_SE -| Thai (Thailand) | th_TH +| Swedish (Sweden) | sv-SE +| Thai (Thailand) | th-TH | Turkish | tr | Ukrainian | uk | Vietnamese | vi diff --git a/modules/ROOT/partials/module-loading/bundling-vite-es6-npm_editor.adoc b/modules/ROOT/partials/module-loading/bundling-vite-es6-npm_editor.adoc index a26d8d2c51..150aef7df2 100644 --- a/modules/ROOT/partials/module-loading/bundling-vite-es6-npm_editor.adoc +++ b/modules/ROOT/partials/module-loading/bundling-vite-es6-npm_editor.adoc @@ -44,10 +44,11 @@ import contentCss from 'tinymce/skins/content/default/content.js'; export function render () { tinymce.init({ selector: 'textarea#editor', + license_key: 'gpl', plugins: 'advlist code emoticons link lists table', toolbar: 'bold italic | bullist numlist | link emoticons', skin_url: 'default', content_css: 'default', }); }; ----- \ No newline at end of file +---- diff --git a/modules/ROOT/partials/plugin-apis/suggestededits-apis.adoc b/modules/ROOT/partials/plugin-apis/suggestededits-apis.adoc new file mode 100644 index 0000000000..5801412599 --- /dev/null +++ b/modules/ROOT/partials/plugin-apis/suggestededits-apis.adoc @@ -0,0 +1,163 @@ +[cols="1,1,4",options="header"] +|=== +|Name |Arguments |Description +|getModel |N/A |Returns a JSON object representing the current model of the document. +|setModel | `+Object+` | Sets the current model of the document. +|resetModel |N/A |Generates a model from the current content and sets it as the current model, clearing all suggestions. +|hasChanges |N/A |Returns a boolean value indicating whether the document contains any suggested edits. +|=== + +.Examples + +[source,js] +---- +// Get the current model of the document +tinymce.activeEditor.plugins.suggestededits.getModel(); + +// Set current model of the document +tinymce.activeEditor.plugins.suggestededits.setModel(model); + +// Reset the model to the current content, clearing all suggestions +tinymce.activeEditor.plugins.suggestededits.resetModel(); + +// Check if document contains changes to be reviewed +tinymce.activeEditor.plugins.suggestededits.hasChanges(); +---- + +[[get_model]] +.`getModel` Example + +This example demonstrates how to submit the current document and model to a server, to ensure they are saved synchronously. The current model is retrieved using the `getModel` API. + +[source,js] +---- +tinymce.init({ + selector: 'textarea#suggestededits', // change this value according to your HTML + plugins: 'suggestededits', + toolbar: 'suggestededits save', + suggestededits_model, + setup: (editor) => { + editor.ui.registry.addButton('save', { + text: 'Save', + onAction: () => { + // Get the current content of the editor + const content = editor.getContent(); + + // Get the current model of the document + const model = editor.plugins.suggestededits.getModel(); + + fetch(`/api/documents/${documentId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: content, + }) + .then((response) => response.json()) + .then((data) => console.log('Document saved:', data)) + .catch((error) => console.error('Error saving document:', error)); + + // Save the model to the server + fetch(`/api/models/${documentId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(model), + }) + .then((response) => response.json()) + .then((data) => console.log('Model saved:', data)) + .catch((error) => console.error('Error saving model:', error)); + } + }); + } +}); + +---- + +[[set_model]] +.`setModel` Example + +This example demonstrates how to set the model and the document in the editor, after fetching them from a server. The `setModel` method sets the current model of the document and the editor content, as generated from that model. + +[source,js] +---- +tinymce.init({ + selector: 'textarea#suggestededits', // change this value according to your HTML + plugins: 'suggestededits', + toolbar: 'suggestededits', + setup: (editor) => { + editor.on('init', () => { + // Fetch and set the suggested edits model + fetch(`/api/models/${documentId}`) + .then((response) => response.json()) + .then((model) => { + editor.plugins.suggestededits.setModel(model); + }) + .catch((error) => console.error(`Error fetching model for ${documentId}:`, error)); + }); + } +}); +---- + +[[reset_model]] +.`resetModel` Example + +This example demonstrates how to reset the model to the current content of the editor, clearing all suggestions. The `resetModel` method generates a new model from the current content and sets it as the current model. + +[source,js] +---- +tinymce.init({ + selector: 'textarea#suggestededits', // change this value according to your HTML + plugins: 'suggestededits', + toolbar: 'suggestededits clearsuggestions', + setup: (editor) => { + editor.ui.registry.addButton('clearsuggestions', { + text: 'Clear Suggestions', + onAction: () => { + // Reset the model to the current content, clearing all suggestions + editor.plugins.suggestededits.resetModel(); + + editor.notificationManager.open({ + text: 'All suggestions have been cleared.', + type: 'info', + timeout: 5000, + }); + } + }); + } +}); +---- + +[[has_changes]] +.`hasChanges` Example + +This example demonstrates how to check if there are any changes in the document that need to be reviewed before saving. The `hasChanges` method is used to determine if there are any unreviewed edits. + +[source,js] +---- +tinymce.init({ + selector: 'textarea#suggestededits', // change this value according to your HTML + plugins: 'suggestededits', + toolbar: 'suggestededits', + setup: (editor) => { + editor.ui.registry.addButton('save', { + text: 'Save', + onAction: () => { + // Get the current model of the document + const hasChanges = editor.plugins.suggestededits.hasChanges(); + + if (hasChanges) { + editor.notificationManager.open({ + text: 'There are changes to be reviewed.', + type: 'warning', + timeout: 5000, + }); + } else { + // Insert save logic here + } + } + }); + } +}); +---- diff --git a/modules/ROOT/partials/plugins/comments-open-sidebar.adoc b/modules/ROOT/partials/plugins/comments-open-sidebar.adoc index 03cbc525aa..ce46de407e 100644 --- a/modules/ROOT/partials/plugins/comments-open-sidebar.adoc +++ b/modules/ROOT/partials/plugins/comments-open-sidebar.adoc @@ -30,7 +30,7 @@ tinymce.init({ selector: 'textarea', // change this value according to your html plugins: 'tinycomments', tinycomments_mode: 'embedded', - tinycomments_author: currentAuthor, + user_id: currentAuthor, tinycomments_can_resolve: canResolveCommentsCallback, sidebar_show: 'showcomments' }); diff --git a/modules/ROOT/partials/plugins/spellchecker-content_langs.adoc b/modules/ROOT/partials/plugins/spellchecker-content_langs.adoc index f7d077b457..8109297d3d 100644 --- a/modules/ROOT/partials/plugins/spellchecker-content_langs.adoc +++ b/modules/ROOT/partials/plugins/spellchecker-content_langs.adoc @@ -22,29 +22,28 @@ tinymce.init({ toolbar: 'language', plugins: 'tinymcespellchecker', content_langs: [ - { title: 'Afrikaans (South Africa)', code: 'af_ZA', customCode: 'af_ZA' }, - { title: 'English (Australia)', code: 'en_AU' }, - { title: 'English (Canada)', code: 'en_CA' }, - { title: 'English (United Kingdom)', code: 'en_GB' }, - { title: 'English (United States)', code: 'en_US' }, - { title: 'Medical English (US)', code: 'en_US', customCode: 'en_US-medical' }, - { title: 'Medical English (UK)', code: 'en_GB', customCode: 'en_GB-medical' }, + { title: 'Afrikaans (South Africa)', code: 'af-ZA', customCode: 'af-ZA' }, + { title: 'English (Australia)', code: 'en-AU' }, + { title: 'English (Canada)', code: 'en-CA' }, + { title: 'English (United Kingdom)', code: 'en-GB' }, + { title: 'English (United States)', code: 'en-US' }, + { title: 'Medical English (US)', code: 'en-US', customCode: 'en-US-medical' }, { title: 'Danish', code: 'da' }, - { title: 'Dutch', code: 'nl_NL' }, + { title: 'Dutch', code: 'nl-NL' }, { title: 'Finnish', code: 'fi' }, { title: 'French', code: 'fr' }, - { title: 'German', code: 'de_DE' }, + { title: 'German', code: 'de-DE' }, { title: 'Hungarian', code: 'hu' }, - { title: 'Italian', code: 'it_IT' }, - { title: 'Maori (New Zealand)', code: 'mi_NZ' }, - { title: 'Norwegian Bokmål', code: 'nb_NO' }, + { title: 'Italian', code: 'it-IT' }, + { title: 'Maori (New Zealand)', code: 'mi-NZ' }, + { title: 'Norwegian Bokmål', code: 'nb-NO' }, { title: 'Norwegian Nynorsk', code: 'nn' }, { title: 'Polish', code: 'pl' }, - { title: 'Portuguese (Brazil)', code: 'pt_BR' }, - { title: 'Portuguese (Portugal)', code: 'pt_PT' }, + { title: 'Portuguese (Brazil)', code: 'pt-BR' }, + { title: 'Portuguese (Portugal)', code: 'pt-PT' }, { title: 'Spanish', code: 'es' }, - { title: 'Swedish', code: 'sv_SE' }, - { title: 'Swedish (Finland)', code: 'sv_FI' } + { title: 'Swedish', code: 'sv-SE' }, + { title: 'Swedish (Finland)', code: 'sv-FI' } ], }); ---- \ No newline at end of file diff --git a/modules/ROOT/partials/security/sanitizing-html-input-and-protecting-against-xss-attacks-dom-parser-and-dom-purify.adoc b/modules/ROOT/partials/security/sanitizing-html-input-and-protecting-against-xss-attacks-dom-parser-and-dom-purify.adoc index 4038fe8c91..d1d9fbe6e3 100644 --- a/modules/ROOT/partials/security/sanitizing-html-input-and-protecting-against-xss-attacks-dom-parser-and-dom-purify.adoc +++ b/modules/ROOT/partials/security/sanitizing-html-input-and-protecting-against-xss-attacks-dom-parser-and-dom-purify.adoc @@ -48,6 +48,7 @@ As of {productname} 6.4, however, it is possible to turn DOMPurify off using the NOTE: `xss_sanitization` is set to `true` by default. That is, {productname} specifically sets the `xss_sanitization` option to `true`, even if this option is not declared in a {productname} configuration. +[[xss_sanitization-option]] *Type:* `+Boolean+` *Default value:* `+true+` @@ -65,3 +66,8 @@ tinymce.init({ ---- WARNING: Turning DomPurify off leaves {productname}, and any application using {productname}, extremely vulnerable to XSS attacks. Only turn DomPurify off if alternative and equivalently capable HTML and XML sanitization and XSS protections are in place. + +[NOTE] +==== +{productname} {release-version} upgrades DOMPurify to version 3.2.6 and enables the `SAFE_FOR_XML` flag by default. This enforces stricter sanitization, which may remove or alter content that previously passed in {productname} 7. For more information see xref:migration-from-7x.adoc#dompurify-update-breaking-change[Migration Guide] +==== diff --git a/modules/ROOT/partials/toolbar-button-ids/suggestededits-toolbar-buttons.adoc b/modules/ROOT/partials/toolbar-button-ids/suggestededits-toolbar-buttons.adoc new file mode 100644 index 0000000000..83f8b4fb4c --- /dev/null +++ b/modules/ROOT/partials/toolbar-button-ids/suggestededits-toolbar-buttons.adoc @@ -0,0 +1,7 @@ +[cols="1,3",options="header"] +|=== +|Toolbar button identifier |Description + +|`+suggestededits+` |Opens the Review edits view. +|`+suggestededits-label+` |Opens the Review edits view (this button uses text instead of an icon). +|=== \ No newline at end of file