Passed
Push — master ( 631cc3...8704f4 )
by
unknown
04:11
created

Element::getCurrentKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 2
rs 10
ccs 0
cts 2
cp 0
crap 2
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   GNU General Public License, version 2 (or any later version)
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 DOMNode;
35
use DOMText;
36
use Title;
0 ignored issues
show
Bug introduced by
The type Title was not found. Maybe you did not declare it correctly or list all dependencies?

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:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
37
38
/**
39
 * This class represents a term-definition pair.
40
 * One term may be related to several definitions.
41
 *
42
 * @ingroup Lingo
43
 */
44
class Element {
45
	const ELEMENT_TERM = 0;
46
	const ELEMENT_DEFINITION = 1;
47
	const ELEMENT_SOURCE = 2;
48
	const ELEMENT_LINK = 3;
49
	const ELEMENT_STYLE = 4;
50
51
	const ELEMENT_FIELDCOUNT = 5;  // number of fields stored for each element; (last field's index) + 1
52
53
	const LINK_TEMPLATE_ID = 'LingoLink';
54
55
	private $formattedTerm = null;
56
	private $formattedDefinitions = null;
57
58
	private $mDefinitions = [];
59
	private $mTerm = null;
60
61
	private $hasBeenDisplayed = false;
62
63
	/**
64
	 * Lingo\Element constructor.
65
	 *
66
	 * @param string $term
67
	 * @param string[] $definition
68
	 */
69 12
	public function __construct( &$term, &$definition ) {
70
71 12
		$this->mTerm = $term;
72 12
		$this->addDefinition( $definition );
73 12
	}
74
75
	/**
76
	 * @param array $definition
77
	 */
78 11
	public function addDefinition( &$definition ) {
79 11
		$this->mDefinitions[] = $definition + array_fill( 0, self::ELEMENT_FIELDCOUNT, null ) ;
80 11
	}
81
82
	/**
83
	 * @param DOMDocument $doc
84
	 *
85
	 * @return DOMElement|DOMText
86
	 */
87 11
	public function getFormattedTerm( DOMDocument &$doc ) {
88
89 11
		global $wgexLingoDisplayOnce;
90
91 11
		if ( $wgexLingoDisplayOnce && $this->hasBeenDisplayed ) {
92 2
			return $doc->createTextNode( $this->mTerm );
93
		}
94
95 11
		$this->hasBeenDisplayed = true;
96
97 11
		$this->buildFormattedTerm( $doc );
98
99 11
		return $this->formattedTerm->cloneNode( true );
100
	}
101
102
	/**
103
	 * @param DOMDocument $doc
104
	 */
105 11
	private function buildFormattedTerm( DOMDocument &$doc ) {
106
107
		// only create if not yet created
108 11
		if ( $this->formattedTerm === null || $this->formattedTerm->ownerDocument !== $doc ) {
109
110 11
			if ( $this->isSimpleLink() ) {
111 7
				$this->formattedTerm = $this->buildFormattedTermAsLink( $doc );
112
			} else {
113 4
				$this->formattedTerm = $this->buildFormattedTermAsTooltip( $doc );
114
			}
115
		}
116 11
	}
117
118
	/**
119
	 * @return bool
120
	 */
121 11
	private function isSimpleLink() {
122 11
		return count( $this->mDefinitions ) === 1 &&
123 11
			!is_string( $this->mDefinitions[ 0 ][ self::ELEMENT_DEFINITION ] ) &&
124 11
			is_string( $this->mDefinitions[ 0 ][ self::ELEMENT_LINK ] );
125
	}
126
127
	/**
128
	 * @param DOMDocument $doc
129
	 * @return DOMElement
130
	 */
131 7
	protected function buildFormattedTermAsLink( DOMDocument &$doc ) {
132
133 7
		$linkTarget = $this->mDefinitions[ 0 ][ self::ELEMENT_LINK ];
134 7
		$descriptor = $this->getDescriptorFromLinkTarget( $linkTarget );
135
136 7
		if ( $descriptor === null ) {
0 ignored issues
show
introduced by
The condition $descriptor === null is always false.
Loading history...
137 2
			$this->mDefinitions = [];
138 2
			$this->addErrorMessageForInvalidLink( $linkTarget );
139 2
			return $this->buildFormattedTermAsTooltip( $doc );
140
		}
141
142
		// create link element
143 5
		$link = $doc->createElement( 'a', htmlentities( $this->mDefinitions[ 0 ][ self::ELEMENT_TERM ] ) );
144
145
		// set the link target
146 5
		$link->setAttribute( 'href', $descriptor[ 'url' ] );
147 5
		$link->setAttribute( 'class', join( ' ', $this->getClassesForLink( $descriptor ) ) );
148
149 5
		$title = $this->getTitleForLink( $descriptor );
150 5
		if ( $title !== null ) {
0 ignored issues
show
introduced by
The condition $title !== null is always true.
Loading history...
151 4
			$link->setAttribute( 'title', $title );
152
		}
153
154 5
		return $link;
155
	}
156
157
	/**
158
	 * @param DOMDocument $doc
159
	 *
160
	 * @return DOMElement
161
	 */
162 6
	protected function buildFormattedTermAsTooltip( DOMDocument &$doc ) {
163
164
		// Wrap term and definition in <span> tags
165 6
		$span = $doc->createElement( 'span', htmlentities( $this->mTerm ) );
166 6
		$span->setAttribute( 'class', 'mw-lingo-term' );
167 6
		$span->setAttribute( 'data-lingo-term-id', $this->getId() );
168
169 6
		return $span;
170
	}
171
172
	/**
173
	 * @param $descriptor
174
	 *
175
	 * @return string[]
176
	 */
177 5
	protected function getClassesForLink( $descriptor ) {
178
179
		// TODO: should this be more elaborate?
180
		// Cleanest would probably be to use Linker::link and parse it
181
		// back into a DOMElement, but we are in a somewhat time-critical
182
		// part here.
183
184
		// set style
185 5
		$classes = [ 'mw-lingo-term' ];
186
187 5
		$classes[] = $this->mDefinitions[ 0 ][ self::ELEMENT_STYLE ];
188
189 5
		if ( array_key_exists( 'title', $descriptor ) && $descriptor[ 'title' ] instanceof Title ) {
190
191 4
			if ( !$descriptor['title']->isKnown() ) {
192 2
				$classes[] = 'new';
193
			}
194
195 4
			if ( $descriptor['title']->isExternal() ) {
196 4
				$classes[] = 'extiw';
197
			}
198
199
		} else {
200 1
			$classes[] = 'ext';
201
		}
202
203 5
		return array_filter( $classes );
204
	}
205
206
	/**
207
	 * @param Title $target
208
	 * @param DOMElement $link
209
	 *
210
	 * @return string
211
	 */
212 5
	protected function getTitleForLink( $descriptor ) {
213
214
		/** @var \Title $target */
215 5
		$target = $descriptor[ 'title' ];
216
217 5
		if ( is_string( $target ) ) {
0 ignored issues
show
introduced by
The condition is_string($target) is always false.
Loading history...
218 1
			return $target;
219
		}
220
221 4
		if ( $target->getPrefixedText() === '' ) {
222 1
			return null;
223
		}
224
225 3
		if ( $target->isKnown() ) {
226 1
			return $target->getPrefixedText();
227
		}
228
229 2
		return wfMessage( 'red-link-title', $target->getPrefixedText() )->text();
0 ignored issues
show
Bug introduced by
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 ignore-call  annotation

229
		return /** @scrutinizer ignore-call */ wfMessage( 'red-link-title', $target->getPrefixedText() )->text();
Loading history...
230
	}
231
232
	/**
233
	 * @return string[]
234
	 */
235 4
	public function getFormattedDefinitions() {
236
237 4
		if ( $this->formattedDefinitions === null ) {
238 4
			$this->buildFormattedDefinitions();
239
		}
240
241 4
		return $this->formattedDefinitions;
242
	}
243
244
	/**
245
	 */
246 4
	protected function buildFormattedDefinitions() {
247
248 4
		if ( $this->isSimpleLink() ) {
249 1
			$this->formattedDefinitions = '';
250 1
			return;
251
		}
252
253 3
		$divDefinitions = "<div class='mw-lingo-tooltip' id='{$this->getId()}'>";
254
255 3
		$definition = reset( $this->mDefinitions );
256 3
		while ( $definition !== false ) {
257
258 3
			$text = $definition[ self::ELEMENT_DEFINITION ];
259 3
			$link = $definition[ self::ELEMENT_LINK ];
260 3
			$style = $definition[ self::ELEMENT_STYLE ];
261
262 3
			$divDefinitions .= "<div class='mw-lingo-definition {$style}'><div class='mw-lingo-definition-text'>\n{$text}\n</div>";
263
264 3
			if ( $link !== null ) {
265
266 2
				$descriptor = $this->getDescriptorFromLinkTarget( $link );
267
268 2
				if ( $descriptor === null ) {
269 1
					$this->addErrorMessageForInvalidLink( $link );
270
				} else {
271 1
					$divDefinitions .= "<div class='mw-lingo-definition-link'>[{$descriptor[ 'url' ]} <nowiki/>]</div>";
272
				}
273
			}
274
275 3
			$divDefinitions .= "</div>";
276
277 3
			$definition = next( $this->mDefinitions );
278
		}
279
280 3
		$divDefinitions .= "\n</div>";
281
282 3
		$this->formattedDefinitions = $divDefinitions;
283 3
	}
284
285
	/**
286
	 * @return string
287
	 */
288 6
	public function getId() {
289 6
		return md5( $this->mTerm );
290
	}
291
292
	/**
293
	 * @param string $linkTarget
294
	 *
295
	 * @return string[]
296
	 */
297 9
	protected function getDescriptorFromLinkTarget( $linkTarget ) {
298
299 9
		if ( $this->isValidLinkTarget( $linkTarget ) ) {
300 1
			return [ 'url' => $linkTarget, 'title' => $this->mTerm ];
301
		}
302
303 8
		$title = Title::newFromText( $linkTarget );
304
305 8
		if ( $title !== null ) {
306 5
			return [ 'url' => $title->getFullURL(), 'title' => $title ];
307
		}
308
309 3
		return null;
310
	}
311
312
	/**
313
	 * @param string $linkTarget
314
	 *
315
	 * @return bool
316
	 */
317 9
	protected function isValidLinkTarget( $linkTarget ) {
318 9
		return wfParseUrl( $linkTarget ) !== false;
0 ignored issues
show
Bug introduced by
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 ignore-call  annotation

318
		return /** @scrutinizer ignore-call */ wfParseUrl( $linkTarget ) !== false;
Loading history...
319
	}
320
321
	/**
322
	 * @param $link
323
	 */
324 3
	protected function addErrorMessageForInvalidLink( $link ) {
325 3
		$errorMessage = wfMessage( 'lingo-invalidlinktarget', $this->mTerm, $link )->text();
0 ignored issues
show
Bug introduced by
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 ignore-call  annotation

325
		$errorMessage = /** @scrutinizer ignore-call */ wfMessage( 'lingo-invalidlinktarget', $this->mTerm, $link )->text();
Loading history...
326 3
		$errorDefinition = [ self::ELEMENT_DEFINITION => $errorMessage, self::ELEMENT_STYLE => 'invalid-link-target' ];
327
328 3
		$this->addDefinition( $errorDefinition );
329 3
	}
330
331
}
332