Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 62
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
PageBundle
0.00% covered (danger)
0.00%
0 / 62
0.00% covered (danger)
0.00%
0 / 5
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 toHtml
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 validate
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
42
 responseData
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
6
 apply
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\Core;
5
6use Composer\Semver\Semver;
7use Wikimedia\Parsoid\DOM\Document;
8use Wikimedia\Parsoid\DOM\Element;
9use Wikimedia\Parsoid\DOM\Node;
10use Wikimedia\Parsoid\Utils\ContentUtils;
11use Wikimedia\Parsoid\Utils\DOMCompat;
12use Wikimedia\Parsoid\Utils\DOMDataUtils;
13use Wikimedia\Parsoid\Utils\DOMUtils;
14
15/**
16 * PORT-FIXME: This is just a placeholder for data that was previously passed
17 * to entrypoint in JavaScript.  Who will construct these objects and whether
18 * this is the correct interface is yet to be determined.
19 */
20class PageBundle {
21    /** @var string */
22    public $html;
23
24    /** @var ?array */
25    public $parsoid;
26
27    /** @var ?array */
28    public $mw;
29
30    /** @var ?string */
31    public $version;
32
33    /** @var ?array */
34    public $headers;
35
36    /** @var string|null */
37    public $contentmodel;
38
39    /**
40     * @param string $html
41     * @param ?array $parsoid
42     * @param ?array $mw
43     * @param ?string $version
44     * @param ?array $headers
45     * @param ?string $contentmodel
46     */
47    public function __construct(
48        string $html, ?array $parsoid = null, ?array $mw = null,
49        ?string $version = null, ?array $headers = null,
50        ?string $contentmodel = null
51    ) {
52        $this->html = $html;
53        $this->parsoid = $parsoid;
54        $this->mw = $mw;
55        $this->version = $version;
56        $this->headers = $headers;
57        $this->contentmodel = $contentmodel;
58    }
59
60    public function toHtml(): string {
61        $doc = DOMUtils::parseHTML( $this->html );
62        self::apply( $doc, $this );
63        return ContentUtils::toXML( $doc );
64    }
65
66    /**
67     * Check if this pagebundle is valid.
68     * @param string $contentVersion Document content version to validate against.
69     * @param ?string &$errorMessage Error message will be returned here.
70     * @return bool
71     */
72    public function validate(
73        string $contentVersion, ?string &$errorMessage = null
74    ) {
75        if ( !$this->parsoid || !isset( $this->parsoid['ids'] ) ) {
76            $errorMessage = 'Invalid data-parsoid was provided.';
77            return false;
78        } elseif ( Semver::satisfies( $contentVersion, '^999.0.0' )
79            && ( !$this->mw || !isset( $this->mw['ids'] ) )
80        ) {
81            $errorMessage = 'Invalid data-mw was provided.';
82            return false;
83        }
84        return true;
85    }
86
87    /**
88     * @return array
89     */
90    public function responseData() {
91        $responseData = [
92            'contentmodel' => $this->contentmodel ?? '',
93            'html' => [
94                'headers' => array_merge( [
95                    'content-type' => 'text/html; charset=utf-8; '
96                        . 'profile="https://www.mediawiki.org/wiki/Specs/HTML/'
97                        . $this->version . '"',
98                ], $this->headers ?? [] ),
99                'body' => $this->html,
100            ],
101            'data-parsoid' => [
102                'headers' => [
103                    'content-type' => 'application/json; charset=utf-8; '
104                        . 'profile="https://www.mediawiki.org/wiki/Specs/data-parsoid/'
105                        . $this->version . '"',
106                ],
107                'body' => $this->parsoid,
108            ],
109        ];
110        if ( Semver::satisfies( $this->version, '^999.0.0' ) ) {
111            $responseData['data-mw'] = [
112                'headers' => [
113                    'content-type' => 'application/json; charset=utf-8; ' .
114                        'profile="https://www.mediawiki.org/wiki/Specs/data-mw/' .
115                        $this->version . '"',
116                ],
117                'body' => $this->mw,
118            ];
119        }
120        return $responseData;
121    }
122
123    /**
124     * Applies the `data-*` attributes JSON structure to the document.
125     * Leaves `id` attributes behind -- they are used by citation code to
126     * extract `<ref>` body from the DOM.
127     *
128     * @param Document $doc doc
129     * @param PageBundle $pb page bundle
130     */
131    public static function apply( Document $doc, PageBundle $pb ): void {
132        DOMUtils::visitDOM(
133            DOMCompat::getBody( $doc ),
134            static function ( Node $node ) use ( &$pb ): void {
135                if ( $node instanceof Element ) {
136                    $id = $node->getAttribute( 'id' ) ?? '';
137                    if ( isset( $pb->parsoid['ids'][$id] ) ) {
138                        DOMDataUtils::setJSONAttribute(
139                            $node, 'data-parsoid', $pb->parsoid['ids'][$id]
140                        );
141                    }
142                    if ( isset( $pb->mw['ids'][$id] ) ) {
143                        // Only apply if it isn't already set.  This means
144                        // earlier applications of the pagebundle have higher
145                        // precedence, inline data being the highest.
146                        if ( !$node->hasAttribute( 'data-mw' ) ) {
147                            DOMDataUtils::setJSONAttribute(
148                                $node, 'data-mw', $pb->mw['ids'][$id]
149                            );
150                        }
151                    }
152                }
153            }
154        );
155    }
156
157}