xref: /dokuwiki/inc/Menu/Item/AbstractItem.php (revision 9f236ef28b069f14b4bd71cac7b91de7296dcf18)
1<?php
2
3namespace dokuwiki\Menu\Item;
4
5/**
6 * Class AbstractItem
7 *
8 * This class defines a single Item to be displayed in one of DokuWiki's menus. Plugins
9 * can extend those menus through action plugins and add their own instances of this class,
10 * overwriting some of its properties.
11 *
12 * Items may be shown multiple times in different contexts. Eg. for the default template
13 * all menus are shown in a Dropdown list on mobile, but are split into several places on
14 * desktop. The item's $context property can be used to hide the item depending on the current
15 * context.
16 *
17 * Children usually just need to overwrite the different properties, but for complex things
18 * the accessors may be overwritten instead.
19 */
20abstract class AbstractItem {
21
22    /** menu item is to be shown on desktop screens only */
23    const CTX_DESKTOP = 1;
24    /** menu item is to be shown on mobile screens only */
25    const CTX_MOBILE = 2;
26    /** menu item is to be shown in all contexts */
27    const CTX_ALL = 3;
28
29    /** @var string name of the action, usually the lowercase class name */
30    protected $type = '';
31    /** @var string optional keyboard shortcut */
32    protected $accesskey = '';
33    /** @var string the page id this action links to */
34    protected $id = '';
35    /** @var string the method to be used when this action is used in a form */
36    protected $method = 'get';
37    /** @var array parameters for the action (should contain the do parameter) */
38    protected $params = array();
39    /** @var bool when true, a rel=nofollow should be used */
40    protected $nofollow = true;
41    /** @var string this item's label may contain a placeholder, which is replaced with this */
42    protected $replacement = '';
43    /** @var string the full path to the SVG icon of this menu item */
44    protected $svg = DOKU_INC . 'lib/images/menu/00-default_checkbox-blank-circle-outline.svg';
45    /** @var string can be set to overwrite the default lookup in $lang.btn_* */
46    protected $label = '';
47    /** @var int the context this titme is shown in */
48    protected $context = self::CTX_ALL;
49
50    /**
51     * AbstractItem constructor.
52     *
53     * Sets the dynamic properties
54     *
55     * Children should always call the parent constructor!
56     *
57     * @throws \RuntimeException when the action is disabled
58     */
59    public function __construct() {
60        global $ID;
61        $this->id = $ID;
62        $this->type = $this->getType();
63        $this->params['do'] = $this->type;
64
65        if(!actionOK($this->type)) throw new \RuntimeException("action disabled: {$this->type}");
66    }
67
68    /**
69     * Return this item's label
70     *
71     * When the label property was set, it is simply returned. Otherwise, the action's type
72     * is used to look up the translation in the main language file and, if used, the replacement
73     * is applied.
74     *
75     * @return string
76     */
77    public function getLabel() {
78        if($this->label !== '') return $this->label;
79
80        /** @var array $lang */
81        global $lang;
82        $label = $lang['btn_' . $this->type];
83        if(strpos($label, '%s')) {
84            $label = sprintf($label, $this->replacement);
85        }
86        if($label === '') $label = '[' . $this->type . ']';
87        return $label;
88    }
89
90    /**
91     * Return the link this item links to
92     *
93     * Basically runs wl() on $id and $params. However if the ID is a hash it is used directly
94     * as the link
95     *
96     * Please note that the generated URL is *not* XML escaped.
97     *
98     * @see wl()
99     * @return string
100     */
101    public function getLink() {
102        if($this->id[0] == '#') {
103            return $this->id;
104        } else {
105            return wl($this->id, $this->params, false, '&');
106        }
107    }
108
109    /**
110     * Convenience method to get the attributes for constructing an <a> element
111     *
112     * @see buildAttributes()
113     * @param string|false $classprefix create a class from type with this prefix, false for no class
114     * @return array
115     */
116    public function getLinkAttributes($classprefix = 'menuitem ') {
117        $attr = array(
118            'href' => $this->getLink(),
119            'title' => $this->getLabel(),
120        );
121        if($this->isNofollow()) $attr['rel'] = 'nofollow';
122        if($this->getAccesskey()) {
123            $attr['accesskey'] = $this->getAccesskey();
124            $attr['title'] .= ' [' . $this->getAccesskey() . ']';
125        }
126        if($classprefix !== false) $attr['class'] = $classprefix . $this->getType();
127
128        return $attr;
129    }
130
131    /**
132     * Convenience method to create a full <a> element
133     *
134     * Wraps around the label and SVG image
135     *
136     * @param string|false $classprefix create a class from type with this prefix, false for no class
137     * @param bool $svg add SVG icon to the link
138     * @return string
139     */
140    public function asHtmlLink($classprefix = 'menuitem ', $svg = true) {
141        $attr = buildAttributes($this->getLinkAttributes($classprefix));
142        $html = "<a $attr>";
143        if($svg) {
144            $html .= '<span>' . hsc($this->getLabel()) . '</span>';
145            $html .= inlineSVG($this->getSvg());
146        } else {
147            $html .= hsc($this->getLabel());
148        }
149        $html .= "</a>";
150
151        return $html;
152    }
153
154    /**
155     * Should this item be shown in the given context
156     *
157     * @param int $ctx the current context
158     * @return bool
159     */
160    public function visibleInContext($ctx) {
161        return (bool) ($ctx & $this->context);
162    }
163
164    /**
165     * @return string the name of this item
166     */
167    public function getType() {
168        if($this->type === '') {
169            $this->type = strtolower(substr(strrchr(get_class($this), '\\'), 1));
170        }
171        return $this->type;
172    }
173
174    /**
175     * @return string
176     */
177    public function getAccesskey() {
178        return $this->accesskey;
179    }
180
181    /**
182     * @return array
183     */
184    public function getParams() {
185        return $this->params;
186    }
187
188    /**
189     * @return bool
190     */
191    public function isNofollow() {
192        return $this->nofollow;
193    }
194
195    /**
196     * @return string
197     */
198    public function getSvg() {
199        return $this->svg;
200    }
201
202    /**
203     * Return this Item's settings as an array as used in tpl_get_action()
204     *
205     * @return array
206     */
207    public function getLegacyData() {
208        return array(
209            'accesskey' => $this->accesskey ? $this->accesskey : null,
210            'type' => $this->type,
211            'id' => $this->id,
212            'method' => $this->method,
213            'params' => $this->params,
214            'nofollow' => $this->nofollow,
215            'replacement' => $this->replacement
216        );
217    }
218}
219