Completed
Push — master ( 841b7d...3ed1d3 )
by
unknown
13:44 queued 04:45
created

Element::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 2
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;
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 $term
67
	 * @param $definition
68
	 */
69 1
	public function __construct( &$term, &$definition = null ) {
70
71 1
		$this->mTerm = $term;
72
73 1
		if ( $definition ) {
74 1
			$this->addDefinition( $definition );
75
		}
76 1
	}
77
78
	/**
79
	 * @param array $definition
80
	 */
81
	public function addDefinition( &$definition ) {
82
		$this->mDefinitions[] = array_pad( $definition, self::ELEMENT_FIELDCOUNT, null );
83
	}
84
85
	/**
86
	 * @param DOMDocument $doc
87
	 *
88
	 * @return DOMNode|DOMText
89
	 */
90
	public function getFormattedTerm( DOMDocument &$doc ) {
91
92
		global $wgexLingoDisplayOnce;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
93
94
		if ( $wgexLingoDisplayOnce && $this->hasBeenDisplayed ) {
95
			return $doc->createTextNode( $this->mTerm );
96
		}
97
98
		$this->hasBeenDisplayed = true;
99
100
		$this->buildFormattedTerm( $doc );
101
102
		return $this->formattedTerm->cloneNode( true );
103
	}
104
105
	/**
106
	 * @param DOMDocument $doc
107
	 */
108
	private function buildFormattedTerm( DOMDocument &$doc ) {
109
110
		// only create if not yet created
111
		if ( $this->formattedTerm === null || $this->formattedTerm->ownerDocument !== $doc ) {
112
113
			if ( $this->isSimpleLink() ) {
114
				$this->formattedTerm = $this->buildFormattedTermAsLink( $doc );
115
			} else {
116
				$this->formattedTerm = $this->buildFormattedTermAsTooltip( $doc );
117
			}
118
		}
119
	}
120
121
	/**
122
	 * @return bool
123
	 */
124
	private function isSimpleLink() {
125
		return count( $this->mDefinitions ) === 1 &&
126
			!is_string( $this->mDefinitions[ 0 ][ self::ELEMENT_DEFINITION ] ) &&
127
			is_string( $this->mDefinitions[ 0 ][ self::ELEMENT_LINK ] );
128
	}
129
130
	/**
131
	 * @param DOMDocument $doc
132
	 * @return DOMElement
133
	 */
134
	protected function buildFormattedTermAsLink( DOMDocument &$doc ) {
135
136
		// create Title object for target page
137
		$target = Title::newFromText( $this->mDefinitions[ 0 ][ self::ELEMENT_LINK ] );
138
139
		if ( !$target instanceof Title ) {
140
			$errorMessage = wfMessage( 'lingo-invalidlinktarget', $this->mTerm, $this->mDefinitions[ 0 ][ self::ELEMENT_LINK ] )->text();
141
			$errorDefinition = [ self::ELEMENT_DEFINITION => $errorMessage, self::ELEMENT_STYLE => 'invalid-link-target' ];
142
			$this->addDefinition( $errorDefinition );
143
			return $this->buildFormattedTermAsTooltip( $doc );
144
		}
145
146
		// create link element
147
		$link = $doc->createElement( 'a', $this->mDefinitions[ 0 ][ self::ELEMENT_TERM ] );
148
149
		// set the link target
150
		$link->setAttribute( 'href', $target->getLinkURL() );
151
152
153
		$link = $this->addClassAttributeToLink( $target, $link );
154
		$link = $this->addTitleAttributeToLink( $target, $link );
155
156
		return $link;
157
	}
158
159
	/**
160
	 * @param DOMDocument $doc
161
	 *
162
	 * @return DOMElement
163
	 */
164
	protected function buildFormattedTermAsTooltip( DOMDocument &$doc ) {
165
166
		// Wrap term and definition in <span> tags
167
		$span = $doc->createElement( 'span', htmlentities( $this->mTerm, ENT_COMPAT, 'UTF-8' ) );
168
		$span->setAttribute( 'class', 'mw-lingo-term ' . $this->mDefinitions[ 0 ][ self::ELEMENT_STYLE ] );
169
		$span->setAttribute( 'data-lingo-term-id', $this->getId() );
170
171
		return $span;
172
	}
173
174
	/**
175
	 * @param Title $target
176
	 * @param DOMElement $link
177
	 *
178
	 * @return DOMElement
179
	 */
