You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

265 lines
7.5 KiB

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