### TinyMCE field editor for enity-front, with links to DatoCMS records

[

**This is a Community Plugin!** Learn how create your own plugin, or copy and remix existing ones in our documentation

](https://www.datocms.com/docs/plugin-sdk/introduction.md)

## datocms-plugin-enity-tinymce

The TinyMCE field editor for enity-front's DatoCMS projects: a fork of DatoCMS's [`datocms-plugin-tinymce-rich-text`](https://github.com/datocms/plugins/tree/master/rich-text-tinymce) (ISC) whose link button (and Ctrl+K) opens one dialog, a DatoCMS modal: link to an **external URL**, a **page** or an **article**, the last two chosen in DatoCMS's own record picker. It links the selection, or inserts the text to display (by default the record's title) when nothing is selected; on an existing link it edits or removes it.

Every TinyMCE field uses it (migration [`0118-tinymce-record-links.ts`](https://www.datocms.com/marketplace/datocms/migrations/0118-tinymce-record-links.ts)). Published to npm, from where DatoCMS's plugin CDN serves it.

## How an internal link works

| Where | What |
| --- | --- |
| Editor (`src/recordLinks.ts`) | Stores `<a href>`: the record, not its URL, so the link survives a slug change. |
| Site (`src/lib/datocms/recordLinks.ts`) | `performRequest` finds those hrefs in any response, looks the records' slugs up in the same locale and draft state, and writes `/&lt;slug&gt;/`. A record that is gone, unpublished or has no slug in the locale leaves its link as plain text. |

The linkable models are `page` and `article` (`LINKABLE_MODELS`), the site's routable models; add a model in both places.

Also differs from the original: `convert_urls`/`relative_urls` are off, so a hand-typed `/slug` is kept as written instead of being rewritten relative to the plugin's own URL; the toolbar's heading picker (`blocks`, which the original's `formatselect` no longer renders in TinyMCE 6+) offers TinyMCE's default paragraph, heading 1–6 and preformatted formats; TinyMCE, its skins and plugins are bundled, so the editor loads nothing from Tiny's cloud.

## Files

| File | Responsibility |
| --- | --- |
| `src/main.tsx` | `connect()`: declares the `tinymce` manual field extension (the original's id, so a field switches by plugin id alone) and renders it. |
| `src/FieldEditor.tsx` | The editor: toolbar and `init` options, two-way sync with the field value, iframe height. |
| `src/recordLinks.ts` | The link button and Ctrl+K, applying the dialog's result in the editor, and the `dato:item/&lt;id&gt;` format. |
| `src/LinkModal.tsx` | The link dialog: link type, URL or record (record picker), text, new tab, remove. An existing record link is looked up with the editor's token to show its title. |
| `src/uploads.ts` | Insert image from the media library, and uploading pasted images. |
| `src/tinymce.ts` | The bundled TinyMCE: models, theme, icons, skins and plugins. |

## Security

-   The plugin runs in a sandboxed iframe on DatoCMS's plugin CDN, a different origin from the CMS: it can't read the CMS session, only use the SDK calls DatoCMS hands it (read and write its own field, open pickers).
-   It asks for the `currentUserAccessToken` permission, as the original does, to upload pasted or dropped images to the media library and to show the title of the record an existing link points at. With it, the plugin can call the API as the editor using it, so whoever can publish this package can act as an editor. Keep publishing in the workflow below, with 2FA on the npm account.
-   Whatever the field holds, the site renders it through `sanitizeRichHtml`.

## Develop, publish, roll out

```bash
cd plugins/datocms-tinymcepnpm install   # its own workspace root: never touches the app's lockfilepnpm dev       # http://localhost:5173, for a private plugin's entry point URLpnpm build     # tsc + vite → dist/
```

To try a change in DatoCMS before publishing, install a private plugin on a sandbox environment with `http://localhost:5173` as its entry point and switch a field to it.

1.  Bump `version` in `package.json` in the PR.
2.  After merge, run **Actions → TinyMCE plugin → Run workflow** on the default branch. It publishes with npm trusted publishing and provenance. The first version is published by hand (`npm publish --access public`), then the repository and `tinymce-plugin.yml` are added as the package's trusted publisher on npmjs.com.
3.  In each brand's project, update the plugin under Settings → Plugins. Until then editors keep the previous version, which is also the rollback.