Importing existing HTML into a document.
This is the migration path. An application that already stores rich text as
HTML — in a :string column, from another editor, from a feed — has to get
that content into the schema before any of the rest of Coelho applies to
it:
{:ok, document} = Coelho.HTML.from_html(post.body_html)
post
|> Ecto.Changeset.change(%{body: document})
|> Repo.update()Requires Floki, declared as an optional dependency: the parser is only needed on this path, and the document core has no dependencies at all.
What the import does with markup it does not know
Importing foreign HTML is not validation, and failing on the first surprise would make it useless. The rules are deliberate:
- an element the schema has no rule for is transparent — it
disappears and its children take its place, so a
<div>wrapper or a<span class="fancy">does not cost you the text inside it <script>,<style>,<head>,<template>and<noscript>are dropped with their content- an element whose attributes fail the schema's validators — an
<img>with nosrc, an<a href="javascript:…">— is treated as unknown, so the link text survives while the link does not - inline content in a place that demands blocks is wrapped in the
schema's first suitable block, which is how a bare
Helloat the top level becomes a paragraph - whitespace is collapsed as HTML collapses it, and runs of whitespace between blocks are dropped
What comes out is a validated, normalised document, or {:error, errors}
if what remained still does not fit the schema.
Teaching a schema to import
Each node and mark declares the tags it comes from, in the same spirit as
the parseDOM rules on the browser side:
paragraph: [content: "inline*", group: "block", parse: ["p"]]
heading: [
content: "inline*",
group: "block",
parse: [{"h1", %{"level" => 1}}, {"h2", %{"level" => 2}}]
]
link: [parse: [{"a", &Coelho.HTML.take(&1, ~w(href title))}]]A rule is a tag, optionally paired with the attributes to give the node: a
fixed map, a function of the element's HTML attributes, or a function of
those and the element's text. Rules are tried in declaration order, nodes
before marks, and a rule whose attributes fail the schema does not match —
which is how <span data-user-id="7"> becomes a mention while every other
span stays a span.
Summary
Functions
Converts HTML into a validated document.
Keeps the named HTML attributes, dropping those the element does not carry.
Types
Functions
@spec from_html(String.t(), Coelho.Schema.t()) :: {:ok, map()} | {:error, term()}
Converts HTML into a validated document.
Keeps the named HTML attributes, dropping those the element does not carry.
Handy inside a parse rule: {"a", &Coelho.HTML.take(&1, ~w(href title))}.