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 */
25
26class BadImageRemover implements Fixer {
27    /**
28     * @var callable
29     */
30    protected $isFiltered;
31
32    /**
33     * @param callable $isFiltered (string, Title) returning bool. First
34     *  argument is the image name to check. Second argument is the page on
35     *  which the image occurs. Returns true when the image should be filtered.
36     */
37    public function __construct( $isFiltered ) {
38        $this->isFiltered = $isFiltered;
39    }
40
41    /**
42     * @return string
43     */
44    public function getXPath() {
45        return '//figure[starts-with(@typeof,"mw:File")]//img[@resource] | ' .
46            '//span[starts-with(@typeof,"mw:File")]//img[@resource] | ' .
47            // TODO: Remove mw:Image when version 2.4.0 of the content is no
48            // longer supported
49            '//figure[starts-with(@typeof,"mw:Image")]//img[@resource] | ' .
50            '//figure-inline[starts-with(@typeof,"mw:Image")]//img[@resource] | ' .
51            '//span[starts-with(@typeof,"mw:Image")]//img[@resource]';
52    }
53
54    /**
55     * Receives an html string. It find all images and run them through
56     * wfIsBadImage() to determine if the image can be shown.
57     *
58     * @param DOMNode $node
59     * @param Title $title
60     */
61    public function apply( DOMNode $node, Title $title ) {
62        if ( !$node instanceof DOMElement ) {
63            return;
64        }
65
66        $resource = $node->getAttribute( 'resource' );
67        if ( $resource === '' ) {
68            return;
69        }
70
71        $image = Utils::createRelativeTitle( rawurldecode( $resource ), $title );
72        if ( !$image ) {
73            wfDebugLog( 'Flow', __METHOD__ . ': Could not construct title for node: ' .
74                $node->ownerDocument->saveXML( $node ) );
75            return;
76        }
77
78        if ( !( $this->isFiltered )( $image->getDBkey(), $title ) ) {
79            return;
80        }
81
82        // Move up the DOM and remove the typeof="mw:File" node
83        $nodeToRemove = $node->parentNode;
84        while ( $nodeToRemove instanceof DOMElement && (
85                strpos( $nodeToRemove->getAttribute( 'typeof' ), 'mw:File' ) === false &&
86                // TODO: Remove mw:Image when version 2.4.0 of the content is
87                // no longer supported
88                strpos( $nodeToRemove->getAttribute( 'typeof' ), 'mw:Image' ) === false
89        ) ) {
90            $nodeToRemove = $nodeToRemove->parentNode;
91        }
92        if ( !$nodeToRemove ) {
93            throw new RuntimeException( 'Did not find parent mw:File to remove' );
94        }
95        $nodeToRemove->parentNode->removeChild( $nodeToRemove );
96    }
97}