180
	protected function &addClassAttributeToLink( $target, &$link ) {
181
182
		// TODO: should this be more elaborate? See Linker::linkAttribs
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
183
		// Cleanest would probably be to use Linker::link and parse it
184
		// back into a DOMElement, but we are in a somewhat time-critical
185
		// part here.
186
187
		// set style
188
		$classes = [];
189
190
		if ( $this->mDefinitions[ 0 ][ self::ELEMENT_STYLE ] !== null ) {
191
			$classes[] = $this->mDefinitions[ 0 ][ self::ELEMENT_STYLE ];
192
		}
193
194
		if ( !$target->isKnown() ) {
195
			$classes[] = 'new';
196
		}
197
198
		if ( $target->isExternal() ) {
199
			$classes[] = 'extiw';
200
		}
201
202
		if ( count( $classes ) > 0 ) {
203
			$link->setAttribute( 'class', join( ' ', $classes ) );
204
		}
205
206
		return $link;
207
	}
208
209
	/**
210
	 * @param Title $target
211
	 * @param DOMElement $link
212
	 *
213
	 * @return DOMElement
214
	 */
215
	protected function &addTitleAttributeToLink( $target, &$link ) {
216
217
		if ( $target->getPrefixedText() === '' ) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
218
			// A link like [[#Foo]].  This used to mean an empty title
219
			// attribute, but that's silly.  Just don't output a title.
220
		} elseif ( $target->isKnown() ) {
221
			$link->setAttribute( 'title', $target->getPrefixedText() );
222
		} else {
223
			$link->setAttribute( 'title', wfMessage( 'red-link-title', $target->getPrefixedText() )->text() );
224
		}
225
226
		return $link;
227
	}
228
229
	/**
230
	 * @return string[]
231
	 */
232
	public function getFormattedDefinitions() {
233
234
		if ( $this->formattedDefinitions === null ) {
235
			$this->buildFormattedDefinitions();
236
		}
237
238
		return $this->formattedDefinitions;
239
	}
240
241
	/**
242
	 */
243
	protected function buildFormattedDefinitions() {
244
245
		// Wrap definition in a <div> tag
246
		$divDefinitions = [];
247
		$divDefinitions[] = '<div class="mw-lingo-tooltip ' . $this->mDefinitions[ 0 ][ self::ELEMENT_STYLE ] . '" id="' . $this->getId() . '" >';
248
249
		foreach ( $this->mDefinitions as $definition ) {
250
251
			$divDefinitions[] = '<div class="mw-lingo-definition">';
252
253
			$divDefinitions[] = '<div class="mw-lingo-definition-text ' . $this->mDefinitions[ 0 ][ self::ELEMENT_STYLE ] . "\">\n";
254
			$divDefinitions[] = $definition[ self::ELEMENT_DEFINITION ];
255
			$divDefinitions[] = "\n" . '</div>';
256
257
			if ( $definition[ self::ELEMENT_LINK ] ) {
258
259
				if ( wfParseUrl( $definition[ self::ELEMENT_LINK ] ) !== false ) {
260
					$url = $definition[ self::ELEMENT_LINK ];
261
				} else {
262
					$url = Title::newFromText( $definition[ self::ELEMENT_LINK ] )->getFullURL();
263
				}
264
265
				if ( $url !== null ) {
266
					$divDefinitions[] = '<div class="mw-lingo-definition-link">[' . $url . ' <nowiki/>]</div>';
267
				}
268
			}
269
270
			$divDefinitions[] = '</div>';
271
		}
272
273
		$divDefinitions[] = "\n" . '</div>';
274
275
		$this->formattedDefinitions = join( $divDefinitions );
276
	}
277
278
	/**
279
	 * @return string
280
	 */
281
	public function getId() {
282
		return md5( $this->mTerm );
283
	}
284
285
	/**
286
	 * @return mixed
287
	 */
288
	public function getCurrentKey() {
289
		return key( $this->mDefinitions );
290
	}
291
292
	/**
293
	 * @param $key
294
	 *
295
	 * @return mixed
296
	 */
297
	public function getTerm( $key ) {
298
		return $this->mDefinitions[ $key ][ self::ELEMENT_TERM ];
299
	}
300
301
	/**
302
	 * @param $key
303
	 *
304
	 * @return mixed
305
	 */
306
	public function getSource( &$key ) {
307
		return $this->mDefinitions[ $key ][ self::ELEMENT_SOURCE ];
308
	}
309
310
	/**
311
	 * @param $key
312
	 *
313
	 * @return mixed
314
	 */
315
	public function getDefinition( &$key ) {
316
		return $this->mDefinitions[ $key ][ self::ELEMENT_DEFINITION ];
317
	}
318
319
	/**
320
	 * @param $key
321
	 *
322
	 * @return mixed
323
	 */
324
	public function getLink( &$key ) {
325
		return $this->mDefinitions[ $key ][ self::ELEMENT_LINK ];
326
	}
327
328
	/**
329
	 * @param $key
330
	 *
331
	 * @return mixed
332
	 */
333
	public function getStyle( &$key ) {
334
		return $this->mDefinitions[ $key ][ self::ELEMENT_STYLE ];
335
	}
336
337
	public function next() {
338
		next( $this->mDefinitions );
339
	}
340
341
}
342