fix: ship the real DTD in the Pelican plugin
This commit is contained in:
parent
f54d350216
commit
884300f3cf
8 changed files with 483 additions and 74 deletions
|
|
@ -10,9 +10,10 @@ well-formed UTF-8 XML with the XHTML namespace on `<html>`. The plugin also
|
||||||
copies the Mews stylesheet into the output tree and gives themes a
|
copies the Mews stylesheet into the output tree and gives themes a
|
||||||
`MEWS_CSS` global plus the `mews/head.html` partial.
|
`MEWS_CSS` global plus the `mews/head.html` partial.
|
||||||
|
|
||||||
The bundled `pelican/plugins/mews/dtd/mews.dtd` is a minimal placeholder.
|
The subset comes from `pelican/plugins/mews/dtd/mews.dtd`, which is a copy of
|
||||||
Replace it with the real Mews Profile DTD when it is available; the plugin
|
the Mews Profile 0.1 DTD. Only element and attribute names are read from it, so
|
||||||
picks up the change without any code edit.
|
the plugin lists none of them in code: change the DTD and the allowlist changes
|
||||||
|
with it.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,87 +1,396 @@
|
||||||
<!-- Placeholder for the Mews subset definition. The real allowlist comes
|
<!-- Mews Profile 0.1 DTD
|
||||||
from the Mews Profile DTD; replace this file when it is available.
|
Published at mews.page/dtd/mews-0.1.dtd. Browsers and clients must not
|
||||||
Only element and attribute names are read; content models are
|
fetch this file.
|
||||||
illustrative. -->
|
|
||||||
|
|
||||||
<!ENTITY % common
|
XHTML Mobile Profile 1.2 restricted as in the Mews Profile
|
||||||
"id ID #IMPLIED
|
Specification 0.1 section 4, extended with lang and dir (section 3).
|
||||||
title CDATA #IMPLIED
|
Maintained by hand and checked against the XHTML Mobile 1.2 driver,
|
||||||
xml:lang NMTOKEN #IMPLIED">
|
xhtml-mobile12.dtd (OMA-SUP-DTD_xhtml_mobile12-V1_2-20080331-A), by
|
||||||
|
check_dtd.py. It cannot be generated by stripping the driver: the
|
||||||
|
driver declares no elements itself, attribute lists only ever add,
|
||||||
|
and the rules below do not exist in XHTML-MP to be kept.
|
||||||
|
|
||||||
<!ELEMENT html (head, body) >
|
This file is standalone: it declares every element and attribute
|
||||||
<!ATTLIST html
|
itself, so validation never depends on the Open Mobile Alliance's or
|
||||||
xmlns CDATA #FIXED "http://www.w3.org/1999/xhtml"
|
W3C's servers. Pages keep the XHTML-MP doctype (section 3.1); only
|
||||||
lang NMTOKEN #IMPLIED
|
validators use this file (section 9.1).
|
||||||
dir (ltr | rtl) #IMPLIED
|
|
||||||
%common;
|
Encoded here: the permitted elements and attributes (4.1), the head
|
||||||
|
rules (3.3), the body variant classes (5.3) and the ban on nested
|
||||||
|
tables (4.2). 51 elements are kept from XHTML Mobile 1.2. Dropped:
|
||||||
|
acronym, base, big, button, div, legend, noscript, object, param,
|
||||||
|
script, small, span, style, sub, sup, tt. Event attributes and the
|
||||||
|
style attribute are omitted entirely.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Datatypes (as in XHTML Modularization) -->
|
||||||
|
<!ENTITY % Text.datatype "CDATA" >
|
||||||
|
<!ENTITY % URI.datatype "CDATA" >
|
||||||
|
<!ENTITY % Number.datatype "CDATA" >
|
||||||
|
<!ENTITY % LanguageCode.datatype "NMTOKEN" >
|
||||||
|
<!ENTITY % Character.datatype "CDATA" >
|
||||||
|
|
||||||
|
<!-- Common attributes. id, title, xml:lang, lang and dir are permitted
|
||||||
|
on every element in the body (SPEC.md 4.1); lang and dir are the Mews
|
||||||
|
extensions over XHTML-MP (SPEC.md 3). -->
|
||||||
|
<!ENTITY % id.attrib "id ID #IMPLIED" >
|
||||||
|
<!ENTITY % title.attrib "title CDATA #IMPLIED" >
|
||||||
|
<!ENTITY % i18n.attrib
|
||||||
|
"xml:lang %LanguageCode.datatype; #IMPLIED
|
||||||
|
lang %LanguageCode.datatype; #IMPLIED
|
||||||
|
dir (ltr | rtl) #IMPLIED" >
|
||||||
|
<!ENTITY % Common.attrib "%id.attrib; %title.attrib; %i18n.attrib;" >
|
||||||
|
|
||||||
|
<!-- Inline classes -->
|
||||||
|
<!ENTITY % InlStruct.class "br" >
|
||||||
|
<!ENTITY % InlPhras.class "| em | strong | code | kbd | samp | var | q | cite | abbr | dfn" >
|
||||||
|
<!ENTITY % InlPres.class "| b | i" >
|
||||||
|
<!ENTITY % Anchor.class "| a" >
|
||||||
|
<!ENTITY % InlSpecial.class "| img" >
|
||||||
|
<!ENTITY % InlFormNoLabel.class "| input | select | textarea" >
|
||||||
|
<!ENTITY % InlForm.class "%InlFormNoLabel.class; | label" >
|
||||||
|
<!ENTITY % Inline.extra "" >
|
||||||
|
<!ENTITY % Inline.class "%InlStruct.class;
|
||||||
|
%InlPhras.class;
|
||||||
|
%InlPres.class;
|
||||||
|
%Anchor.class;
|
||||||
|
%InlSpecial.class;
|
||||||
|
%InlForm.class;
|
||||||
|
%Inline.extra;" >
|
||||||
|
<!ENTITY % InlNoAnchor.class "%InlStruct.class;
|
||||||
|
%InlPhras.class;
|
||||||
|
%InlPres.class;
|
||||||
|
%InlSpecial.class;
|
||||||
|
%InlForm.class;
|
||||||
|
%Inline.extra;" >
|
||||||
|
<!ENTITY % InlNoLabel.class "%InlStruct.class;
|
||||||
|
%InlPhras.class;
|
||||||
|
%InlPres.class;
|
||||||
|
%Anchor.class;
|
||||||
|
%InlSpecial.class;
|
||||||
|
%InlFormNoLabel.class;
|
||||||
|
%Inline.extra;" >
|
||||||
|
|
||||||
|
<!-- Block classes -->
|
||||||
|
<!ENTITY % Heading.class "h1 | h2 | h3 | h4 | h5 | h6" >
|
||||||
|
<!ENTITY % List.class "ul | ol | dl" >
|
||||||
|
<!ENTITY % BlkStruct.class "p" >
|
||||||
|
<!ENTITY % BlkPhras.class "| pre | blockquote | address" >
|
||||||
|
<!ENTITY % BlkPres.class "| hr" >
|
||||||
|
<!ENTITY % Table.class "| table" >
|
||||||
|
<!ENTITY % Form.class "| form" >
|
||||||
|
<!ENTITY % Fieldset.class "| fieldset" >
|
||||||
|
<!ENTITY % Block.extra "" >
|
||||||
|
<!ENTITY % BlkNoForm.class "%Heading.class;
|
||||||
|
| %List.class;
|
||||||
|
| %BlkStruct.class;
|
||||||
|
%BlkPhras.class;
|
||||||
|
%BlkPres.class;
|
||||||
|
%Fieldset.class;
|
||||||
|
%Block.extra;" >
|
||||||
|
<!ENTITY % BlkNoTable.class "%BlkNoForm.class;
|
||||||
|
%Form.class;
|
||||||
|
%Block.extra;" >
|
||||||
|
<!ENTITY % Blk.class "%BlkNoTable.class;
|
||||||
|
%Table.class;
|
||||||
|
%Block.extra;" >
|
||||||
|
<!ENTITY % FlowNoTable.class "%Inline.class;
|
||||||
|
| %BlkNoTable.class;" >
|
||||||
|
|
||||||
|
<!-- pre content model, taken verbatim from the driver. It excludes
|
||||||
|
%InlPres.class; and %InlSpecial.class;, so b, i and img are not
|
||||||
|
permitted in pre, as in XHTML Mobile 1.2. -->
|
||||||
|
<!ENTITY % pre.content
|
||||||
|
"( #PCDATA
|
||||||
|
| %InlStruct.class;
|
||||||
|
%InlPhras.class;
|
||||||
|
%Anchor.class;
|
||||||
|
%Inline.extra; )*"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!ELEMENT head (title, meta, link)* >
|
<!-- Document structure (SPEC.md 4.1) -->
|
||||||
<!ELEMENT title (#PCDATA) >
|
<!--
|
||||||
<!ATTLIST title %common; >
|
Only body uses %Blk.class;, so a table can appear directly in the
|
||||||
|
body and nowhere else, and tables cannot nest (SPEC.md 4.2). body may
|
||||||
|
carry one class from the variant list (SPEC.md 5.3).
|
||||||
|
-->
|
||||||
|
<!ELEMENT html ( head, body ) >
|
||||||
|
<!ATTLIST html
|
||||||
|
xmlns CDATA #FIXED "http://www.w3.org/1999/xhtml"
|
||||||
|
xml:lang %LanguageCode.datatype; #IMPLIED
|
||||||
|
lang %LanguageCode.datatype; #IMPLIED
|
||||||
|
dir (ltr | rtl) #IMPLIED
|
||||||
|
>
|
||||||
|
<!ELEMENT head ( title, meta, ( meta | link )* ) >
|
||||||
|
<!ELEMENT title ( #PCDATA )* >
|
||||||
|
<!ELEMENT body ( %Blk.class; )+ >
|
||||||
|
<!ATTLIST body
|
||||||
|
%Common.attrib;
|
||||||
|
class (mews-warm | mews-cool | mews-green | mews-mono) #IMPLIED
|
||||||
|
>
|
||||||
|
|
||||||
|
<!-- Head elements (SPEC.md 3.3) -->
|
||||||
|
<!--
|
||||||
|
The head may contain only title, meta and link. The title comes
|
||||||
|
first and at least one meta is required; the conformance marker is a
|
||||||
|
meta (SPEC.md 3.2), but a DTD cannot check its name and content
|
||||||
|
together, so validators confirm the marker as a rule check (SPEC.md
|
||||||
|
9.1). link rel is limited to the uses SPEC.md 3.3 names: the
|
||||||
|
stylesheet, the icon and feed links.
|
||||||
|
-->
|
||||||
<!ELEMENT meta EMPTY >
|
<!ELEMENT meta EMPTY >
|
||||||
<!ATTLIST meta
|
<!ATTLIST meta
|
||||||
name NMTOKEN #IMPLIED
|
name NMTOKEN #IMPLIED
|
||||||
content CDATA #REQUIRED
|
content CDATA #REQUIRED
|
||||||
>
|
>
|
||||||
|
|
||||||
<!ELEMENT link EMPTY >
|
<!ELEMENT link EMPTY >
|
||||||
<!ATTLIST link
|
<!ATTLIST link
|
||||||
rel CDATA #IMPLIED
|
rel (alternate | icon | stylesheet) #IMPLIED
|
||||||
href CDATA #IMPLIED
|
href %URI.datatype; #IMPLIED
|
||||||
type CDATA #IMPLIED
|
type CDATA #IMPLIED
|
||||||
title CDATA #IMPLIED
|
title CDATA #IMPLIED
|
||||||
%common;
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<!ELEMENT body (#PCDATA | h1 | h2 | h3 | h4 | h5 | h6 | p | ul | blockquote | pre | hr | a | img)* >
|
<!-- Headings (SPEC.md 4.1) -->
|
||||||
<!ATTLIST body %common; >
|
<!ELEMENT h1 ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST h1
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT h2 ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST h2
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT h3 ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST h3
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT h4 ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST h4
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT h5 ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST h5
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT h6 ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST h6
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
|
||||||
<!ELEMENT h1 (#PCDATA) >
|
<!-- Text blocks (SPEC.md 4.1) -->
|
||||||
<!ATTLIST h1 %common; >
|
<!ELEMENT p ( #PCDATA | %Inline.class; )* >
|
||||||
<!ELEMENT h2 (#PCDATA) >
|
<!ATTLIST p
|
||||||
<!ATTLIST h2 %common; >
|
%Common.attrib;
|
||||||
<!ELEMENT h3 (#PCDATA) >
|
>
|
||||||
<!ATTLIST h3 %common; >
|
<!ELEMENT blockquote ( %BlkNoTable.class; )* >
|
||||||
<!ELEMENT h4 (#PCDATA) >
|
<!ATTLIST blockquote
|
||||||
<!ATTLIST h4 %common; >
|
%Common.attrib;
|
||||||
<!ELEMENT h5 (#PCDATA) >
|
cite %URI.datatype; #IMPLIED
|
||||||
<!ATTLIST h5 %common; >
|
>
|
||||||
<!ELEMENT h6 (#PCDATA) >
|
<!ELEMENT pre %pre.content; >
|
||||||
<!ATTLIST h6 %common; >
|
<!ATTLIST pre
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT address ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST address
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT hr EMPTY >
|
||||||
|
<!ATTLIST hr
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
|
||||||
<!ELEMENT p (#PCDATA | em | strong | code | a | img | br)* >
|
<!-- Inline text (SPEC.md 4.1) -->
|
||||||
<!ATTLIST p %common; >
|
<!ELEMENT em ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST em
|
||||||
<!ELEMENT em (#PCDATA) >
|
%Common.attrib;
|
||||||
<!ATTLIST em %common; >
|
>
|
||||||
<!ELEMENT strong (#PCDATA) >
|
<!ELEMENT strong ( #PCDATA | %Inline.class; )* >
|
||||||
<!ATTLIST strong %common; >
|
<!ATTLIST strong
|
||||||
<!ELEMENT code (#PCDATA) >
|
%Common.attrib;
|
||||||
<!ATTLIST code %common; >
|
>
|
||||||
<!ELEMENT a (#PCDATA) >
|
<!ELEMENT code ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST code
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT kbd ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST kbd
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT samp ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST samp
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT var ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST var
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT cite ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST cite
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT abbr ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST abbr
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT dfn ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST dfn
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT b ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST b
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT i ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST i
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT q ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST q
|
||||||
|
%Common.attrib;
|
||||||
|
cite %URI.datatype; #IMPLIED
|
||||||
|
>
|
||||||
|
<!ELEMENT br EMPTY >
|
||||||
|
<!ATTLIST br
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT a ( #PCDATA | %InlNoAnchor.class; )* >
|
||||||
<!ATTLIST a
|
<!ATTLIST a
|
||||||
href CDATA #IMPLIED
|
%Common.attrib;
|
||||||
title CDATA #IMPLIED
|
href %URI.datatype; #IMPLIED
|
||||||
rel CDATA #IMPLIED
|
hreflang %LanguageCode.datatype; #IMPLIED
|
||||||
%common;
|
rel CDATA #IMPLIED
|
||||||
|
type CDATA #IMPLIED
|
||||||
|
accesskey %Character.datatype; #IMPLIED
|
||||||
>
|
>
|
||||||
<!ELEMENT img EMPTY >
|
<!ELEMENT img EMPTY >
|
||||||
<!ATTLIST img
|
<!ATTLIST img
|
||||||
src CDATA #IMPLIED
|
%Common.attrib;
|
||||||
alt CDATA #IMPLIED
|
src %URI.datatype; #REQUIRED
|
||||||
%common;
|
alt %Text.datatype; #REQUIRED
|
||||||
>
|
>
|
||||||
<!ELEMENT br EMPTY >
|
|
||||||
|
|
||||||
<!ELEMENT ul (li)+ >
|
<!-- Lists (SPEC.md 4.1) -->
|
||||||
<!ATTLIST ul %common; >
|
<!ELEMENT ul ( li )+ >
|
||||||
<!ELEMENT li (#PCDATA | p | ul)* >
|
<!ATTLIST ul
|
||||||
<!ATTLIST li %common; >
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT ol ( li )+ >
|
||||||
|
<!ATTLIST ol
|
||||||
|
%Common.attrib;
|
||||||
|
start %Number.datatype; #IMPLIED
|
||||||
|
>
|
||||||
|
<!ELEMENT dl ( dt | dd )+ >
|
||||||
|
<!ATTLIST dl
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT li ( #PCDATA | %FlowNoTable.class; )* >
|
||||||
|
<!ATTLIST li
|
||||||
|
%Common.attrib;
|
||||||
|
value %Number.datatype; #IMPLIED
|
||||||
|
>
|
||||||
|
<!ELEMENT dt ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST dt
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT dd ( #PCDATA | %FlowNoTable.class; )* >
|
||||||
|
<!ATTLIST dd
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
|
||||||
<!ELEMENT blockquote (p)* >
|
<!-- Tables (SPEC.md 4.2) -->
|
||||||
<!ATTLIST blockquote %common; >
|
<!--
|
||||||
<!ELEMENT pre (#PCDATA) >
|
As in the XHTML Mobile basic tables module, cells take
|
||||||
<!ATTLIST pre %common; >
|
%FlowNoTable.class; and never a table, so tables cannot nest
|
||||||
<!ELEMENT hr EMPTY >
|
(SPEC.md 4.2).
|
||||||
|
-->
|
||||||
|
<!ELEMENT table ( caption?, tr+ ) >
|
||||||
|
<!ATTLIST table
|
||||||
|
%Common.attrib;
|
||||||
|
summary %Text.datatype; #IMPLIED
|
||||||
|
>
|
||||||
|
<!ELEMENT caption ( #PCDATA | %Inline.class; )* >
|
||||||
|
<!ATTLIST caption
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT tr ( th | td )+ >
|
||||||
|
<!ATTLIST tr
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT th ( #PCDATA | %FlowNoTable.class; )* >
|
||||||
|
<!ATTLIST th
|
||||||
|
%Common.attrib;
|
||||||
|
rowspan %Number.datatype; "1"
|
||||||
|
colspan %Number.datatype; "1"
|
||||||
|
scope (row | col) #IMPLIED
|
||||||
|
>
|
||||||
|
<!ELEMENT td ( #PCDATA | %FlowNoTable.class; )* >
|
||||||
|
<!ATTLIST td
|
||||||
|
%Common.attrib;
|
||||||
|
rowspan %Number.datatype; "1"
|
||||||
|
colspan %Number.datatype; "1"
|
||||||
|
scope (row | col) #IMPLIED
|
||||||
|
>
|
||||||
|
|
||||||
|
<!-- Forms (SPEC.md 4.1, 4.2) -->
|
||||||
|
<!--
|
||||||
|
As in the XHTML MP forms module, form takes %BlkNoForm.class;, so
|
||||||
|
forms cannot nest. fieldset also takes %BlkNoForm.class;, so a
|
||||||
|
fieldset cannot reintroduce a form.
|
||||||
|
-->
|
||||||
|
<!ELEMENT form ( %BlkNoForm.class; )+ >
|
||||||
|
<!ATTLIST form
|
||||||
|
%Common.attrib;
|
||||||
|
action %URI.datatype; #REQUIRED
|
||||||
|
method (get | post) "get"
|
||||||
|
enctype CDATA "application/x-www-form-urlencoded"
|
||||||
|
>
|
||||||
|
<!ELEMENT fieldset ( #PCDATA | %Inline.class; | %BlkNoForm.class; )* >
|
||||||
|
<!ATTLIST fieldset
|
||||||
|
%Common.attrib;
|
||||||
|
>
|
||||||
|
<!ELEMENT label ( #PCDATA | %InlNoLabel.class; )* >
|
||||||
|
<!ATTLIST label
|
||||||
|
%Common.attrib;
|
||||||
|
for IDREF #IMPLIED
|
||||||
|
accesskey %Character.datatype; #IMPLIED
|
||||||
|
>
|
||||||
|
<!ELEMENT input EMPTY >
|
||||||
|
<!ATTLIST input
|
||||||
|
%Common.attrib;
|
||||||
|
type (text | password | checkbox | radio | submit | reset | hidden) "text"
|
||||||
|
name CDATA #IMPLIED
|
||||||
|
value CDATA #IMPLIED
|
||||||
|
checked (checked) #IMPLIED
|
||||||
|
size %Number.datatype; #IMPLIED
|
||||||
|
maxlength %Number.datatype; #IMPLIED
|
||||||
|
accesskey %Character.datatype; #IMPLIED
|
||||||
|
inputmode CDATA #IMPLIED
|
||||||
|
>
|
||||||
|
<!ELEMENT select ( optgroup | option )+ >
|
||||||
|
<!ATTLIST select
|
||||||
|
%Common.attrib;
|
||||||
|
name CDATA #IMPLIED
|
||||||
|
multiple (multiple) #IMPLIED
|
||||||
|
size %Number.datatype; #IMPLIED
|
||||||
|
>
|
||||||
|
<!ELEMENT optgroup ( option )+ >
|
||||||
|
<!ATTLIST optgroup
|
||||||
|
%Common.attrib;
|
||||||
|
label %Text.datatype; #REQUIRED
|
||||||
|
>
|
||||||
|
<!ELEMENT option ( #PCDATA )* >
|
||||||
|
<!ATTLIST option
|
||||||
|
%Common.attrib;
|
||||||
|
value CDATA #IMPLIED
|
||||||
|
selected (selected) #IMPLIED
|
||||||
|
>
|
||||||
|
<!ELEMENT textarea ( #PCDATA )* >
|
||||||
|
<!ATTLIST textarea
|
||||||
|
%Common.attrib;
|
||||||
|
name CDATA #IMPLIED
|
||||||
|
rows %Number.datatype; #REQUIRED
|
||||||
|
cols %Number.datatype; #REQUIRED
|
||||||
|
accesskey %Character.datatype; #IMPLIED
|
||||||
|
inputmode CDATA #IMPLIED
|
||||||
|
>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,15 @@ THEME = TESTS / "theme"
|
||||||
|
|
||||||
BASE_SETTINGS = {
|
BASE_SETTINGS = {
|
||||||
"ARTICLE_PATHS": [],
|
"ARTICLE_PATHS": [],
|
||||||
"MARKDOWN": {"output_format": "xhtml"},
|
# Tables and definition lists are both in the Mews subset, and both need
|
||||||
|
# an extension turned on before python-markdown will emit them.
|
||||||
|
"MARKDOWN": {
|
||||||
|
"output_format": "xhtml",
|
||||||
|
"extension_configs": {
|
||||||
|
"markdown.extensions.tables": {},
|
||||||
|
"markdown.extensions.def_list": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
"PAGE_PATHS": ["pages"],
|
"PAGE_PATHS": ["pages"],
|
||||||
"PAGE_SAVE_AS": "{slug}.xhtml",
|
"PAGE_SAVE_AS": "{slug}.xhtml",
|
||||||
"PAGE_URL": "{slug}.xhtml",
|
"PAGE_URL": "{slug}.xhtml",
|
||||||
|
|
|
||||||
35
pelican-mews/tests/fixtures/structure/expected.xhtml
vendored
Normal file
35
pelican-mews/tests/fixtures/structure/expected.xhtml
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Structure</title>
|
||||||
|
<meta name="mews-profile" content="0.1"/>
|
||||||
|
<meta name="viewport" content="width=device-width"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/mews-0.1.css"/></head>
|
||||||
|
<body>
|
||||||
|
<p>A table, an ordered list and a definition list: everything the placeholder
|
||||||
|
DTD used to throw away.</p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<th>Network</th>
|
||||||
|
<th>Band</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<td>GSM 900</td>
|
||||||
|
<td>900 MHz</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<ol>
|
||||||
|
<li>First</li>
|
||||||
|
<li>Second</li>
|
||||||
|
</ol>
|
||||||
|
<dl>
|
||||||
|
<dt>Term</dt>
|
||||||
|
<dd>Its meaning</dd>
|
||||||
|
</dl>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
16
pelican-mews/tests/fixtures/structure/input.md
vendored
Normal file
16
pelican-mews/tests/fixtures/structure/input.md
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
Title: Structure
|
||||||
|
Slug: page
|
||||||
|
Date: 2026-10-11
|
||||||
|
|
||||||
|
A table, an ordered list and a definition list: everything the placeholder
|
||||||
|
DTD used to throw away.
|
||||||
|
|
||||||
|
| Network | Band |
|
||||||
|
| --- | --- |
|
||||||
|
| GSM 900 | 900 MHz |
|
||||||
|
|
||||||
|
1. First
|
||||||
|
2. Second
|
||||||
|
|
||||||
|
Term
|
||||||
|
: Its meaning
|
||||||
|
|
@ -18,7 +18,7 @@ PACKAGE = "pelican.plugins.mews"
|
||||||
STRICT_OVERRIDES = {"MEWS_STRICT": True}
|
STRICT_OVERRIDES = {"MEWS_STRICT": True}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("case", ["basic", "strip", "cssdir"])
|
@pytest.mark.parametrize("case", ["basic", "strip", "cssdir", "structure"])
|
||||||
def test_written_page_matches_expected(tmp_path, case):
|
def test_written_page_matches_expected(tmp_path, case):
|
||||||
"""The written page matches the fixture, whatever was stripped."""
|
"""The written page matches the fixture, whatever was stripped."""
|
||||||
overrides = {"MEWS_CSS_DIR": "style"} if case == "cssdir" else {}
|
overrides = {"MEWS_CSS_DIR": "style"} if case == "cssdir" else {}
|
||||||
|
|
@ -28,7 +28,7 @@ def test_written_page_matches_expected(tmp_path, case):
|
||||||
assert normalize(page) == normalize(expected)
|
assert normalize(page) == normalize(expected)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("case", ["basic", "strip", "cssdir"])
|
@pytest.mark.parametrize("case", ["basic", "strip", "cssdir", "structure"])
|
||||||
def test_css_is_copied(tmp_path, case):
|
def test_css_is_copied(tmp_path, case):
|
||||||
"""The bundled stylesheet lands under the configured directory."""
|
"""The bundled stylesheet lands under the configured directory."""
|
||||||
overrides = {"MEWS_CSS_DIR": "style"} if case == "cssdir" else {}
|
overrides = {"MEWS_CSS_DIR": "style"} if case == "cssdir" else {}
|
||||||
|
|
@ -81,13 +81,28 @@ def test_parallel_output_folder(tmp_path):
|
||||||
assert not (output / "css").exists()
|
assert not (output / "css").exists()
|
||||||
|
|
||||||
|
|
||||||
|
# Section 4.1 of the specification keeps this many elements. The plugin lists
|
||||||
|
# none of them itself; the DTD is the only place they are written down.
|
||||||
|
SECTION_4_1_ELEMENTS = 51
|
||||||
|
|
||||||
|
|
||||||
def test_allowlist_is_read_from_the_dtd():
|
def test_allowlist_is_read_from_the_dtd():
|
||||||
"""The subset comes from the DTD, not from code."""
|
"""The subset comes from the DTD, not from code."""
|
||||||
dtd = files(PACKAGE).joinpath("dtd", "mews.dtd").read_text(encoding="utf-8")
|
dtd = files(PACKAGE).joinpath("dtd", "mews.dtd").read_text(encoding="utf-8")
|
||||||
allowlist = parse_allowlist(dtd)
|
allowlist = parse_allowlist(dtd)
|
||||||
assert allowlist["p"] == {"id", "title", "xml:lang"}
|
assert len(allowlist) == SECTION_4_1_ELEMENTS
|
||||||
assert allowlist["html"] == {"dir", "id", "lang", "title", "xml:lang", "xmlns"}
|
# The common attributes, including the two the profile adds to XHTML-MP.
|
||||||
|
assert allowlist["p"] == {"dir", "id", "lang", "title", "xml:lang"}
|
||||||
|
# html takes no id or title; section 4.1 gives it these four and no more.
|
||||||
|
assert allowlist["html"] == {"dir", "lang", "xml:lang", "xmlns"}
|
||||||
assert "href" in allowlist["a"]
|
assert "href" in allowlist["a"]
|
||||||
|
# class is permitted on body and nowhere else (section 5.3).
|
||||||
|
assert "class" in allowlist["body"]
|
||||||
|
assert not [
|
||||||
|
name for name, attrs in allowlist.items() if name != "body" and "class" in attrs
|
||||||
|
]
|
||||||
|
# The parts the placeholder DTD lacked, which the plugin was stripping.
|
||||||
|
assert {"table", "td", "ol", "dl", "form", "input", "address"} <= set(allowlist)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(shutil.which("uv") is None, reason="uv is not installed")
|
@pytest.mark.skipif(shutil.which("uv") is None, reason="uv is not installed")
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,12 @@ COPIES = {
|
||||||
"mews-0.1.dtd": ROOT / "dtd" / "mews-0.1.dtd",
|
"mews-0.1.dtd": ROOT / "dtd" / "mews-0.1.dtd",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# The Pelican plugin is a separate distributable package, so it carries its own
|
||||||
|
# copy of the DTD rather than importing one. It shipped a hand-written
|
||||||
|
# placeholder for a while, which silently stripped tables, ordered lists, forms
|
||||||
|
# and the body variant class out of every page it produced.
|
||||||
|
PLUGIN_DTD = ROOT / "pelican-mews" / "pelican" / "plugins" / "mews" / "dtd" / "mews.dtd"
|
||||||
|
|
||||||
|
|
||||||
def test_packaged_copies_match_the_originals():
|
def test_packaged_copies_match_the_originals():
|
||||||
for name, original in COPIES.items():
|
for name, original in COPIES.items():
|
||||||
|
|
@ -28,3 +34,10 @@ def test_the_spec_quotes_the_same_stylesheet():
|
||||||
spec = (ROOT / "doc" / "SPEC.md").read_text()
|
spec = (ROOT / "doc" / "SPEC.md").read_text()
|
||||||
quoted = spec.split("```css\n", 1)[1].split("```", 1)[0]
|
quoted = spec.split("```css\n", 1)[1].split("```", 1)[0]
|
||||||
assert quoted.strip() == (ROOT / "mews-0.1.css").read_text().strip()
|
assert quoted.strip() == (ROOT / "mews-0.1.css").read_text().strip()
|
||||||
|
|
||||||
|
|
||||||
|
def test_the_pelican_plugin_bundles_the_real_dtd():
|
||||||
|
canonical = (ROOT / "dtd" / "mews-0.1.dtd").read_bytes()
|
||||||
|
assert PLUGIN_DTD.read_bytes() == canonical, (
|
||||||
|
f"{PLUGIN_DTD} differs from dtd/mews-0.1.dtd. Copy the original over it."
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,18 @@ def test_the_projects_own_pages_have_no_warnings_either(page):
|
||||||
assert not report.warnings, "\n".join(str(f) for f in report.warnings)
|
assert not report.warnings, "\n".join(str(f) for f in report.warnings)
|
||||||
|
|
||||||
|
|
||||||
|
PLUGIN_PAGES = sorted(
|
||||||
|
(ROOT / "pelican-mews" / "tests" / "fixtures").glob("*/expected.xhtml")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("page", PLUGIN_PAGES, ids=lambda p: p.parent.name)
|
||||||
|
def test_pages_the_pelican_plugin_produces_conform(page):
|
||||||
|
"""What the plugin writes has to pass the validator, or it is no use."""
|
||||||
|
report = validate_file(str(page))
|
||||||
|
assert report.conforms, "\n".join(str(f) for f in report.failures)
|
||||||
|
|
||||||
|
|
||||||
def test_the_dtd_still_matches_the_spec():
|
def test_the_dtd_still_matches_the_spec():
|
||||||
"""dtd/check_dtd.py is hand-maintained; keep it running in CI."""
|
"""dtd/check_dtd.py is hand-maintained; keep it running in CI."""
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue