Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
77.78% covered (warning)
77.78%
21 / 27
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
BadImageRemover
77.78% covered (warning)
77.78%
21 / 27
66.67% covered (warning)
66.67%
2 / 3
12.33
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getXPath
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 apply
71.43% covered (warning)
71.43%
15 / 21
0.00% covered (danger)
0.00%
0 / 1
10.89
1<?php
2
3namespace Flow\Parsoid\Fixer;
4
5use DOMElement;
6use DOMNode;
7use Flow\Conversion\Utils;
8use Flow\Parsoid\Fixer;
9use MediaWiki\Title\Title;
10use RuntimeException;
11
12/**
13 * Parsoid ignores bad_image_list. With good reason: bad images should only be
14 * removed when rendering the content, not when it's created. This
15 * class updates HTML content from Parsoid by deleting inappropriate images, as
16 * defined by wfIsBadImage().
17 *
18 * Usage:
19 *
20 *    $badImageRemover = new BadImageRemover();
21 *
22 *    // Before outputting content
23 *    $content = $badImageRemover->apply( $foo->getContent(), $title );
24 */
25class BadImageRemover implements Fixer {
26    /**
27     * @var callable
28     */
29    protected $isFiltered;
30
31    /**
32     * @param callable $isFiltered (string, Title) returning bool. First
33     *  argument is the image name to check. Second argument is the page on
34     *  which the image occurs. Returns true when the image should be filtered.
35     */
36    public function __construct( $isFiltered ) {
37        $this->isFiltered = $isFiltered;
38    }
39
40    /**
41     * @return string
42     */
43    public function getXPath() {
44        return '//figure[starts-with(@typeof,"mw:File")]//img[@resource] | ' .
45            '//span[starts-with(@typeof,"mw:File")]//img[@resource] | ' .
46            // TODO: Remove mw:Image when version 2.4.0 of the content is no
47            // longer supported
48            '//figure[starts-with(@typeof,"mw:Image")]//img[@resource] | ' .
49            '//figure-inline[starts-with(@typeof,"mw:Image")]//img[@resource] | ' .
50            '//span[starts-with(@typeof,"mw:Image")]//img[@resource]';
51    }
52
53    /**
54     * Receives an html string. It find all images and run them through
55     * wfIsBadImage() to determine if the image can be shown.
56     *
57     * @param DOMNode $node
58     * @param Title $title
59     */
60    public function apply( DOMNode $node, Title $title ) {
61        if ( !$node instanceof DOMElement ) {
62            return;
63        }
64
65        $resource = $node->getAttribute( 'resource' );
66        if ( $resource === '' ) {
67            return;
68        }
69
70        $image = Utils::createRelativeTitle( rawurldecode( $resource ), $title );
71        if ( !$image ) {
72            wfDebugLog( 'Flow', __METHOD__ . ': Could not construct title for node: ' .
73                $node->ownerDocument->saveXML( $node ) );
74            return;
75        }
76
77        if ( !( $this->isFiltered )( $image->getDBkey(), $title ) ) {
78            return;
79        }
80
81        // Move up the DOM and remove the typeof="mw:File" node
82        $nodeToRemove = $node->parentNode;
83        while ( $nodeToRemove instanceof DOMElement && (
84                strpos( $nodeToRemove->getAttribute( 'typeof' ), 'mw:File' ) === false &&
85                // TODO: Remove mw:Image when version 2.4.0 of the content is
86                // no longer supported
87                strpos( $nodeToRemove->getAttribute( 'typeof' ), 'mw:Image' ) === false
88        ) ) {
89            $nodeToRemove = $nodeToRemove->parentNode;
90        }
91        if ( !$nodeToRemove ) {
92            throw new RuntimeException( 'Did not find parent mw:File to remove' );
93        }
94        $nodeToRemove->parentNode->removeChild( $nodeToRemove );
95    }
96}