xref: /dokuwiki/inc/Menu/Item/AbstractItem.php (revision 33b91513e25639a6c7eb35668484d29098f7c9b4)
193b8c351SAndreas Gohr<?php
293b8c351SAndreas Gohr
393b8c351SAndreas Gohrnamespace dokuwiki\Menu\Item;
493b8c351SAndreas Gohr
593b8c351SAndreas Gohr/**
693b8c351SAndreas Gohr * Class AbstractItem
793b8c351SAndreas Gohr *
893b8c351SAndreas Gohr * This class defines a single Item to be displayed in one of DokuWiki's menus. Plugins
993b8c351SAndreas Gohr * can extend those menus through action plugins and add their own instances of this class,
1093b8c351SAndreas Gohr * overwriting some of its properties.
1193b8c351SAndreas Gohr *
1293b8c351SAndreas Gohr * Items may be shown multiple times in different contexts. Eg. for the default template
1393b8c351SAndreas Gohr * all menus are shown in a Dropdown list on mobile, but are split into several places on
1493b8c351SAndreas Gohr * desktop. The item's $context property can be used to hide the item depending on the current
1593b8c351SAndreas Gohr * context.
1693b8c351SAndreas Gohr *
1793b8c351SAndreas Gohr * Children usually just need to overwrite the different properties, but for complex things
1893b8c351SAndreas Gohr * the accessors may be overwritten instead.
1993b8c351SAndreas Gohr */
20*33b91513SAndreas Gohrabstract class AbstractItem
21*33b91513SAndreas Gohr{
2293b8c351SAndreas Gohr
2393b8c351SAndreas Gohr    /** menu item is to be shown on desktop screens only */
2493b8c351SAndreas Gohr    const CTX_DESKTOP = 1;
2593b8c351SAndreas Gohr    /** menu item is to be shown on mobile screens only */
2693b8c351SAndreas Gohr    const CTX_MOBILE = 2;
2793b8c351SAndreas Gohr    /** menu item is to be shown in all contexts */
2893b8c351SAndreas Gohr    const CTX_ALL = 3;
2993b8c351SAndreas Gohr
30368ce258SAndreas Gohr    /** @var string name of the action, usually the lowercase class name */
3193b8c351SAndreas Gohr    protected $type = '';
32368ce258SAndreas Gohr    /** @var string optional keyboard shortcut */
3393b8c351SAndreas Gohr    protected $accesskey = '';
34368ce258SAndreas Gohr    /** @var string the page id this action links to */
3593b8c351SAndreas Gohr    protected $id = '';
36368ce258SAndreas Gohr    /** @var string the method to be used when this action is used in a form */
3793b8c351SAndreas Gohr    protected $method = 'get';
38368ce258SAndreas Gohr    /** @var array parameters for the action (should contain the do parameter) */
39*33b91513SAndreas Gohr    protected $params = [];
40368ce258SAndreas Gohr    /** @var bool when true, a rel=nofollow should be used */
4193b8c351SAndreas Gohr    protected $nofollow = true;
42368ce258SAndreas Gohr    /** @var string this item's label may contain a placeholder, which is replaced with this */
4393b8c351SAndreas Gohr    protected $replacement = '';
44368ce258SAndreas Gohr    /** @var string the full path to the SVG icon of this menu item */
45c2b9771aSAndreas Gohr    protected $svg = DOKU_INC . 'lib/images/menu/00-default_checkbox-blank-circle-outline.svg';
46368ce258SAndreas Gohr    /** @var string can be set to overwrite the default lookup in $lang.btn_* */
4793b8c351SAndreas Gohr    protected $label = '';
4850ca245cSAndreas Gohr    /** @var string the tooltip title, defaults to $label */
4950ca245cSAndreas Gohr    protected $title = '';
50368ce258SAndreas Gohr    /** @var int the context this titme is shown in */
5193b8c351SAndreas Gohr    protected $context = self::CTX_ALL;
5293b8c351SAndreas Gohr
53368ce258SAndreas Gohr    /**
54368ce258SAndreas Gohr     * AbstractItem constructor.
55368ce258SAndreas Gohr     *
56368ce258SAndreas Gohr     * Sets the dynamic properties
57368ce258SAndreas Gohr     *
58368ce258SAndreas Gohr     * Children should always call the parent constructor!
59368ce258SAndreas Gohr     *
60368ce258SAndreas Gohr     * @throws \RuntimeException when the action is disabled
61368ce258SAndreas Gohr     */
62*33b91513SAndreas Gohr    public function __construct()
63*33b91513SAndreas Gohr    {
6493b8c351SAndreas Gohr        global $ID;
6593b8c351SAndreas Gohr        $this->id = $ID;
6651b0b64bSAndreas Gohr        $this->type = $this->getType();
6793b8c351SAndreas Gohr        $this->params['do'] = $this->type;
6893b8c351SAndreas Gohr
6993b8c351SAndreas Gohr        if (!actionOK($this->type)) throw new \RuntimeException("action disabled: {$this->type}");
7093b8c351SAndreas Gohr    }
7193b8c351SAndreas Gohr
7293b8c351SAndreas Gohr    /**
7393b8c351SAndreas Gohr     * Return this item's label
7493b8c351SAndreas Gohr     *
7593b8c351SAndreas Gohr     * When the label property was set, it is simply returned. Otherwise, the action's type
7693b8c351SAndreas Gohr     * is used to look up the translation in the main language file and, if used, the replacement
7793b8c351SAndreas Gohr     * is applied.
7893b8c351SAndreas Gohr     *
7993b8c351SAndreas Gohr     * @return string
8093b8c351SAndreas Gohr     */
81*33b91513SAndreas Gohr    public function getLabel()
82*33b91513SAndreas Gohr    {
8393b8c351SAndreas Gohr        if ($this->label !== '') return $this->label;
8493b8c351SAndreas Gohr
8593b8c351SAndreas Gohr        /** @var array $lang */
8693b8c351SAndreas Gohr        global $lang;
8793b8c351SAndreas Gohr        $label = $lang['btn_' . $this->type];
8893b8c351SAndreas Gohr        if (strpos($label, '%s')) {
8993b8c351SAndreas Gohr            $label = sprintf($label, $this->replacement);
9093b8c351SAndreas Gohr        }
9193b8c351SAndreas Gohr        if ($label === '') $label = '[' . $this->type . ']';
9293b8c351SAndreas Gohr        return $label;
9393b8c351SAndreas Gohr    }
9493b8c351SAndreas Gohr
9593b8c351SAndreas Gohr    /**
9650ca245cSAndreas Gohr     * Return this item's title
9750ca245cSAndreas Gohr     *
9850ca245cSAndreas Gohr     * This title should be used to display a tooltip (using the HTML title attribute). If
9950ca245cSAndreas Gohr     * a title property was not explicitly set, the label will be returned.
10050ca245cSAndreas Gohr     *
10150ca245cSAndreas Gohr     * @return string
10250ca245cSAndreas Gohr     */
103*33b91513SAndreas Gohr    public function getTitle()
104*33b91513SAndreas Gohr    {
10550ca245cSAndreas Gohr        if ($this->title === '') return $this->getLabel();
10650ca245cSAndreas Gohr        return $this->title;
10750ca245cSAndreas Gohr    }
10850ca245cSAndreas Gohr
10950ca245cSAndreas Gohr    /**
11093b8c351SAndreas Gohr     * Return the link this item links to
11193b8c351SAndreas Gohr     *
11293b8c351SAndreas Gohr     * Basically runs wl() on $id and $params. However if the ID is a hash it is used directly
11393b8c351SAndreas Gohr     * as the link
11493b8c351SAndreas Gohr     *
1154875c354SAndreas Gohr     * Please note that the generated URL is *not* XML escaped.
1164875c354SAndreas Gohr     *
11793b8c351SAndreas Gohr     * @return string
118*33b91513SAndreas Gohr     * @see wl()
11993b8c351SAndreas Gohr     */
120*33b91513SAndreas Gohr    public function getLink()
121*33b91513SAndreas Gohr    {
1223a5cb034SPhy        if ($this->id && $this->id[0] == '#') {
12393b8c351SAndreas Gohr            return $this->id;
12493b8c351SAndreas Gohr        } else {
1254875c354SAndreas Gohr            return wl($this->id, $this->params, false, '&');
12693b8c351SAndreas Gohr        }
12793b8c351SAndreas Gohr    }
12893b8c351SAndreas Gohr
12993b8c351SAndreas Gohr    /**
13093b8c351SAndreas Gohr     * Convenience method to get the attributes for constructing an <a> element
13193b8c351SAndreas Gohr     *
13293b8c351SAndreas Gohr     * @param string|false $classprefix create a class from type with this prefix, false for no class
13393b8c351SAndreas Gohr     * @return array
134*33b91513SAndreas Gohr     * @see buildAttributes()
13593b8c351SAndreas Gohr     */
136*33b91513SAndreas Gohr    public function getLinkAttributes($classprefix = 'menuitem ')
137*33b91513SAndreas Gohr    {
138*33b91513SAndreas Gohr        $attr = ['href' => $this->getLink(), 'title' => $this->getTitle()];
13993b8c351SAndreas Gohr        if ($this->isNofollow()) $attr['rel'] = 'nofollow';
14093b8c351SAndreas Gohr        if ($this->getAccesskey()) {
14193b8c351SAndreas Gohr            $attr['accesskey'] = $this->getAccesskey();
14293b8c351SAndreas Gohr            $attr['title'] .= ' [' . $this->getAccesskey() . ']';
14393b8c351SAndreas Gohr        }
14493b8c351SAndreas Gohr        if ($classprefix !== false) $attr['class'] = $classprefix . $this->getType();
14593b8c351SAndreas Gohr
14693b8c351SAndreas Gohr        return $attr;
14793b8c351SAndreas Gohr    }
14893b8c351SAndreas Gohr
14993b8c351SAndreas Gohr    /**
15093b8c351SAndreas Gohr     * Convenience method to create a full <a> element
15193b8c351SAndreas Gohr     *
15293b8c351SAndreas Gohr     * Wraps around the label and SVG image
15393b8c351SAndreas Gohr     *
15493b8c351SAndreas Gohr     * @param string|false $classprefix create a class from type with this prefix, false for no class
1552f7a5efdSAndreas Gohr     * @param bool $svg add SVG icon to the link
15693b8c351SAndreas Gohr     * @return string
15793b8c351SAndreas Gohr     */
158*33b91513SAndreas Gohr    public function asHtmlLink($classprefix = 'menuitem ', $svg = true)
159*33b91513SAndreas Gohr    {
16093b8c351SAndreas Gohr        $attr = buildAttributes($this->getLinkAttributes($classprefix));
16193b8c351SAndreas Gohr        $html = "<a $attr>";
1622f7a5efdSAndreas Gohr        if ($svg) {
16393b8c351SAndreas Gohr            $html .= '<span>' . hsc($this->getLabel()) . '</span>';
164c3766a92SAndreas Gohr            $html .= inlineSVG($this->getSvg());
1652f7a5efdSAndreas Gohr        } else {
1662f7a5efdSAndreas Gohr            $html .= hsc($this->getLabel());
1672f7a5efdSAndreas Gohr        }
16893b8c351SAndreas Gohr        $html .= "</a>";
16993b8c351SAndreas Gohr
17093b8c351SAndreas Gohr        return $html;
17193b8c351SAndreas Gohr    }
17293b8c351SAndreas Gohr
17393b8c351SAndreas Gohr    /**
174b965a044SAndreas Gohr     * Convenience method to create a <button> element inside it's own form element
175b965a044SAndreas Gohr     *
176b965a044SAndreas Gohr     * Uses html_btn()
177b965a044SAndreas Gohr     *
178b965a044SAndreas Gohr     * @return string
179b965a044SAndreas Gohr     */
180*33b91513SAndreas Gohr    public function asHtmlButton()
181*33b91513SAndreas Gohr    {
182b965a044SAndreas Gohr        return html_btn(
183b965a044SAndreas Gohr            $this->getType(),
184b965a044SAndreas Gohr            $this->id,
185b965a044SAndreas Gohr            $this->getAccesskey(),
186b965a044SAndreas Gohr            $this->getParams(),
187b965a044SAndreas Gohr            $this->method,
188b965a044SAndreas Gohr            $this->getTitle(),
189679dba01SMichael Große            $this->getLabel(),
190e824d633SMichael Große            $this->getSvg()
191b965a044SAndreas Gohr        );
192b965a044SAndreas Gohr    }
193b965a044SAndreas Gohr
194b965a044SAndreas Gohr    /**
19593b8c351SAndreas Gohr     * Should this item be shown in the given context
19693b8c351SAndreas Gohr     *
19793b8c351SAndreas Gohr     * @param int $ctx the current context
19893b8c351SAndreas Gohr     * @return bool
19993b8c351SAndreas Gohr     */
200*33b91513SAndreas Gohr    public function visibleInContext($ctx)
201*33b91513SAndreas Gohr    {
20293b8c351SAndreas Gohr        return (bool)($ctx & $this->context);
20393b8c351SAndreas Gohr    }
20493b8c351SAndreas Gohr
20593b8c351SAndreas Gohr    /**
20693b8c351SAndreas Gohr     * @return string the name of this item
20793b8c351SAndreas Gohr     */
208*33b91513SAndreas Gohr    public function getType()
209*33b91513SAndreas Gohr    {
21051b0b64bSAndreas Gohr        if ($this->type === '') {
21151b0b64bSAndreas Gohr            $this->type = strtolower(substr(strrchr(get_class($this), '\\'), 1));
21251b0b64bSAndreas Gohr        }
21393b8c351SAndreas Gohr        return $this->type;
21493b8c351SAndreas Gohr    }
21593b8c351SAndreas Gohr
21693b8c351SAndreas Gohr    /**
21793b8c351SAndreas Gohr     * @return string
21893b8c351SAndreas Gohr     */
219*33b91513SAndreas Gohr    public function getAccesskey()
220*33b91513SAndreas Gohr    {
22193b8c351SAndreas Gohr        return $this->accesskey;
22293b8c351SAndreas Gohr    }
22393b8c351SAndreas Gohr
22493b8c351SAndreas Gohr    /**
22593b8c351SAndreas Gohr     * @return array
22693b8c351SAndreas Gohr     */
227*33b91513SAndreas Gohr    public function getParams()
228*33b91513SAndreas Gohr    {
22993b8c351SAndreas Gohr        return $this->params;
23093b8c351SAndreas Gohr    }
23193b8c351SAndreas Gohr
23293b8c351SAndreas Gohr    /**
23393b8c351SAndreas Gohr     * @return bool
23493b8c351SAndreas Gohr     */
235*33b91513SAndreas Gohr    public function isNofollow()
236*33b91513SAndreas Gohr    {
23793b8c351SAndreas Gohr        return $this->nofollow;
23893b8c351SAndreas Gohr    }
23993b8c351SAndreas Gohr
24093b8c351SAndreas Gohr    /**
24193b8c351SAndreas Gohr     * @return string
24293b8c351SAndreas Gohr     */
243*33b91513SAndreas Gohr    public function getSvg()
244*33b91513SAndreas Gohr    {
24593b8c351SAndreas Gohr        return $this->svg;
24693b8c351SAndreas Gohr    }
24793b8c351SAndreas Gohr
2484887c154SAndreas Gohr    /**
2494887c154SAndreas Gohr     * Return this Item's settings as an array as used in tpl_get_action()
2504887c154SAndreas Gohr     *
2514887c154SAndreas Gohr     * @return array
2524887c154SAndreas Gohr     */
253*33b91513SAndreas Gohr    public function getLegacyData()
254*33b91513SAndreas Gohr    {
255*33b91513SAndreas Gohr        return [
2562a85c691SAndreas Gohr            'accesskey' => $this->accesskey ?: null,
2574887c154SAndreas Gohr            'type' => $this->type,
2584887c154SAndreas Gohr            'id' => $this->id,
2594887c154SAndreas Gohr            'method' => $this->method,
2604887c154SAndreas Gohr            'params' => $this->params,
2614887c154SAndreas Gohr            'nofollow' => $this->nofollow,
2624887c154SAndreas Gohr            'replacement' => $this->replacement
263*33b91513SAndreas Gohr        ];
2644887c154SAndreas Gohr    }
26593b8c351SAndreas Gohr}
266