Completed
Push — master ( c102e0...8b9029 )
by
unknown
04:59
created

LingoElement::buildFullDefinition()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 0
cts 8
cp 0
rs 9.2
cc 4
eloc 6
nc 3
nop 1
crap 20
1
<?php
2
3
/**
4
 * File holding the Extensions\Lingo\LingoElement class.
5
 *
6
 * This file is part of the MediaWiki extension Lingo.
7
 *
8
 * @copyright 2011 - 2016, 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 Extensions\Lingo;
31
use DOMDocument;
32
use DOMElement;
33
use DOMNode;
34
use DOMText;
35
use MWException;
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 LingoElement {
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
	static private $mLinkTemplate = null;
53
	private $mFullDefinition = null;
54
	private $mDefinitions = array();
55
	private $mTerm = null;
56
	private $mHasBeenDisplayed = false;
57
58
	/**
59
	 * Extensions\Lingo\LingoElement constructor.
60
	 * @param $term
61
	 * @param $definition
62
	 */
63 1
	public function __construct( &$term, &$definition = null ) {
64
65 1
		$this->mTerm = $term;
66
67 1
		if ( $definition ) {
68 1
			$this->addDefinition( $definition );
69 1
		}
70 1
	}
71
72
	/**
73
	 * @param $definition
74
	 */
75
	public function addDefinition( &$definition ) {
76
		$this->mDefinitions[] = array_pad( $definition, self::ELEMENT_FIELDCOUNT, null );
77
	}
78
79
	/**
80
	 * @param DOMDocument $doc
81
	 * @return DOMNode|DOMText
82
	 */
83
	public function getFullDefinition( DOMDocument &$doc ) {
84
85
		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...
86
87
		if ( $wgexLingoDisplayOnce && $this->mHasBeenDisplayed ) {
88
			return $doc->createTextNode( $this->mTerm );
89
		}
90
91
		$this->buildFullDefinition( $doc );
92
		$this->mHasBeenDisplayed = true;
93
94
		return $this->mFullDefinition->cloneNode( true );
95
	}
96
97
	/**
98
	 * @param DOMDocument $doc
99
	 * @return DOMDocument
100
	 */
101
	private function buildFullDefinition( DOMDocument &$doc ) {
102
103
		// only create if not yet created
104
		if ( $this->mFullDefinition === null || $this->mFullDefinition->ownerDocument !== $doc ) {
105
106
			if ( $this->isSimpleLink() ) {
107
				$this->mFullDefinition = $this->getFullDefinitionAsLink( $doc );
108
			} else {
109
				$this->mFullDefinition = $this->getFullDefinitionAsTooltip( $doc );
110
			}
111
		}
112
	}
113
114
	/**
115
	 * @return bool
116
	 */
117
	private function isSimpleLink() {
118
		return count( $this->mDefinitions ) === 1 &&
119
			!is_string( $this->mDefinitions[ 0 ][ self::ELEMENT_DEFINITION ] ) &&
120
			is_string( $this->mDefinitions[ 0 ][ self::ELEMENT_LINK ] );
121
	}
122
123
	/**
124
	 * @param DOMDocument $doc
125
	 *
126
	 * @return DOMElement
127
	 * @throws MWException
128
	 */
129
	protected function getFullDefinitionAsLink( DOMDocument &$doc ) {
130
131
		// create Title object for target page
132
		$target = Title::newFromText( $this->mDefinitions[ 0 ][ self::ELEMENT_LINK ] );
133
134
		// create link element
135
		$link = $doc->createElement( 'a', $this->mDefinitions[ 0 ][ self::ELEMENT_TERM ] );
136
137
		// set the link target
138
		$link->setAttribute( 'href', $target->getLinkUrl() );
139
		$link = $this->addClassAttributeToLink( $target, $link );
140
		$link = $this->addTitleAttributeToLink( $target, $link );
141
142
		return $link;
143
	}
144
145
	/**
146
	 * @param $target
147
	 * @param $link
148
	 */
149
	protected function &addClassAttributeToLink( $target, &$link ) {
150
151
		// 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...
152
		// Cleanest would probably be to use Linker::link and parse it
153
		// back into a DOMElement, but we are in a somewhat time-critical
154
		// part here.
155
		$classes = '';
156
157
		if ( !$target->isKnown() ) {
158
			$classes .= 'new ';
159
		}
160
161
		if ( $target->isExternal() ) {
162
			$classes .= 'extiw ';
163
		}
164
165
		// set style
166
		$classes .= $this->mDefinitions[ 0 ][ self::ELEMENT_STYLE ];
167
168
		if ( $classes !== '' ) {
169
			$link->setAttribute( 'class', $classes );
170
		}
171
172
		return $link;
173
	}
174
175
	/**
176
	 * @param $target
177
	 * @param $link
178
	 */
