Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
66.67% |
38 / 57 |
|
0.00% |
0 / 1 |
CRAP | |
0.00% |
0 / 1 |
AddRedLinks | |
66.67% |
38 / 57 |
|
0.00% |
0 / 1 |
39.93 | |
0.00% |
0 / 1 |
run | |
66.67% |
38 / 57 |
|
0.00% |
0 / 1 |
39.93 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace Wikimedia\Parsoid\Wt2Html\PP\Processors; |
5 | |
6 | use Wikimedia\Parsoid\Config\Env; |
7 | use Wikimedia\Parsoid\DOM\DocumentFragment; |
8 | use Wikimedia\Parsoid\DOM\Element; |
9 | use Wikimedia\Parsoid\DOM\Node; |
10 | use Wikimedia\Parsoid\Utils\DOMCompat; |
11 | use Wikimedia\Parsoid\Utils\PHPUtils; |
12 | use Wikimedia\Parsoid\Utils\UrlUtils; |
13 | use Wikimedia\Parsoid\Utils\WTUtils; |
14 | use Wikimedia\Parsoid\Wt2Html\Wt2HtmlDOMProcessor; |
15 | |
16 | class AddRedLinks implements Wt2HtmlDOMProcessor { |
17 | /** |
18 | * Add red links to a document. |
19 | * |
20 | * @inheritDoc |
21 | */ |
22 | public function run( |
23 | Env $env, Node $root, array $options = [], bool $atTopLevel = false |
24 | ): void { |
25 | '@phan-var Element|DocumentFragment $root'; // @var Element|DocumentFragment $root |
26 | $allLinks = PHPUtils::iterable_to_array( |
27 | DOMCompat::querySelectorAll( $root, 'a[rel~="mw:WikiLink"]' ) |
28 | ); |
29 | |
30 | // Split up processing into chunks of 1000 so that we don't exceed LinkCache::MAX_SIZE |
31 | $chunks = array_chunk( $allLinks, 1000 ); |
32 | foreach ( $chunks as $links ) { |
33 | $titles = []; |
34 | foreach ( $links as $a ) { |
35 | $t = DOMCompat::getAttribute( $a, 'title' ); |
36 | if ( $t !== null ) { |
37 | $titles[$t] = true; |
38 | } |
39 | } |
40 | |
41 | if ( !$titles ) { |
42 | return; |
43 | } |
44 | |
45 | $start = microtime( true ); |
46 | $titleMap = $env->getDataAccess()->getPageInfo( $env->getPageConfig(), array_keys( $titles ) ); |
47 | if ( $env->profiling() ) { |
48 | $profile = $env->getCurrentProfile(); |
49 | $profile->bumpMWTime( "RedLinks", 1000 * ( microtime( true ) - $start ), "api" ); |
50 | $profile->bumpCount( "RedLinks" ); |
51 | } |
52 | |
53 | $prefixedTitleText = $env->getContextTitle()->getPrefixedText(); |
54 | |
55 | foreach ( $links as $a ) { |
56 | $k = DOMCompat::getAttribute( $a, 'title' ); |
57 | if ( $k === null ) { |
58 | continue; |
59 | } |
60 | if ( empty( $titleMap[$k] ) ) { |
61 | // Likely a consequence of T237535; can be removed once |
62 | // that is fixed. |
63 | $env->log( 'warn', 'We should have data for the title: ' . $k ); |
64 | continue; |
65 | } |
66 | $data = $titleMap[$k]; |
67 | $a->removeAttribute( 'class' ); // Clear all, if we're doing a pb2pb refresh |
68 | |
69 | $href = DOMCompat::getAttribute( $a, 'href' ); |
70 | $parsedURL = UrlUtils::parseUrl( $href ?? '' ); |
71 | |
72 | $queryElts = []; |
73 | if ( isset( $parsedURL['query'] ) ) { |
74 | parse_str( $parsedURL['query'], $queryElts ); |
75 | } |
76 | |
77 | if ( |
78 | !empty( $data['missing'] ) && empty( $data['known'] ) && |
79 | $k !== $prefixedTitleText |
80 | ) { |
81 | DOMCompat::getClassList( $a )->add( 'new' ); |
82 | WTUtils::addPageContentI18nAttribute( $a, 'title', 'red-link-title', [ $k ] ); |
83 | $queryElts['action'] = 'edit'; |
84 | $queryElts['redlink'] = '1'; |
85 | } else { |
86 | if ( $k === $prefixedTitleText ) { |
87 | if ( isset( $parsedURL['fragment'] ) ) { |
88 | DOMCompat::getClassList( $a )->add( 'mw-selflink-fragment' ); |
89 | } else { |
90 | DOMCompat::getClassList( $a )->add( 'mw-selflink', 'selflink' ); |
91 | } |
92 | $a->removeAttribute( 'title' ); |
93 | } |
94 | // Clear a potential redlink, if we're doing a pb2pb refresh |
95 | // This is similar to what's happening in Html2Wt/RemoveRedLinks |
96 | // and maybe that pass should just run before this one. |
97 | if ( isset( $queryElts['action'] ) && $queryElts['action'] === 'edit' ) { |
98 | unset( $queryElts['action'] ); |
99 | } |
100 | if ( isset( $queryElts['redlink'] ) && $queryElts['redlink'] === '1' ) { |
101 | unset( $queryElts['redlink'] ); |
102 | } |
103 | } |
104 | |
105 | if ( count( $queryElts ) === 0 ) { |
106 | // avoids the insertion of ? on empty query string |
107 | $parsedURL['query'] = null; |
108 | } else { |
109 | $parsedURL['query'] = http_build_query( $queryElts ); |
110 | } |
111 | $newHref = UrlUtils::assembleUrl( $parsedURL ); |
112 | |
113 | $a->setAttribute( 'href', $newHref ); |
114 | |
115 | if ( !empty( $data['redirect'] ) ) { |
116 | DOMCompat::getClassList( $a )->add( 'mw-redirect' ); |
117 | } |
118 | foreach ( $data['linkclasses'] ?? [] as $extraClass ) { |
119 | DOMCompat::getClassList( $a )->add( $extraClass ); |
120 | } |
121 | } |
122 | } |
123 | } |
124 | } |