Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ContentFixer
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 5
182
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 getContent
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 apply
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 applyToDom
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 createDOM
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3namespace Flow\Parsoid;
4
5use DOMDocument;
6use DOMXPath;
7use Flow\Conversion\Utils;
8use Flow\Exception\FlowException;
9use Flow\Model\AbstractRevision;
10use MediaWiki\Title\Title;
11
12class ContentFixer {
13    /**
14     * @var Fixer[]
15     */
16    protected $contentFixers = [];
17
18    /**
19     * Accepts multiple content fixers.
20     *
21     * @param Fixer $contentFixer,...
22     * @throws FlowException When provided arguments are not an instance of Fixer
23     */
24    public function __construct( Fixer $contentFixer /* [, Fixer $contentFixer2 [, ...]] */ ) {
25        $this->contentFixers = func_get_args();
26
27        // validate data
28        foreach ( $this->contentFixers as $contentFixer ) {
29            if ( !$contentFixer instanceof Fixer ) {
30                throw new FlowException( 'Invalid content fixer', 'default' );
31            }
32        }
33    }
34
35    /**
36     * @param AbstractRevision $revision
37     * @return string
38     */
39    public function getContent( AbstractRevision $revision ) {
40        return $this->apply(
41            $revision->getContent( 'html' ),
42            $revision->getCollection()->getTitle()
43        );
44    }
45
46    /**
47     * Applies all contained content fixers to the provided HTML content.
48     * The resulting content is then suitable for display to the end user.
49     *
50     * @param string $content Html
51     * @param Title $title
52     * @return string Html
53     */
54    public function apply( $content, Title $title ) {
55        $dom = self::createDOM( $content );
56        $this->applyToDom( $dom, $title );
57        return Utils::getInnerHtml( $dom->getElementsByTagName( 'body' )->item( 0 ) );
58    }
59
60    /**
61     * Applies all content fixers to the provided DOMDocument.
62     * Like apply(), but modifies a DOM in place rather than parsing and reserializing a string.
63     *
64     * @param DOMDocument $dom
65     * @param Title $title
66     */
67    public function applyToDom( DOMDocument $dom, Title $title ) {
68        $xpath = new DOMXPath( $dom );
69        foreach ( $this->contentFixers as $i => $contentFixer ) {
70            $found = $xpath->query( $contentFixer->getXPath() );
71            if ( !$found ) {
72                wfDebugLog( 'Flow', __METHOD__ . ': Invalid XPath from ' . get_class( $contentFixer ) . ' of: ' .
73                    $contentFixer->getXPath() );
74                unset( $this->contentFixers[$i] );
75                continue;
76            }
77
78            foreach ( $found as $node ) {
79                $contentFixer->apply( $node, $title );
80            }
81        }
82    }
83
84    /**
85     * Creates a DOM with extra considerations for BC with
86     * previous parsoid content
87     *
88     * @param string $content HTML from parsoid
89     * @return DOMDocument
90     * @throws \Flow\Exception\WikitextException
91     */
92    public static function createDOM( $content ) {
93         // The body tag is required otherwise <meta> tags at the top are
94         // magic'd into <head> rather than kept with the content.
95        if (
96            substr( $content, 0, 5 ) !== '<body'
97            && substr( $content, 0, 9 ) !== '<!DOCTYPE'
98            // We might have set the html/head/base href in AbstractRevision#getContent, so
99            // make one more check before body wrapping the content.
100            && substr( $content, 0, 12 ) !== '<html><head>'
101        ) {
102            // BC: content currently comes from parsoid and is stored
103            // wrapped in <body> tags, but prior to I0d9659f we were
104            // storing only the contents and not the body tag itself.
105            $content = "<body>$content</body>";
106        }
107        return Utils::createDOM( $content );
108    }
109}