179
	protected function &addTitleAttributeToLink( $target, &$link ) {
180
181
		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...
182
			// A link like [[#Foo]].  This used to mean an empty title
183
			// attribute, but that's silly.  Just don't output a title.
184
		} elseif ( $target->isKnown() ) {
185
			$link->setAttribute( 'title', $target->getPrefixedText() );
186
		} else {
187
			$link->setAttribute( 'title', wfMessage( 'red-link-title', $target->getPrefixedText() )->text() );
188
		}
189
190
		return $link;
191
	}
192
193
	/**
194
	 * @param DOMDocument $doc
195
	 *
196
	 * @return string
197
	 * @throws MWException
198
	 */
199
	protected function getFullDefinitionAsTooltip( DOMDocument &$doc ) {
200
201
		// Wrap term and definition in <span> tags
202
		$span = $doc->createElement( 'span' );
203
		$span->setAttribute( 'class', 'mw-lingo-tooltip ' . $this->mDefinitions[ 0 ][ self::ELEMENT_STYLE ] );
204
205
		// Wrap term in <span> tag, hidden
206
		wfSuppressWarnings();
207
		$spanTerm = $doc->createElement( 'span', htmlentities( $this->mTerm, ENT_COMPAT, 'UTF-8' ) );
208
209
		wfRestoreWarnings();
210
		$spanTerm->setAttribute( 'class', 'mw-lingo-tooltip-abbr' );
211
212
		// Wrap definition in a <span> tag
213
		$spanDefinition = $doc->createElement( 'span' );
214
		$spanDefinition->setAttribute( 'class', 'mw-lingo-tooltip-tip ' . $this->mDefinitions[ 0 ][ self::ELEMENT_STYLE ] );
215
216
		foreach ( $this->mDefinitions as $definition ) {
217
			wfSuppressWarnings();
218
			$element = $doc->createElement( 'span', htmlentities( $definition[ self::ELEMENT_DEFINITION ], ENT_COMPAT, 'UTF-8' ) );
219
			$element->setAttribute( 'class', 'mw-lingo-tooltip-definition ' . $this->mDefinitions[ 0 ][ self::ELEMENT_STYLE ] );
220
			wfRestoreWarnings();
221
			if ( $definition[ self::ELEMENT_LINK ] ) {
222
				$linkedTitle = Title::newFromText( $definition[ self::ELEMENT_LINK ] );
223
				if ( $linkedTitle ) {
224
					$link = $this->getLinkTemplate( $doc );
225
					$link->setAttribute( 'href', $linkedTitle->getFullURL() );
226
					$element->appendChild( $link );
227
				}
228
			}
229
			$spanDefinition->appendChild( $element );
230
		}
231
232
		// insert term and definition
233
		$span->appendChild( $spanTerm );
234
		$span->appendChild( $spanDefinition );
235
		return $span;
236
	}
237
238
	/**
239
	 * @param DOMDocument $doc
240
	 * @return DOMNode
241
	 */
242
	private function getLinkTemplate( DOMDocument &$doc ) {
243
		// create template if it does not yet exist
244
		if ( !self::$mLinkTemplate || ( self::$mLinkTemplate->ownerDocument !== $doc ) ) {
245
			global $wgScriptPath;
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...
246
247
			$linkimage = $doc->createElement( 'img' );
248
			$linkimage->setAttribute( 'src', $wgScriptPath . '/extensions/Lingo/styles/linkicon.png' );
249
250
			self::$mLinkTemplate = $doc->createElement( 'a' );
251
			self::$mLinkTemplate->appendChild( $linkimage );
252
		}
253
254
		return self::$mLinkTemplate->cloneNode( true );
255
	}
256
257
	/**
258
	 * @return mixed
259
	 */
260
	public function getCurrentKey() {
261
		return key( $this->mDefinitions );
262
	}
263
264
	/**
265
	 * @param $key
266
	 * @return mixed
267
	 */
268
	public function getTerm( $key ) {
269
		return $this->mDefinitions[ $key ][ self::ELEMENT_TERM ];
270
	}
271
272
	/**
273
	 * @param $key
274
	 * @return mixed
275
	 */
276
	public function getSource( &$key ) {
277
		return $this->mDefinitions[ $key ][ self::ELEMENT_SOURCE ];
278
	}
279
280
	/**
281
	 * @param $key
282
	 * @return mixed
283
	 */
284
	public function getDefinition( &$key ) {
285
		return $this->mDefinitions[ $key ][ self::ELEMENT_DEFINITION ];
286
	}
287
288
	/**
289
	 * @param $key
290
	 * @return mixed
291
	 */
292
	public function getLink( &$key ) {
293
		return $this->mDefinitions[ $key ][ self::ELEMENT_LINK ];
294
	}
295
296
	/**
297
	 * @param $key
298
	 * @return mixed
299
	 */
300
	public function getStyle( &$key ) {
301
		return $this->mDefinitions[ $key ][ self::ELEMENT_STYLE ];
302
	}
303
304
	public function next() {
305
		next( $this->mDefinitions );
306
	}
307
308
}
309