Select Git revision
PluginDispatcher.php
Forked from
Stud.IP / Stud.IP
Source project has a limited visibility.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
LinkElement.php 7.80 KiB
<?php
/**
* @author Jan-Hendrik Willms <tleilax+studip@gmail.com>
* @license GPL2 or any later version
*/
class LinkElement extends WidgetElement implements ArrayAccess
{
/**
* Create link by parsing a html chunk.
*
* @param String $html HTML chunk to parse
* @param Icon $icon Optional icon
* @return LinkElement Link element from parsed html
* @throws Exception if html can not be parsed
*/
public static function fromHTML($html, \Icon $icon = null)
{
$matched = preg_match('~(<a(?P<attributes>(?:\s+\w+=".*?")+)>\s*(?P<label>.*?)\s*</a>)~s', $html, $match);
if (!$matched) {
throw new Exception('Could not parse html');
}
$attributes = self::parseAttributes($match['attributes']);
$url = $attributes['href'] ?: '#';
unset($attributes['href']);
return new self(html_entity_decode($match['label']), html_entity_decode($url), $icon, $attributes);
}
/**
* Parse a string of html attributes into an associative array.
*
* @param String $text String of html attributes
* @return Array parsed attributes as key => value pairs
* @see https://gist.github.com/rodneyrehm/3070128
*/
protected static function parseAttributes($text)
{
$attributes = [];
$pattern = '#(?(DEFINE)
(?<name>[a-zA-Z][a-zA-Z0-9-:]*)
(?<value_double>"[^"]+")
(?<value_single>\'[^\']+\')
(?<value_none>[^\s>]+)
(?<value>((?&value_double)|(?&value_single)|(?&value_none)))
)
(?<n>(?&name))(=(?<v>(?&value)))?#xs';
if (preg_match_all($pattern, $text, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$attributes[$match['n']] = isset($match['v'])
? html_entity_decode(trim($match['v'], '\'"'))
: null;
}
}
return $attributes;
}
public $url;
public $label;
public $icon;
public $active = false;
public $attributes = [];
public $as_button = false;
/**
* create a link for a widget
*
* @param String $label Label/content of the link
* @param String $url URL/Location of the link (raw url, no entities)
* @param Icon $icon Icon for the link
* @param array $attributes HTML-attributes for the a-tag in an associative array.
*/
public function __construct($label, $url, \Icon $icon = null, $attributes = [])
{
parent::__construct();
// TODO: Remove this some versions after 5.0
$url = html_entity_decode($url);
$this->label = $label;
$this->url = $url;
$this->attributes = $attributes;
$this->icon = $icon;
}
/**
* Sets the active state of the element.
*
* @param bool $active Active state (optional, defaults to true)
* @return LinkElement instance to allow chaining
*/
public function setActive($active = true)
{
$this->active = $active;
return $this;
}
/**
* Sets the dialog options for the element. Passing false as $state will
* reset the dialog options to "none".
*
* @param mixed $active Dialog options (optional, defaults to blank/standard
* dialog)
* @return LinkElement instance to allow chaining
*/
public function asDialog($state = '')
{
if ($state !== false) {
$this->attributes['data-dialog'] = $state;
} else {
unset($this->attributes['data-dialog']);
}
return $this;
}
/**
* Defines whether the link should be rendered as a button/form with POST
* method.
*
* @param bool $active State (optional, defaults to true)
* @return LinkElement instance to allow chaining
*/
public function asButton($state = true)
{
$this->as_button = $state;
return $this;
}
/**
* Sets the target attribute of the element.
*
* @param string $target Target attribute
* @return LinkElement instance to allow chaining
*/
public function setTarget($target)
{
if ($target) {
$this->attributes['target'] = $target;
} else {
unset($this->attributes['target']);
}
return $this;
}
/**
* Adds a css class to the rendered element.
*
* @param string $clas CSS class to add
* @return LinkElement instance to allow chaining
*/
public function addClass($class)
{
$this->attributes['class'] = isset($this->attributes['class'])
? $this->attributes['class'] . " " . $class
: $class;
return $this;
}
/**
* Set disabled state
*
* @param boolean $state Disabled state
* @return LinkElement instance to allow chaining
*/
public function setDisabled($state = true)
{
if ($state) {
$this->attributes['disabled'] = true;
} else {
unset($this->attributes['disabled']);
}
return $this;
}
/**
* Returns whether the element is disabled.
*
* @return bool
*/
public function isDisabled()
{
return isset($this->attributes['disabled']) && $this->attributes['disabled'] !== false;
}
/**
* Renders the element.
*
* @return string
*/
public function render()
{
$disabled = $this->isDisabled();
if ($this->as_button && !$disabled) {
return $this->renderButton();
}
if ($this->active) {
$this->addClass('active');
}
$attributes = (array) $this->attributes;
if ($disabled) {
$tag = 'span';
} else {
$tag = 'a';
$attributes['href'] = $this->url;
}
return sprintf(
'<%1$s %2$s>%3$s %4$s</%1$s>',
$tag,
arrayToHtmlAttributes($attributes),
$this->icon ? $this->icon->asImg(null, ['class' => 'text-bottom']) : '',
htmlReady($this->label)
);
}
/**
* Renders the element as a button/form.
*
* @return string
*/
protected function renderButton()
{
return sprintf(
'<form action="%1$s" method="post" %2$s>%3$s<button type="submit">%4$s</button></form>',
htmlReady($this->url),
arrayToHtmlAttributes((array) $this->attributes),
CSRFProtection::tokenTag(),
htmlReady($this->label)
);
}
/**
* Returns whether the given url is valid.
*
* @param string $url URL to test
* @return bool
*/
protected function isURL($url)
{
return filter_var($url, FILTER_VALIDATE_URL) !== false;
}
// Array access for attributes
/**
* @todo Add bool return type when Stud.IP requires PHP8 minimal
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->attributes[$offset]);
}
/**
* @param $offset
* @return mixed
*
* @todo Add mixed return type when Stud.IP requires PHP8 minimal
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->attributes[$offset];
}
/**
* @todo Add void return type when Stud.IP requires PHP8 minimal
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
$this->attributes[$offset] = $value;
}
/**
* @todo Add void return type when Stud.IP requires PHP8 minimal
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->attributes[$offset]);
}
}