1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * File holding the Lingo\Element class. |
||||
5 | * |
||||
6 | * This file is part of the MediaWiki extension Lingo. |
||||
7 | * |
||||
8 | * @copyright 2011 - 2018, Stephan Gambke |
||||
9 | * @license GPL-2.0-or-later |
||||
10 | * |
||||
11 | * The Lingo extension is free software: you can redistribute it and/or modify |
||||
12 | * it under the terms of the GNU General Public License as published by the Free |
||||
13 | * Software Foundation; either version 2 of the License, or (at your option) any |
||||
14 | * later version. |
||||
15 | * |
||||
16 | * The Lingo extension is distributed in the hope that it will be useful, but |
||||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
||||
18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
||||
19 | * details. |
||||
20 | * |
||||
21 | * You should have received a copy of the GNU General Public License along |
||||
22 | * with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
23 | * |
||||
24 | * @author Stephan Gambke |
||||
25 | * |
||||
26 | * @file |
||||
27 | * @ingroup Lingo |
||||
28 | */ |
||||
29 | |||||
30 | namespace Lingo; |
||||
31 | |||||
32 | use DOMDocument; |
||||
33 | use DOMElement; |
||||
34 | use DOMText; |
||||
35 | use Title; |
||||
0 ignored issues
–
show
|
|||||
36 | |||||
37 | /** |
||||
38 | * This class represents a term-definition pair. |
||||
39 | * One term may be related to several definitions. |
||||
40 | * |
||||
41 | * @ingroup Lingo |
||||
42 | */ |
||||
43 | class Element { |
||||
44 | const ELEMENT_TERM = 0; |
||||
45 | const ELEMENT_DEFINITION = 1; |
||||
46 | const ELEMENT_SOURCE = 2; |
||||
47 | const ELEMENT_LINK = 3; |
||||
48 | const ELEMENT_STYLE = 4; |
||||
49 | |||||
50 | const ELEMENT_FIELDCOUNT = 5; // number of fields stored for each element; (last field's index) + 1 |
||||
51 | |||||
52 | const LINK_TEMPLATE_ID = 'LingoLink'; |
||||
53 | |||||
54 | private $formattedTerm = null; |
||||
55 | private $formattedDefinitions = null; |
||||
56 | |||||
57 | private $mDefinitions = []; |
||||
58 | private $mTerm = null; |
||||
59 | |||||
60 | private $hasBeenDisplayed = false; |
||||
61 | |||||
62 | /** |
||||
63 | * Lingo\Element constructor. |
||||
64 | * |
||||
65 | * @param string &$term |
||||
66 | * @param string[] &$definition |
||||
67 | */ |
||||
68 | 12 | public function __construct( &$term, &$definition ) { |
|||
69 | 12 | $this->mTerm = $term; |
|||
70 | 12 | $this->addDefinition( $definition ); |
|||
71 | 12 | } |
|||
72 | |||||
73 | /** |
||||
74 | * @param array &$definition |
||||
75 | */ |
||||
76 | 11 | public function addDefinition( &$definition ) { |
|||
77 | 11 | $this->mDefinitions[] = $definition + array_fill( 0, self::ELEMENT_FIELDCOUNT, null ); |
|||
78 | 11 | } |
|||
79 | |||||
80 | /** |
||||
81 | * @param DOMDocument &$doc |
||||
82 | * |
||||
83 | * @return DOMElement|DOMText |
||||
84 | */ |
||||
85 | 11 | public function getFormattedTerm( DOMDocument &$doc ) { |
|||
86 | 11 | global $wgexLingoDisplayOnce; |
|||
87 | |||||
88 | 11 | if ( $wgexLingoDisplayOnce && $this->hasBeenDisplayed ) { |
|||
89 | 2 | return $doc->createTextNode( $this->mTerm ); |
|||
90 | } |
||||
91 | |||||
92 | 11 | $this->hasBeenDisplayed = true; |
|||
93 | |||||
94 | 11 | $this->buildFormattedTerm( $doc ); |
|||
95 | |||||
96 | 11 | return $this->formattedTerm->cloneNode( true ); |
|||
97 | } |
||||
98 | |||||
99 | /** |
||||
100 | * @param DOMDocument &$doc |
||||
101 | */ |
||||
102 | 11 | private function buildFormattedTerm( DOMDocument &$doc ) { |
|||
103 | // only create if not yet created |
||||
104 | 11 | if ( $this->formattedTerm === null || $this->formattedTerm->ownerDocument !== $doc ) { |
|||
105 | |||||
106 | 11 | if ( $this->isSimpleLink() ) { |
|||
107 | 7 | $this->formattedTerm = $this->buildFormattedTermAsLink( $doc ); |
|||
108 | } else { |
||||
109 | 4 | $this->formattedTerm = $this->buildFormattedTermAsTooltip( $doc ); |
|||
110 | } |
||||
111 | } |
||||
112 | 11 | } |
|||
113 | |||||
114 | /** |
||||
115 | * @return bool |
||||
116 | */ |
||||
117 | 11 | private function isSimpleLink() { |
|||
118 | 11 | return count( $this->mDefinitions ) === 1 && |
|||
119 | 11 | !is_string( $this->mDefinitions[ 0 ][ self::ELEMENT_DEFINITION ] ) && |
|||
120 | 11 | is_string( $this->mDefinitions[ 0 ][ self::ELEMENT_LINK ] ); |
|||
121 | } |
||||
122 | |||||
123 | /** |
||||
124 | * @param DOMDocument &$doc |
||||
125 | * @return DOMElement |
||||
126 | */ |
||||
127 | 7 | protected function buildFormattedTermAsLink( DOMDocument &$doc ) { |
|||
128 | 7 | $linkTarget = $this->mDefinitions[ 0 ][ self::ELEMENT_LINK ]; |
|||
129 | 7 | $descriptor = $this->getDescriptorFromLinkTarget( $linkTarget ); |
|||
130 | |||||
131 | 7 | if ( $descriptor === null ) { |
|||
0 ignored issues
–
show
|
|||||
132 | 2 | $this->mDefinitions = []; |
|||
133 | 2 | $this->addErrorMessageForInvalidLink( $linkTarget ); |
|||
134 | 2 | return $this->buildFormattedTermAsTooltip( $doc ); |
|||
135 | } |
||||
136 | |||||
137 | // create link element |
||||
138 | 5 | $link = $doc->createElement( 'a', htmlentities( $this->mDefinitions[ 0 ][ self::ELEMENT_TERM ] ) ); |
|||
139 | |||||
140 | // set the link target |
||||
141 | 5 | $link->setAttribute( 'href', $descriptor[ 'url' ] ); |
|||
142 | 5 | $link->setAttribute( 'class', implode( ' ', $this->getClassesForLink( $descriptor ) ) ); |
|||
143 | |||||
144 | 5 | $title = $this->getTitleForLink( $descriptor ); |
|||
145 | 5 | if ( $title !== null ) { |
|||
0 ignored issues
–
show
|
|||||
146 | 4 | $link->setAttribute( 'title', $title ); |
|||
147 | } |
||||
148 | |||||
149 | 5 | return $link; |
|||
150 | } |
||||
151 | |||||
152 | /** |
||||
153 | * @param DOMDocument &$doc |
||||
154 | * |
||||
155 | * @return DOMElement |
||||
156 | */ |
||||
157 | 6 | protected function buildFormattedTermAsTooltip( DOMDocument &$doc ) { |
|||
158 | // Wrap term and definition in <span> tags |
||||
159 | 6 | $span = $doc->createElement( 'span', htmlentities( $this->mTerm ) ); |
|||
160 | 6 | $span->setAttribute( 'class', 'mw-lingo-term' ); |
|||
161 | 6 | $span->setAttribute( 'data-lingo-term-id', $this->getId() ); |
|||
162 | |||||
163 | 6 | return $span; |
|||
164 | } |
||||
165 | |||||
166 | /** |
||||
167 | * @param array $descriptor |
||||
168 | * |
||||
169 | * @return string[] |
||||
170 | */ |
||||
171 | 5 | protected function getClassesForLink( $descriptor ) { |
|||
172 | // TODO: should this be more elaborate? |
||||
0 ignored issues
–
show
|
|||||
173 | // Cleanest would probably be to use Linker::link and parse it |
||||
174 | // back into a DOMElement, but we are in a somewhat time-critical |
||||
175 | // part here. |
||||
176 | |||||
177 | // set style |
||||
178 | 5 | $classes = [ 'mw-lingo-term' ]; |
|||
179 | |||||
180 | 5 | $classes[] = $this->mDefinitions[ 0 ][ self::ELEMENT_STYLE ]; |
|||
181 | |||||
182 | 5 | if ( array_key_exists( 'title', $descriptor ) && $descriptor[ 'title' ] instanceof Title ) { |
|||
183 | |||||
184 | 4 | if ( !$descriptor['title']->isKnown() ) { |
|||
185 | 2 | $classes[] = 'new'; |
|||
186 | } |
||||
187 | |||||
188 | 4 | if ( $descriptor['title']->isExternal() ) { |
|||
189 | 4 | $classes[] = 'extiw'; |
|||
190 | } |
||||
191 | |||||
192 | } else { |
||||
193 | 1 | $classes[] = 'ext'; |
|||
194 | } |
||||
195 | |||||
196 | 5 | return array_filter( $classes ); |
|||
197 | } |
||||
198 | |||||
199 | /** |
||||
200 | * @param array $descriptor |
||||
201 | * @return string |
||||
202 | */ |
||||
203 | protected function getTitleForLink( $descriptor ) { |
||||
204 | /** @var \Title $target */ |
||||
205 | 5 | $target = $descriptor[ 'title' ]; |
|||
206 | |||||
207 | 5 | if ( is_string( $target ) ) { |
|||
0 ignored issues
–
show
|
|||||
208 | return $target; |
||||
209 | 5 | } |
|||
210 | 1 | ||||
211 | if ( $target->getPrefixedText() === '' ) { |
||||
212 | return null; |
||||
213 | 4 | } |
|||
214 | 1 | ||||
215 | if ( $target->isKnown() ) { |
||||
216 | return $target->getPrefixedText(); |
||||
217 | 3 | } |
|||
218 | 1 | ||||
219 | return wfMessage( 'red-link-title', $target->getPrefixedText() )->text(); |
||||
0 ignored issues
–
show
The function
wfMessage was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
220 | } |
||||
221 | 2 | ||||
222 | /** |
||||
223 | * @return string[] |
||||
224 | */ |
||||
225 | public function getFormattedDefinitions() { |
||||
226 | if ( $this->formattedDefinitions === null ) { |
||||
227 | 4 | $this->buildFormattedDefinitions(); |
|||
228 | 4 | } |
|||
229 | 4 | ||||
230 | return $this->formattedDefinitions; |
||||
231 | } |
||||
232 | 4 | ||||
233 | /** |
||||
234 | */ |
||||
235 | protected function buildFormattedDefinitions() { |
||||
236 | if ( $this->isSimpleLink() ) { |
||||
237 | 4 | $this->formattedDefinitions = ''; |
|||
238 | 4 | return; |
|||
239 | 1 | } |
|||
240 | 1 | ||||
241 | $divDefinitions = "<div class='mw-lingo-tooltip' id='{$this->getId()}'>"; |
||||
0 ignored issues
–
show
As per coding-style, please use concatenation or
sprintf for the variable $this instead of interpolation.
It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings. // Instead of
$x = "foo $bar $baz";
// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
|
|||||
242 | |||||
243 | 3 | $definition = reset( $this->mDefinitions ); |
|||
244 | while ( $definition !== false ) { |
||||
245 | 3 | ||||
246 | 3 | $text = $definition[ self::ELEMENT_DEFINITION ]; |
|||
247 | $link = $definition[ self::ELEMENT_LINK ]; |
||||
248 | 3 | $style = $definition[ self::ELEMENT_STYLE ]; |
|||
249 | 3 | ||||
250 | 3 | // navigation-not-searchable removes definition from CirrusSearch index |
|||
251 | $divDefinitions .= "<div class='mw-lingo-definition navigation-not-searchable {$style}'>" |
||||
0 ignored issues
–
show
As per coding-style, please use concatenation or
sprintf for the variable $style instead of interpolation.
It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings. // Instead of
$x = "foo $bar $baz";
// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
|
|||||
252 | 3 | . "<div class='mw-lingo-definition-text'>\n{$text}\n</div>"; |
|||
0 ignored issues
–
show
As per coding-style, please use concatenation or
sprintf for the variable $text instead of interpolation.
It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings. // Instead of
$x = "foo $bar $baz";
// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
|
|||||
253 | |||||
254 | 3 | if ( $link !== null ) { |
|||
255 | |||||
256 | 2 | $descriptor = $this->getDescriptorFromLinkTarget( $link ); |
|||
257 | |||||
258 | 2 | if ( $descriptor === null ) { |
|||
259 | 1 | $this->addErrorMessageForInvalidLink( $link ); |
|||
260 | } else { |
||||
261 | 1 | $divDefinitions .= "<div class='mw-lingo-definition-link'>[{$descriptor[ 'url' ]} <nowiki/>]</div>"; |
|||
0 ignored issues
–
show
As per coding-style, please use concatenation or
sprintf for the variable $descriptor instead of interpolation.
It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings. // Instead of
$x = "foo $bar $baz";
// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
|
|||||
262 | } |
||||
263 | } |
||||
264 | |||||
265 | 3 | $divDefinitions .= "</div>"; |
|||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
The string literal
</div> does not require double quotes, as per coding-style, please use single quotes.
PHP provides two ways to mark string literals. Either with single quotes String literals in single quotes on the other hand are evaluated very literally and the only two
characters that needs escaping in the literal are the single quote itself ( Double quoted string literals may contain other variables or more complex escape sequences. <?php
$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";
print $doubleQuoted;
will print an indented: If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear. For more information on PHP string literals and available escape sequences see the PHP core documentation.
Loading history...
|
|||||
266 | |||||
267 | 3 | $definition = next( $this->mDefinitions ); |
|||
268 | } |
||||
269 | |||||
270 | 3 | $divDefinitions .= "\n</div>"; |
|||
271 | |||||
272 | 3 | $this->formattedDefinitions = $divDefinitions; |
|||
273 | 3 | } |
|||
274 | |||||
275 | /** |
||||
276 | * @return string |
||||
277 | */ |
||||
278 | 6 | public function getId() { |
|||
279 | 6 | return md5( $this->mTerm ); |
|||
280 | } |
||||
281 | |||||
282 | /** |
||||
283 | * @param string $linkTarget |
||||
284 | * |
||||
285 | * @return string[] |
||||
286 | */ |
||||
287 | 9 | protected function getDescriptorFromLinkTarget( $linkTarget ) { |
|||
288 | 9 | if ( $this->isValidLinkTarget( $linkTarget ) ) { |
|||
289 | 1 | return [ 'url' => $linkTarget, 'title' => $this->mTerm ]; |
|||
290 | } |
||||
291 | |||||
292 | 8 | $title = Title::newFromText( $linkTarget ); |
|||
293 | |||||
294 | 8 | if ( $title !== null ) { |
|||
295 | 5 | return [ 'url' => $title->getFullURL(), 'title' => $title ]; |
|||
296 | } |
||||
297 | |||||
298 | 3 | return null; |
|||
299 | } |
||||
300 | |||||
301 | /** |
||||
302 | * @param string $linkTarget |
||||
303 | * |
||||
304 | * @return bool |
||||
305 | */ |
||||
306 | 9 | protected function isValidLinkTarget( $linkTarget ) { |
|||
307 | 9 | return wfParseUrl( $linkTarget ) !== false; |
|||
0 ignored issues
–
show
The function
wfParseUrl was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
308 | } |
||||
309 | |||||
310 | /** |
||||
311 | * @param string $link |
||||
312 | */ |
||||
313 | 3 | protected function addErrorMessageForInvalidLink( $link ) { |
|||
314 | 3 | $errorMessage = wfMessage( 'lingo-invalidlinktarget', $this->mTerm, $link )->text(); |
|||
0 ignored issues
–
show
The function
wfMessage was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
315 | 3 | $errorDefinition = [ self::ELEMENT_DEFINITION => $errorMessage, self::ELEMENT_STYLE => 'invalid-link-target' ]; |
|||
316 | |||||
317 | 3 | $this->addDefinition( $errorDefinition ); |
|||
318 | 3 | } |
|||
319 | |||||
320 | } |
||||
321 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths