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