Completed
Push — master ( aae2fd...925a43 )
by
unknown
06:06
created

LingoElement::addClassAttributeToLink()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 25
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

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