GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#1)
by Simon
05:07
created

NavbarHorizontal   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 463
Duplicated Lines 6.91 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 96.71%

Importance

Changes 15
Bugs 5 Features 3
Metric Value
wmc 51
c 15
b 5
f 3
lcom 1
cbo 8
dl 32
loc 463
ccs 206
cts 213
cp 0.9671
rs 8.3206

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getHtml() 0 8 2
A buildHtml() 0 13 2
A buildFixedNavBarIfRequested() 0 21 3
A buildNavBarOpeningTags() 0 15 1
A getHtmlId() 0 6 2
A buildNavBarComponents() 0 18 2
A buildNavBarElementsFromDomTree() 0 17 3
B buildAndCollectNavBarElementFromDomElement() 0 22 6
A buildNavBarElementFromDomElement() 0 13 2
A getLogo() 8 8 1
A getNavMenu() 7 7 1
C getPageTools() 0 61 13
A getSearchBar() 7 7 1
B getPersonalTools() 3 69 6
A getMenu() 7 7 1
A buildHead() 0 13 1
A buildTail() 0 7 1
A buildNavBarClosingTags() 0 5 1
B getLinkAndRemoveFromPageToolStructure() 0 30 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like NavbarHorizontal often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use NavbarHorizontal, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * File holding the NavbarHorizontal class
4
 *
5
 * This file is part of the MediaWiki skin Chameleon.
6
 *
7
 * @copyright 2013 - 2015, Stephan Gambke
8
 * @license   GNU General Public License, version 3 (or any later version)
9
 *
10
 * The Chameleon skin is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by the Free
12
 * Software Foundation, either version 3 of the License, or (at your option) any
13
 * later version.
14
 *
15
 * The Chameleon skin is distributed in the hope that it will be useful, but
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18
 * details.
19
 *
20
 * You should have received a copy of the GNU General Public License along
21
 * with this program. If not, see <http://www.gnu.org/licenses/>.
22
 *
23
 * @file
24
 * @ingroup   Skins
25
 */
26
27
namespace Skins\Chameleon\Components;
28
29
use Hooks;
30
use Skins\Chameleon\IdRegistry;
31
32
/**
33
 * The NavbarHorizontal class.
34
 *
35
 * A horizontal navbar containing the sidebar items.
36
 * Does not include standard items (toolbox, search, language links). They need
37
 * to be added to the page elsewhere
38
 *
39
 * The navbar is a list of lists wrapped in a nav element: <nav
40
 * role="navigation" id="p-navbar" >
41
 *
42
 * @author  Stephan Gambke
43
 * @since   1.0
44
 * @ingroup Skins
45
 */
