Completed
Push — master ( 7aacb2...2b39b6 )
by
unknown
06:55
created

Button::calculateClassAttribute()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 13
cts 13
cp 1
rs 9.3222
c 0
b 0
f 0
cc 5
nc 16
nop 0
crap 5
1
<?php
2
/**
3
 * Contains the component class for rendering a button.
4
 *
5
 * @copyright (C) 2018, Tobias Oetterer, Paderborn University
6
 * @license       https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License, version 3 (or later)
7
 *
8
 * This file is part of the MediaWiki extension BootstrapComponents.
9
 * The BootstrapComponents extension is free software: you can redistribute it
10
 * and/or modify it under the terms of the GNU General Public License as published
11
 * by the Free Software Foundation, either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * The BootstrapComponents extension is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21
 *
22
 * @file
23
 * @ingroup       BootstrapComponents
24
 * @author        Tobias Oetterer
25
 */
26
27
namespace BootstrapComponents\Components;
28
29
use BootstrapComponents\AbstractComponent;
30
use \Html;
31
use \Title;
32
33
/**
34
 * Class Button
35
 *
36
 * Class for component 'button'
37
 *
38
 * @see   https://github.com/oetterer/BootstrapComponents/blob/master/docs/components.md#Button
39
 * @since 1.0
40
 */
41
class Button extends AbstractComponent {
42
	/**
43
	 * @var array $rawAttributes
44
	 */
45
	private $rawAttributes = [];
46
47
	/**
48
	 * Allows spawning objects (like {@see \BootstrapComponents\Collapse}) to insert additional data into the button tag.
49
	 * These attributes and their values will neither be parsed or verified. It is assumed, that you know what you are doing.
50
	 *
51
	 * @param array $rawAttributes of form attribute => value
52
	 */
53 3
	public function injectRawAttributes( array $rawAttributes ) {
54 3
		if ( is_array( $rawAttributes ) && count( $rawAttributes ) ) {
55 3
			$this->rawAttributes += $rawAttributes;
56 3
		}
57 3
	}
58
59
	/**
60
	 * @inheritdoc
61
	 *
62
	 * @param string $input
63
	 */
64 11
	protected function placeMe( $input ) {
65 11
		if ( empty( $input ) ) {
66 2
			return $this->getParserOutputHelper()->renderErrorMessage( 'bootstrap-components-button-target-missing' );
67
		}
68
69 10
		list( $target, $text ) = $this->getTargetAndText( $input );
70
71 10
		if ( empty( $target ) ) {
72 1
			return $this->getParserOutputHelper()->renderErrorMessage( 'bootstrap-components-button-target-invalid' );
73
		}
74
75 9
		list ( $class, $style ) = $this->processCss(
76 9
			$this->calculateClassAttribute(),
77 9
			[]
78 9
		);
79
80
		return [
81 9
			Html::rawElement(
82 9
				'a',
83
				[
84 9
					'class' => $this->arrayToString( $class, ' ' ),
85 9
					'style' => $this->arrayToString( $style, ';' ),
86 9
					'role'  => 'button',
87 9
					'id'    => $this->getId(),
88 9
					'href'  => $target,
89 9
				] + $this->rawAttributes,
90
				$text
91 9
			),
92 9
			'isHTML'  => true,
93 9
			'noparse' => true,
94 9
		];
95
	}
96
97
	/**
98
	 * Calculates the css class string from the attributes array.
99
	 *
100
	 * @return string[]
101
	 */
102 9
	private function calculateClassAttribute() {
103
104 9
		$class = [ "btn" ];
105 9
		$colorClass = 'btn-';
106 9
		if ( (bool)$this->getValueFor( 'outline' ) ) {
107 4
			$colorClass .= 'outline-';
108 4
		}
109 9
		$class[] = $colorClass . $this->getValueFor( 'color', 'primary' );
110 3
		if ( $size = $this->getValueFor( 'size' ) ) {
111 3
			$class[] = "btn-" . $size;
112 9
		}
113 3
		if ( $this->getValueFor( 'active' ) ) {
114 3
			$class[] = 'active';
115 9
		}
116
		if ( $this->getValueFor( 'disabled' ) ) {
117
			$class[] = 'disabled';
118
		}
119
		return $class;
120
	}
121
122
	/**
123
	 * Generates a valid target a suitable text for the button
124
	 *
125 10
	 * @param string $input
126 10
	 *
127 10
	 * @return array containing (string|null) target, (string) text. Note that target is null when invalid
128 10
	 */
129 8
	private function getTargetAndText( $input ) {
130 8
		$target = trim( $input );
131 10
		$text = (string)$this->getValueFor( 'text' );
132
		if ( empty( $text ) ) {
133 7
			$text = $target;
134 7
		}
135 7
		if ( strlen( $target ) && !preg_match( '/^#[A-Za-z_0-9-]+/', $target ) ) {
136 10
			// $target is not a fragment (e.g. not #anchor)
137 10
			$targetTitle = Title::newFromText( $target );
138
			$target = $targetTitle ? $targetTitle->getLocalURL() : null;
139
		}
140
		list( $text, $target ) = $this->stripLinksFrom( $text, $target );
141
		return [ $target, $text ];
142
	}
143
144
	/**
145
	 * @param string $text
146 10
	 * @param string $target
147 10
	 *
148
	 * @return string[]
149
	 */
150
	private function stripLinksFrom( $text, $target ) {
151
		if ( preg_match( '~<a.+href=.([^>]+Special:Upload[^"]+)[^>]*>(.+)</a>~', $text, $matches ) ) {
152 2
			// we have an non existing image as text, return image name as text and upload url as target
153 2
			// since $text was already parsed and html_encoded and Html::rawElement will do this again,
154
			// we need to decode the html special characters in target aka $matches[1]
155 10
			// also clear additionally injected raw attributes
156
			$this->rawAttributes = [];
157
			return [ $matches[2], htmlspecialchars_decode( $matches[1] ) ];
158
		}
159
		return [ preg_replace( '~^(.*)(<a.+href=[^>]+>)(.+)(</a>)(.*)$~ms', '\1\3\5', $text ), $target ];
160
	}
161
}
162