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