Install and configure¶
Installation¶
Install with the explicit 1.0.x-dev constraint this development release needs:
composer require 'drupal/blocknote:1.0.x-dev'
drush pm:install blocknote
Naming 1.0.x-dev on the command line sets Composer's per-package stability, so no minimum-stability change is needed in your project's own composer.json. After the first stable release, this becomes composer require drupal/blocknote.
Installing the module offers two text formats as optional configuration: BlockNote, which names its fifteen blocks explicitly and permits links to content of every kind of node, and BlockNote snippet, which holds one paragraph, permits bold, italic, strikethrough, subscript, superscript and links, and offers no alignment and no colors, for a caption, a byline or a summary; the Single paragraph section below describes the restriction. Each is created only if a format with its own id, blocknote or blocknote_snippet, does not already exist. BlockNote needs the Node module for its content links, so on a site without Node it is created when Node is installed, and BlockNote snippet does not wait. A site that installed the module before BlockNote snippet shipped gets it, with its editor and on the same terms, from the module's database updates, run at /update.php or with Drush. A site that already owns a format under either id keeps it as it is, and the BlockNote editor is not attached to it: an editor is installed for a shipped format alone. No role is granted permission to use either: deciding who may write in a format is the site's decision, and a field created through the BlockNote snippet entry stays unusable until some role is granted the Use the BlockNote snippet text format permission. Installing the media submodule later does not change either shipped format, as the Media section describes. A format added in the user interface is a different matter: its block list starts with every installed block ticked, Media among them, so read that list before saving a new format.
Configuration¶
Four steps take a site from "module installed" to "an author can type".
1. Create or choose a text format¶
Go to Administration › Configuration › Content authoring › Text formats and editors (/admin/config/content/formats). Use the shipped BlockNote format, or add a format and set its Text editor to BlockNote. With BlockNote chosen as the text editor, the form shows the module's settings in one set of tabs under the editor select, Blocks, Inline content, Block styles, Text, a tab for each block or inline content type that has settings, and Editor interface, and hides the other filters, which a BlockNote format cannot enable; one that is still on stays visible so it can be turned off.
A BlockNote format has exactly one enabled filter, BlockNote document, and no other. The renderer is the sanitizer: it never echoes the stored text, it builds markup from the parsed document. An HTML filter added after it would be filtering markup the module already decided on, so the format form refuses any other filter in a BlockNote format, and refuses a BlockNote filter in a format whose editor is something else. The same rule is a validation constraint on the text format itself, but core validates a configuration entity only when a caller asks it to: the format form asks, and configuration import, recipes, config actions and programmatic saves do not. A format written by one of those can therefore fail the definition, and an item using it renders nothing and logs the refusal rather than putting the stored JSON on the page. The BlockNote filter refuses a format that enables another filter on its own too, so even a direct call to check_markup() renders nothing and logs the refusal.
The filter's settings are the whole content policy, Blocks, Inline content and Block styles each on the tab of that name and the rest on the Text tab:
- Blocks: which of the block types this format's authors may use. Paragraph cannot be disabled. BlockNote names that type literally: it is the block Enter creates and the block the editor keeps at the end of every document, so the client keeps it in its schema whatever the format says, and a format without it would only produce documents the server refuses. The form refuses to turn it off, and a format written by code, config import or a recipe gets it added back.
- Inline content: which of the inline content types this format's authors may use. The module ships one of its own, the content link (see Links to content), and another module can add more; nothing on the tab is enabled until it is ticked.
- Block styles: the styles an author may give a block, described below.
- Styles: bold, italic, underline, strikethrough, inline code, subscript and superscript.
- Text colours and Background colours: the nine palette names.
- Alignments: left, center, right and justify, for blocks and table cells; left is the default and is always accepted.
- Links, whether a permitted link may open in a new tab, and the URL schemes a link may point at:
http,https,mailtoandtel.
Three blocks have settings of their own, each on a tab named after the block that is shown while the block is ticked: Heading (the permitted levels, 2 to 6 by default because the page already renders the entity's title as its h1, and whether collapsible headings are allowed), Code (the languages authors may pick from, one per line as id|Label, with a button that fills in about thirty ids highlight.js and Prism both read, and the default language, chosen from Plain text and the listed languages, a language just typed or filled in among them at once), and Table (whether cells may be merged, whether leading rows and columns may be headers, and whether cells may be colored).
A Single paragraph section sits on the Blocks tab as well, for a field that holds a caption, a byline, a pull quote or similar short text rather than a document. Ticking Restrict this format to a single paragraph narrows this format, wherever it is read, to the paragraph block alone: the enabled blocks list above, and the settings tab of every block in it, are hidden while it is ticked and reappear exactly as they were left if it is unticked again. Two more controls open with it. Element chooses what the paragraph renders as on the page: a <p>, the default; a <span>, with the same classes; or no element at all, which drops the paragraph's classes along with it, so a format that also wants alignment or block colors on its paragraph needs a <p> or a <span> to carry them. In the editor, Enter inserts nothing, and the first press after the editor gains focus is announced to screen readers; a paste of several blocks is joined into the one paragraph, and the formatting toolbar offers no block type select and no Nest block or Unnest block button. Permit line breaks, on by default, decides whether Shift+Enter inserts a break and a pasted newline survives as one; off, neither key inserts anything and a pasted newline is collapsed to a space instead, which is the setting for a field such as a title or a name where a second line should never appear. A document already stored with more than one block, or with a paragraph carrying children, or with a line break the format does not permit, is refused on its next save once the restriction is on. The editor opens it as stored; once a save has been refused, a document of more than one block is shown joined into one paragraph, with a line break where one block ended, or a space where breaks are off, and the next save stores it that way. Nothing already on the page is affected, because the restriction changes what a save accepts, not what an already stored document shows.
The Block styles tab lists the styles an author may give a block, one per line, in the syntax of the CKEditor 5 Style plugin with a block type in place of the element: paragraph.lead|Lead gives paragraphs a style labeled Lead that carries the class lead, and heading.smaller.muted|Quiet heading gives headings one that carries two classes. A class is made of letters, digits, hyphens and underscores, and may not start with bn-, blocknote- or ProseMirror, which the editor and the module use themselves; a block carries one style at most, so a combination is a style of its own with several classes. The form refuses a line naming a block type the format does not enable, or a table, which cannot have styles yet; a format restricted to a single paragraph takes lines for the paragraph alone, and none while its Element is none, which leaves the paragraph no element to carry a class. Block types in this format, under the list, gives the machine name of each block type the format enables, with a button that copies it. On the page a block's style is its classes on the block's outer element, after the module's own, for the theme to style: the module ships no CSS for them, and docs/rendering.md section 3 says where they go. A block stores the classes of its style, not the label, so a label may change freely, while changing a line's classes or removing it takes the style off the page of every block that carries it. The editor opens such a document without the style, under the notice that names what came off, and a save of a document carrying a style the format does not list is refused.
In the editor a block type with styles gets a Style select in the formatting toolbar, right after the block type select, which shows the label of the block's style, or Default style when it has none; its first item, Default style, takes the style off, and with several blocks selected it offers only the styles their types share, and reads Multiple styles when the blocks carry different ones. The Alt+F10 block menu, and the menu the side menu's drag handle opens, offer the same choices in a Style group. Changing a block's type keeps its style only where the new type lists the same classes, and a style pasted in from outside is checked the same way. The editor puts a style's classes on the block too, but has no CSS for them unless the default theme names stylesheets under blocknote-stylesheets in its .info.yml, as it names CKEditor 5's under ckeditor5-stylesheets:
blocknote-stylesheets:
- css/blocknote-editor.css
A path is relative to the theme, to the Drupal root when it starts with /, or an external address; a base theme's come first. They load on every page the editor is on, so each rule is scoped to the editor and targets the classes on the block's element, .bn-editor .bn-block-content.lead, or, for a block rendered through React such as a media block, on its wrapper one level further out, .bn-editor .react-renderer.boxed. Without such a stylesheet the select is the only sign of a style in the editor. A theme's .info.yml is cached, so clear the caches after adding the key; saving another default theme needs no cache clear.
Anything the policy forbids is refused when the document is saved, naming what was refused, rather than being stripped on the way out. The editor keeps to the same policy while the author works, so little reaches that refusal: a style, a color, a link, a table header or a cell color the format does not permit does not stay in the document, whether it was typed, chosen from a menu or pasted, and a stored document carrying formatting the format has since stopped permitting opens without it, under a notice naming what came off. The same notice names, apart, formatting the editor cannot show together on one piece of text, which no format permits: inline code with any other style, a color or a link, and subscript with superscript. The editor leaves off the code, or the subscript, and a save carrying the combination is refused. The notice says which of two things a save does: after any edit it stores the document as shown, while a save with no edit submits the stored document, which is refused by name; once a save has been refused, the document as shown is what the next save stores. A protocol-relative URL in a link, which carries no scheme of its own, is permitted only when both http and https are. The URL of an image, video, audio or file block is checked on save as well, against the narrower allowlist the public output uses: http, https, a root-relative path or a scheme-less relative path, and never mailto, tel or a protocol-relative URL, because a media source is fetched by the browser without the visitor choosing to follow it.
2. Grant the permission¶
Go to Administration › People › Permissions (/admin/people/permissions) and grant Use the BlockNote text format to the roles that should write in it. Until some role has it, the format exists but nobody is offered it, and a BlockNote field shows an author nothing to write in.
3. Add a field¶
On any fieldable bundle, add a field of type BlockNote document (blocknote_document). The field type list offers a second entry, BlockNote snippet, for a caption, a byline or a pull quote rather than a document: picking it creates the same blocknote_document field, with Allowed text formats preconfigured to the shipped BlockNote snippet format (the single-paragraph restriction described in step 1 above) rather than left open, and with Allow uploads off, because that format permits the paragraph block alone and so has nowhere to put a file. Every listing still names the field blocknote_document. Nobody can write in it until a role holds Use the BlockNote snippet text format, granted the same way as in step 2 above. Its settings are:
| Setting | Where | What it does |
|---|---|---|
| Allowed text formats | Field settings | Restricts which BlockNote formats this field offers. Leave every box clear to offer every BlockNote format the author may use. Only BlockNote formats are ever offered, and a format outside this list is refused on a programmatic or JSON:API write as well as in the form. |
| Rows | Widget settings | The height of the fallback textarea, which is what an author sees before the editor loads or with JavaScript off. |
| Placeholder | Widget settings | Text shown in an empty document until the author starts writing, and the placeholder of the fallback textarea. Empty by default. It is translatable configuration, like any widget placeholder in core. |
| Read-only | Widget settings | Renders the stored document in place of the editor. The field loads no editor, nobody can edit it, and the form keeps the stored document whatever is submitted. See Read-only fields. |
| Attach the default styles | Formatter settings | Attaches the module's optional public stylesheet. On by default. See What the public page contains. |
| File uploads | Field settings | Whether an author may upload files from inside a document, where they go and what is accepted. See File uploads. |
The field has two formatters. Default shows the whole document. Trimmed shows its start, for a teaser or a card, through the same text format and the same markup: a Limit, which counts characters or words, while images, media and other blocks without text count nothing; where to Cut, after whole blocks only, at a word boundary or at the exact character, though a table and a code block are never cut, and a heading only when nothing but headings would be shown otherwise; the Text where it cuts, an ellipsis by default, which may be empty; and an optional Summary field, another BlockNote field of the bundle shown whole in place of the trimmed document whenever it has content, which is what core's Summary or trimmed did with a summary. A trimmed document never shows a table of contents. Both formatters offer Attach the default styles. docs/rendering.md section 12 says exactly what a trimmed document keeps.
4. Choose what the editor's interface offers¶
The editor's own settings are on the Editor interface tab and are eight toggles, all on by default: Formatting toolbar, Block side menu, Slash menu, Link toolbar, Table handles, File panel, Paste as Markdown and Click into links. The shipped BlockNote snippet format has Block side menu, Slash menu, Table handles and File panel off. The first six decide which surfaces the editor renders, and nothing else. Which blocks exist and what may be written is the filter's policy, not this.
Paste as Markdown, on by default, reads pasted plain text as Markdown, the way BlockNote does on its own; switch it off for authors who paste text that happens to start with # or -.
Click into links, on by default, makes a click on a link place the caret in its text, the way CKEditor 5 does, and Ctrl+click (Cmd+click on a Mac) open the link in a new tab; switch it off to restore BlockNote's own behavior, where a click opens the link. The link toolbar's Open link button is the keyboard route either way.
Read-only fields¶
A read-only widget puts the document on the form instead of an editor nobody may type in. The field keeps the label, the description and the required marker every widget shows, and under them the module renders the stored document the way it renders it on a public page: the same HTML, the same templates and the same text format. The format is the policy on the form as well, so a block it has stopped enabling, a link it forbids and a file the account may not see look the same here as they do to a visitor. A block the format no longer enables is not shown at all, where the editor would have carried it in a notice. docs/rendering.md is the contract for both.
The field brings that markup and the blocknote/content stylesheet to the page, and nothing else of this module: no editor JavaScript, no editor CSS, so a form whose BlockNote fields are all read-only loads no editor at all. The stylesheet is attached whichever way Attach the default styles is set, because that setting belongs to the public page.
Nothing about the field can be reached by a submission. The value, the format and the document's identity are submitted back as they were loaded, so a crafted request cannot write through the field, and on a multiple-value field the rows cannot be reordered or removed either.
The setting does not fill a field. A required field with no stored document refuses the save, with the message "This value should not be null.", because the form offers no way to write the document it asks for. Turn the setting on for fields that already hold what they should hold, or that something other than this form writes.
Uninstalling¶
Core refuses to uninstall BlockNote while a BlockNote document field exists, including a deleted field whose values have not been purged yet, and while a text format has the BlockNote document filter turned on. The steps, in order:
- If BlockNote Media is installed, untick Media and Media gallery in every format that enables them, then uninstall
blocknote_media. - Delete every BlockNote document field, at Manage fields for each bundle, and run cron until the deleted fields are purged.
- In every text format that has the BlockNote document filter on, set Text editor to None and untick BlockNote document in one save. Either change on its own is refused, because a format using the editor has to enable the filter and a format enabling the filter has to use the editor. Do not disable such a format instead: a disabled format whose BlockNote filter is still on is deleted when the module is uninstalled.
- Uninstall
blocknote. - Decide what to do with the formats step 3 left behind. A BlockNote format enables no other filter, so after step 3 it has none at all, and an enabled format with no filters prints whatever is typed into it unchanged. Every role granted permission to use that format keeps it, so disable the format, or revoke the permission from each of those roles, unless the site means to keep an unfiltered format.
Each text format stays, without the BlockNote filter, and the BlockNote editor configuration is deleted at step 3, as soon as the format's Text editor is set to None. Installing the module again later does not undo any of it: a format still holding the id blocknote is not replaced, and the editor is not attached to it, so such a format has to be given its Text editor and its BlockNote document filter back in one save before it is a BlockNote format again. The files authors uploaded stay on the site, and the usage the module recorded for them is removed as the fields are purged, so a private file that only a document used can no longer be downloaded. Purging sends no document events, so a module that mirrors documents elsewhere is not told that the purged documents are gone.