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 23 /** menu item is to be shown on desktop screens only */ 24 const CTX_DESKTOP = 1; 25 /** menu item is to be shown on mobile screens only */ 26 const CTX_MOBILE = 2; 27 /** menu item is to be shown in all contexts */ 28 const CTX_ALL = 3; 29 30 /** @var string name of the action, usually the lowercase class name */ 31 protected $type = ''; 32 /** @var string optional keyboard shortcut */ 33 protected $accesskey = ''; 34 /** @var string the page id this action links to */ 35 protected $id = ''; 36 /** @var string the method to be used when this action is used in a form */ 37 protected $method = 'get'; 38 /** @var array parameters for the action (should contain the do parameter) */ 39 protected $params = []; 40 /** @var bool when true, a rel=nofollow should be used */ 41 protected $nofollow = true; 42 /** @var string this item's label may contain a placeholder, which is replaced with this */ 43 protected $replacement = ''; 44 /** @var string the full path to the SVG icon of this menu item */ 45 protected $svg = DOKU_INC . 'lib/images/menu/00-default_checkbox-blank-circle-outline.svg'; 46 /** @var string can be set to overwrite the default lookup in $lang.btn_* */ 47 protected $label = ''; 48 /** @var string the tooltip title, defaults to $label */ 49 protected $title = ''; 50 /** @var int the context this titme is shown in */ 51 protected $context = self::CTX_ALL; 52 53 /** 54 * AbstractItem constructor. 55 * 56 * Sets the dynamic properties 57 * 58 * Children should always call the parent constructor! 59 * 60 * @throws \RuntimeException when the action is disabled 61 */ 62 public function __construct() 63 { 64 global $ID; 65 $this->id = $ID; 66 $this->type = $this->getType(); 67 $this->params['do'] = $this->type; 68 69 if (!actionOK($this->type)) throw new \RuntimeException("action disabled: {$this->type}"); 70 } 71 72 /** 73 * Return this item's label 74 * 75 * When the label property was set, it is simply returned. Otherwise, the action's type 76 * is used to look up the translation in the main language file and, if used, the replacement 77 * is applied. 78 * 79 * @return string 80 */ 81 public function getLabel() 82 { 83 if ($this->label !== '') return $this->label; 84 85 /** @var array $lang */ 86 global $lang; 87 $label = $lang['btn_' . $this->type]; 88 if (strpos($label, '%s')) { 89 $label = sprintf($label, $this->replacement); 90 } 91 if ($label === '') $label = '[' . $this->type . ']'; 92 return $label; 93 } 94 95 /** 96 * Return this item's title 97 * 98 * This title should be used to display a tooltip (using the HTML title attribute). If 99 * a title property was not explicitly set, the label will be returned. 100 * 101 * @return string 102 */ 103 public function getTitle() 104 { 105 if ($this->title === '') return $this->getLabel(); 106 return $this->title; 107 } 108 109 /** 110 * Return the link this item links to 111 * 112 * Basically runs wl() on $id and $params. However if the ID is a hash it is used directly 113 * as the link 114 * 115 * Please note that the generated URL is *not* XML escaped. 116 * 117 * @return string 118 * @see wl() 119 */ 120 public function getLink() 121 { 122 if ($this->id && $this->id[0] == '#') { 123 return $this->id; 124 } else { 125 return wl($this->id, $this->params, false, '&'); 126 } 127 } 128 129 /** 130 * Convenience method to get the attributes for constructing an <a> element 131 * 132 * @param string|false $classprefix create a class from type with this prefix, false for no class 133 * @return array 134 * @see buildAttributes() 135 */ 136 public function getLinkAttributes($classprefix = 'menuitem ') 137 { 138 $attr = ['href' => $this->getLink(), 'title' => $this->getTitle()]; 139 if ($this->isNofollow()) $attr['rel'] = 'nofollow'; 140 if ($this->getAccesskey()) { 141 $attr['accesskey'] = $this->getAccesskey(); 142 $attr['title'] .= ' [' . $this->getAccesskey() . ']'; 143 } 144 if ($classprefix !== false) $attr['class'] = $classprefix . $this->getType(); 145 146 return $attr; 147 } 148 149 /** 150 * Convenience method to create a full <a> element 151 * 152 * Wraps around the label and SVG image 153 * 154 * @param string|false $classprefix create a class from type with this prefix, false for no class 155 * @param bool $svg add SVG icon to the link 156 * @return string 157 */ 158 public function asHtmlLink($classprefix = 'menuitem ', $svg = true) 159 { 160 $attr = buildAttributes($this->getLinkAttributes($classprefix)); 161 $html = "<a $attr>"; 162 if ($svg) { 163 $html .= '<span>' . hsc($this->getLabel()) . '</span>'; 164 $html .= inlineSVG($this->getSvg()); 165 } else { 166 $html .= hsc($this->getLabel()); 167 } 168 $html .= "</a>"; 169 170 return $html; 171 } 172 173 /** 174 * Convenience method to create a <button> element inside it's own form element 175 * 176 * Uses html_btn() 177 * 178 * @return string 179 */ 180 public function asHtmlButton() 181 { 182 return html_btn( 183 $this->getType(), 184 $this->id, 185 $this->getAccesskey(), 186 $this->getParams(), 187 $this->method, 188 $this->getTitle(), 189 $this->getLabel(), 190 $this->getSvg() 191 ); 192 } 193 194 /** 195 * Should this item be shown in the given context 196 * 197 * @param int $ctx the current context 198 * @return bool 199 */ 200 public function visibleInContext($ctx) 201 { 202 return (bool)($ctx & $this->context); 203 } 204 205 /** 206 * @return string the name of this item 207 */ 208 public function getType() 209 { 210 if ($this->type === '') { 211 $this->type = strtolower(substr(strrchr(get_class($this), '\\'), 1)); 212 } 213 return $this->type; 214 } 215 216 /** 217 * @return string 218 */ 219 public function getAccesskey() 220 { 221 return $this->accesskey; 222 } 223 224 /** 225 * @return array 226 */ 227 public function getParams() 228 { 229 return $this->params; 230 } 231 232 /** 233 * @return bool 234 */ 235 public function isNofollow() 236 { 237 return $this->nofollow; 238 } 239 240 /** 241 * @return string 242 */ 243 public function getSvg() 244 { 245 return $this->svg; 246 } 247 248 /** 249 * Return this Item's settings as an array as used in tpl_get_action() 250 * 251 * @return array 252 */ 253 public function getLegacyData() 254 { 255 return [ 256 'accesskey' => $this->accesskey ?: null, 257 'type' => $this->type, 258 'id' => $this->id, 259 'method' => $this->method, 260 'params' => $this->params, 261 'nofollow' => $this->nofollow, 262 'replacement' => $this->replacement 263 ]; 264 } 265} 266