46
class NavbarHorizontal extends Component {
47
48
	private $mHtml = null;
49
	private $htmlId = null;
50
51
	/**
52
	 * Builds the HTML code for this component
53
	 *
54
	 * @return String the HTML code
55
	 */
56 11
	public function getHtml() {
57
58 11
		if ( $this->mHtml === null ) {
59 11
			$this->buildHtml();
60 11
		}
61
62 11
		return $this->mHtml;
63
	}
64
65
	/**
66
	 *
67
	 */
68 11
	protected function buildHtml() {
69
70 11
		if ( $this->getDomElement() === null ) {
71 1
			$this->mHtml = '';
72 1
			return;
73
		}
74
75 10
		$this->mHtml =
76 10
			$this->buildFixedNavBarIfRequested() .
77 10
			$this->buildNavBarOpeningTags() .
78 10
			$this->buildNavBarComponents() .
79 10
			$this->buildNavBarClosingTags();
80 10
	}
81
82
	/**
83
	 *
84
	 */
85 10
	protected function buildFixedNavBarIfRequested() {
86
		// if a fixed navbar is requested
87 10
		if ( filter_var( $this->getDomElement()->getAttribute( 'fixed' ), FILTER_VALIDATE_BOOLEAN ) === true ||
88 10
			$this->getDomElement()->getAttribute( 'position' ) === 'fixed'
89 10
		) {
90
91
			// first build the actual navbar and set a class so it will be fixed
92 1
			$this->getDomElement()->setAttribute( 'fixed', '0' );
93 1
			$this->getDomElement()->setAttribute( 'position', '' );
94
95 1
			$realNav = new self( $this->getSkinTemplate(), $this->getDomElement(), $this->getIndent() );
96 1
			$realNav->setClasses( $this->getClassString() . ' navbar-fixed-top' );
97
98
			// then add an invisible copy of the nav bar that will act as a spacer
99 1
			$this->addClasses( 'navbar-static-top invisible' );
100
101 1
			return $realNav->getHtml();
102
		} else {
103 10
			return '';
104
		}
105
	}
106
107
	/**
108
	 * @return string
109
	 */
110 10
	protected function buildNavBarOpeningTags() {
111
		$openingTags =
112 10
			$this->indent() . '<!-- navigation bar -->' .
113 10
			$this->indent() . \HTML::openElement( 'nav', array(
114 10
					'class' => 'navbar navbar-default p-navbar ' . $this->getClassString(),
115 10
					'role'  => 'navigation',
116 10
					'id'    => $this->getHtmlId()
117 10
				)
118 10
			) .
119 10
			$this->indent( 1 ) . '<div class="container-fluid">';
120
121 10
		$this->indent( 1 );
122
123 10
		return $openingTags;
124
	}
125
126
	/**
127
	 * @return string
128
	 */
129 10
	private function getHtmlId() {
130 10
		if ( $this->htmlId === null ) {
131 10
			$this->htmlId = IdRegistry::getRegistry()->getId( 'mw-navigation' );
132 10
		}
133 10
		return $this->htmlId;
134
	}
135
136
	/**
137
	 *
138
	 */
139 10
	protected function buildNavBarComponents() {
140
141 10
		$elements = $this->buildNavBarElementsFromDomTree();
142
143 10
		if ( !empty( $elements[ 'right' ] ) ) {
144
145 4
			$elements[ 'left' ][ ] =
146 4
				$this->indent( 1 ) . '<div class="navbar-right-aligned">' .
147 4
				implode( $elements[ 'right' ] ) .
148 4
				$this->indent() . '</div> <!-- navbar-right-aligned -->';
149
150 4
			$this->indent( -1 );
151 4
		}
152
153
		return
154 10
			$this->buildHead( $elements[ 'head' ] ) .
155 10
			$this->buildTail( $elements[ 'left' ] );
156
	}
157
158
	/**
159
	 * @return string[][]
160
	 */
161 10
	protected function buildNavBarElementsFromDomTree() {
162
163
		$elements = array(
164 10
			'head'  => array(),
165 10
			'left'  => array(),
166 10
			'right' => array(),
167 10
		);
168
169
		/** @var \DOMElement[] $children */
170 10
		$children = $this->getDomElement()->hasChildNodes() ? $this->getDomElement()->childNodes : array();
171
172
		// add components
173 10
		foreach ( $children as $node ) {
174 9
			$this->buildAndCollectNavBarElementFromDomElement( $node, $elements );
175 10
		}
176 10
		return $elements;
177
	}
178
179
	/**
180
	 * @param \DOMElement $node
181
	 * @param $elements
182
	 */
183 9
	protected function buildAndCollectNavBarElementFromDomElement( $node, &$elements ) {
184
185 9
		if ( is_a( $node, 'DOMElement' ) && $node->tagName === 'component' && $node->hasAttribute( 'type' ) ) {
186
187 9
			$position = $node->getAttribute( 'position' );
188
189 9
			if ( !array_key_exists( $position, $elements ) ) {
190 9
				$position = 'left';
191 9
			}
192
193 9
			$indentation = ( $position === 'right' ) ? 2 : 1;
194
195 9
			$this->indent( $indentation );
196 9
			$html = $this->buildNavBarElementFromDomElement( $node );
197 9
			$this->indent( -$indentation );
198
199 9
			$elements[ $position ][ ] = $html;
200
201 9
		} else {
202
			// TODO: Warning? Error?
203
		}
204 9
	}
205
206
	/**
207
	 * @param \DomElement $node
208
	 *
209
	 * @return string
210
	 */
211 9
	protected function buildNavBarElementFromDomElement( $node ) {
212
213 9
		$type = $node->getAttribute( 'type' );
214 9
		$functionName = 'get' . $type;
215
216 9
		if (method_exists($this, $functionName)) {
217 9
			return $this->{$functionName}($node);
218
		} else {
219
			$className = __NAMESPACE__ . '\\' . $type;
220
			$component = new $className( $this->getSkinTemplate(), $node, $this->getIndent() );
221
			return '<ul class="nav navbar-nav ' . $type . '">' . $component->getHtml() . "</ul>\n";
222
		}
223
	}
224
225
	/**
226
	 * Creates HTML code for the wiki logo in a navbar
227
	 *
228
	 * @param \DOMElement $domElement
229
	 *
230
	 * @return String
231
	 */
232 7 View Code Duplication
	protected function getLogo( \DOMElement $domElement = null ) {
233
234 7
		$logo = new Logo( $this->getSkinTemplate(), $domElement, $this->getIndent() );
235 7
		$logo->addClasses( 'navbar-brand' );
236
237
//        return \Html::rawElement( 'li', array(), $logo->getHtml() );
238 7
		return $logo->getHtml();
239
	}
240
241
	/**
242
	 * Creates a list of navigational links usually found in the sidebar
243
	 *
244
	 * @param \DOMElement $domElement
245
	 *
246
	 * @return string
247
	 */
248 8 View Code Duplication
	protected function getNavMenu( \DOMElement $domElement = null ) {
249
250 8
		$navMenu = new NavMenu( $this->getSkinTemplate(), $domElement, $this->getIndent() );
251
252 8
		return '<ul class="nav navbar-nav">' . $navMenu->getHtml() . "</ul>\n";
253
254
	}
255
256
	/**
257
	 * Create a dropdown containing the page tools (page, talk, edit, history,
258
	 * ...)
259
	 *
260
	 * @param \DOMElement $domElement
261
	 *
262
	 * @return string
263
	 */
264 7
	protected function getPageTools( \DOMElement $domElement = null ) {
265
266 7
		$ret = '';
267
268 7
		$pageTools = new PageTools( $this->getSkinTemplate(), $domElement, $this->getIndent() + 1 );
269
270 7
		$pageTools->setFlat( true );
271 7
		$pageTools->removeClasses( 'text-center list-inline' );
272 7
		$pageTools->addClasses( 'dropdown-menu' );
273
274 7
		$editLinkHtml = '';
275 7
		$pageToolsStructure = $pageTools->getPageToolsStructure();
276
277 7
		if ( array_key_exists( 'views', $pageToolsStructure ) &&
278 7
			array_key_exists( 'form_edit', $pageToolsStructure[ 'views' ] ) &&
279 7
			array_key_exists( 'sfgRenameEditTabs', $GLOBALS ) &&
280
			$GLOBALS[ 'sfgRenameEditTabs' ] === true
281
282 7
		) {
283
284
			$editLinkHtml = $this->getLinkAndRemoveFromPageToolStructure( $pageTools, 'form_edit' );
285
286 7
		} elseif ( array_key_exists( 'views', $pageToolsStructure ) &&
287 7
			array_key_exists( 've-edit', $pageToolsStructure[ 'views' ] )
288 7
		) {
289
290
			$editLinkHtml = $this->getLinkAndRemoveFromPageToolStructure( $pageTools, 've-edit' );
291
292 7
		} elseif ( array_key_exists( 'views', $pageToolsStructure ) &&
293 7
			array_key_exists( 'edit', $pageToolsStructure[ 'views' ] )
294 7
		) {
295
296 7
			$editLinkHtml = $this->getLinkAndRemoveFromPageToolStructure( $pageTools, 'edit' );
297
298 7
		}
299
300 7
		$pageToolsHtml = $pageTools->getHtml();
301
302 7
		if ( $editLinkHtml || $pageToolsHtml ) {
303
			$ret =
304 7
				$this->indent() . '<!-- page tools -->' .
305 7
				$this->indent() . '<ul class="navbar-tools navbar-nav" >';
306
307 7
			if ( $editLinkHtml !== '' ) {
308 7
				$ret .= $this->indent( 1 ) . $editLinkHtml;
309 7
			}
310
311 7
			if ( $pageToolsHtml !== '' ) {
312
				$ret .=
313 7
					$this->indent( 1 ) . '<li class="navbar-tools-tools dropdown">' .
314 7
					$this->indent( 1 ) . '<a data-toggle="dropdown" class="dropdown-toggle" href="#" title="' . $this->getSkinTemplate()->getMsg( 'specialpages-group-pagetools' )->text() . '" ><span>...</span></a>' .
315 7
					$pageToolsHtml .
316 7
					$this->indent( -1 ) . '</li>';
317 7
			}
318
319
			$ret .=
320 7
				$this->indent( -1 ) . '</ul>' . "\n";
321 7
		}
322
323 7
		return $ret;
324
	}
325
326
	/**
327
	 * @param \DOMElement $domElement
328
	 *
329
	 * @return string
330
	 */
331 7 View Code Duplication
	protected function getSearchBar( \DOMElement $domElement = null ) {
332
333 7
		$search = new SearchBar( $this->getSkinTemplate(), $domElement, $this->getIndent() );
334 7
		$search->addClasses( 'navbar-form' );
335
336 7
		return $search->getHtml();
337
	}
338
339
	/**
340
	 * Creates a user's personal tools and the newtalk notifier
341
	 *
342
	 * @return string
343
	 */
344 7
	protected function getPersonalTools() {
345
346 7
		$user = $this->getSkinTemplate()->getSkin()->getUser();
347
348 7
		if ( $user->isLoggedIn() ) {
349 2
			$toolsClass = 'navbar-userloggedin';
350 2
			$toolsLinkText = $this->getSkinTemplate()->getMsg( 'chameleon-loggedin' )->params( $user->getName() )->text();
351 2
		} else {
352 5
			$toolsClass = 'navbar-usernotloggedin';
353 5
			$toolsLinkText = $this->getSkinTemplate()->getMsg( 'chameleon-notloggedin' )->text();
354
		}
355
356 7
		$linkText = '<span class="glyphicon glyphicon-user"></span>';
357 7
		\Hooks::run('ChameleonNavbarHorizontalPersonalToolsLinkText', array( &$linkText, $this->getSkin() ) );
358
359
		// start personal tools element
360
		$ret =
361 7
			$this->indent() . '<!-- personal tools -->' .
362 7
			$this->indent() . '<ul class="navbar-tools navbar-nav" >' .
363 7
			$this->indent( 1 ) . '<li class="dropdown navbar-tools-tools">' .
364 7
			$this->indent( 1 ) . '<a class="dropdown-toggle ' . $toolsClass . '" href="#" data-toggle="dropdown" title="' . $toolsLinkText . '" >' . $linkText . '</a>' .
365 7
			$this->indent() . '<ul class="p-personal-tools dropdown-menu dropdown-menu-right" >';
366
367 7
		$this->indent( 1 );
368
369
		// add personal tools (links to user page, user talk, prefs, ...)
370 7 View Code Duplication
		foreach ( $this->getSkinTemplate()->getPersonalTools() as $key => $item ) {
371 7
			$ret .= $this->indent() . $this->getSkinTemplate()->makeListItem( $key, $item );
372 7
		}
373
374
		$ret .=
375 7
			$this->indent( -1 ) . '</ul>' .
376 7
			$this->indent( -1 ) . '</li>';
377
378
		// if the user is logged in, add the newtalk notifier
379 7
		if ( $user->isLoggedIn() ) {
380
381 2
			$newMessagesAlert = '';
382 2
			$newtalks = $user->getNewMessageLinks();
383 2
			$out = $this->getSkinTemplate()->getSkin()->getOutput();
384
385
			// Allow extensions to disable the new messages alert;
386
			// since we do not display the link text, we ignore the actual value returned in $newMessagesAlert
387 2
			if ( Hooks::run( 'GetNewMessagesAlert', array( &$newMessagesAlert, $newtalks, $user, $out ) ) ) {
388
389 2
				if ( count( $user->getNewMessageLinks() ) > 0 ) {
390 1
					$newtalkClass = 'navbar-newtalk-available';
391 1
					$newtalkLinkText = $this->getSkinTemplate()->getMsg( 'chameleon-newmessages' )->text();
392 1
				} else {
393 1
					$newtalkClass = 'navbar-newtalk-not-available';
394 1
					$newtalkLinkText = $this->getSkinTemplate()->getMsg( 'chameleon-nonewmessages' )->text();
395
				}
396
397 2
				$linkText = '<span class="glyphicon glyphicon-envelope"></span>';
398 2
				\Hooks::run('ChameleonNavbarHorizontalNewTalkLinkText', array( &$linkText, $this->getSkin() ) );
399
400 2
				$ret .= $this->indent() . '<li class="navbar-newtalk-notifier">' .
401 2
					$this->indent( 1 ) . '<a class="dropdown-toggle ' . $newtalkClass . '" title="' .
402 2
					$newtalkLinkText . '" href="' . $user->getTalkPage()->getLinkURL( 'redirect=no' ) . '">' . $linkText . '</a>' .
403 2
					$this->indent( -1 ) . '</li>';
404
405 2
			}
406
407 2
		}
408
409 7
		$ret .= $this->indent( -1 ) . '</ul>' . "\n";
410
411 7
		return $ret;
412
	}
413
414
	/**
415
	 * Creates a list of navigational links from a message key or message text
416
	 *
417
	 * @param \DOMElement $domElement
418
	 *
419
	 * @return string
420
	 */
421 1 View Code Duplication
	protected function getMenu( \DOMElement $domElement = null ) {
422
423 1
		$menu = new Menu( $this->getSkinTemplate(), $domElement, $this->getIndent() );
424
425 1
		return '<ul class="nav navbar-nav">' . $menu->getHtml() . "</ul>\n";
426
427
	}
428
429
430
	/**
431
	 * @param string[] $headElements
432
	 *
433
	 * @return string
434
	 */
435 10
	protected function buildHead( $headElements ) {
436
437
		$head =
438 10
			$this->indent() . "<div class=\"navbar-header\">\n" .
439 10
			$this->indent( 1 ) . "<button type=\"button\" class=\"navbar-toggle collapsed\" data-toggle=\"collapse\" data-target=\"#" . $this->getHtmlId() . "-collapse\">" .
440 10
			$this->indent( 1 ) . "<span class=\"sr-only\">Toggle navigation</span>" .
441 10
			$this->indent() . str_repeat( "<span class=\"icon-bar\"></span>", 3 ) .
442 10
			$this->indent( -1 ) . "</button>\n" .
443 10
			implode( '', $headElements ) . "\n" .
444 10
			$this->indent( -1 ) . "</div>\n";
445
446 10
		return $head;
447
	}
448
449
	/**
450
	 * @param string[] $tailElements
451
	 *
452
	 * @return string
453
	 */
454 10
	protected function buildTail( $tailElements ) {
455
456
		return
457 10
			$this->indent() . '<div class="collapse navbar-collapse" id="' . $this->getHtmlId() . '-collapse">' .
458 10
			implode( '', $tailElements ) .
459 10
			$this->indent() . '</div><!-- /.navbar-collapse -->';
460
	}
461
462
	/**
463
	 * @return string
464
	 */
465 10
	protected function buildNavBarClosingTags() {
466
		return
467 10
			$this->indent( -1 ) . '</div>' .
468 10
			$this->indent( -1 ) . '</nav>' . "\n";
469
	}
470
471
	/**
472
	 * @param $pageTools
473
	 * @param $editActionId
474
	 *
475
	 * @return string
476
	 */
477 7
	protected function getLinkAndRemoveFromPageToolStructure( $pageTools, $editActionId ) {
478
479 7
		$pageToolsStructure  = $pageTools->getPageToolsStructure();
480 7
		$editActionStructure = $pageToolsStructure[ 'views' ][ $editActionId ];
481
482 7
		$editActionStructure[ 'text' ] = '';
483
484 7
		if ( array_key_exists( 'class', $editActionStructure ) ) {
485 7
			$editActionStructure[ 'class' ] .= ' navbar-tools-tools';
486 7
		} else {
487
			$editActionStructure[ 'class' ] = 'navbar-tools-tools';
488
		}
489
490
		$options = array (
491
			'text-wrapper' => array(
492 7
				'tag' => 'span',
493 7
				'attributes' => array('class' => 'glyphicon glyphicon-pencil',)
494 7
			),
495 7
		);
496
497 7
		$editLinkHtml = $this->getSkinTemplate()->makeListItem(
498 7
			$editActionId,
499 7
			$editActionStructure,
500
			$options
501 7
		);
502
503 7
		$pageTools->setRedundant( $editActionId );
504
505 7
		return $editLinkHtml;
506
	}
507
508
}
509