Skip to content
Snippets Groups Projects
Select Git revision
  • cec90ca497d70ff9ebbdc27958f9540844395e7f
  • main default protected
  • studip-rector
  • ci-opt
  • course-members-export-as-word
  • data-vue-app
  • pipeline-improvements
  • webpack-optimizations
  • rector
  • icon-renewal
  • http-client-and-factories
  • jsonapi-atomic-operations
  • vueify-messages
  • tic-2341
  • 135-translatable-study-areas
  • extensible-sorm-action-parameters
  • sorm-configuration-trait
  • jsonapi-mvv-routes
  • docblocks-for-magic-methods
19 results

LinkElement.php

Blame
  • 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]);
        }
    }