1e1d9dcc8SAndreas Gohr<?php 2e1d9dcc8SAndreas Gohr 3e1d9dcc8SAndreas Gohrnamespace dokuwiki\Extension; 4e1d9dcc8SAndreas Gohr 53d041397SAndreas Gohruse dokuwiki\Logger; 63d041397SAndreas Gohr 7e1d9dcc8SAndreas Gohr/** 8e1d9dcc8SAndreas Gohr * Provides standard DokuWiki plugin behaviour 9e1d9dcc8SAndreas Gohr */ 10e1d9dcc8SAndreas Gohrtrait PluginTrait 11e1d9dcc8SAndreas Gohr{ 12e1d9dcc8SAndreas Gohr protected $localised = false; // set to true by setupLocale() after loading language dependent strings 131490c177SAndreas Gohr protected $lang = []; // array to hold language dependent strings, best accessed via ->getLang() 14e1d9dcc8SAndreas Gohr protected $configloaded = false; // set to true by loadConfig() after loading plugin configuration variables 151490c177SAndreas Gohr protected $conf = []; // array to hold plugin settings, best accessed via ->getConf() 16e1d9dcc8SAndreas Gohr 17e1d9dcc8SAndreas Gohr /** 18e1d9dcc8SAndreas Gohr * @see PluginInterface::getInfo() 19e1d9dcc8SAndreas Gohr */ 20e1d9dcc8SAndreas Gohr public function getInfo() 21e1d9dcc8SAndreas Gohr { 223d041397SAndreas Gohr $class = get_class($this); 233d041397SAndreas Gohr $parts = sexplode('_', $class, 3); 243d041397SAndreas Gohr $ext = $parts[2]; 25e1d9dcc8SAndreas Gohr 263d041397SAndreas Gohr if (empty($ext)) { 273d041397SAndreas Gohr throw new \RuntimeException('Class does not follow the plugin naming convention'); 283d041397SAndreas Gohr } 293d041397SAndreas Gohr 30e9472a98SAnna Dabrowska // class like action_plugin_myplugin_ajax belongs to plugin 'myplugin' 31e9472a98SAnna Dabrowska $ext = strtok($ext, '_'); 32e9472a98SAnna Dabrowska 333d041397SAndreas Gohr $base = [ 343d041397SAndreas Gohr 'base' => $ext, 353d041397SAndreas Gohr 'author' => 'Unknown', 363d041397SAndreas Gohr 'email' => 'unknown@example.com', 37e1d9dcc8SAndreas Gohr 'date' => '0000-00-00', 383d041397SAndreas Gohr 'name' => $ext . ' plugin', 393d041397SAndreas Gohr 'desc' => 'Unknown purpose - bad plugin.info.txt', 403d041397SAndreas Gohr 'url' => 'https://www.dokuwiki.org/plugins/' . $ext, 411490c177SAndreas Gohr ]; 423d041397SAndreas Gohr 433d041397SAndreas Gohr $file = DOKU_PLUGIN . '/' . $ext . '/plugin.info.txt'; 443d041397SAndreas Gohr if (file_exists($file)) { 453d041397SAndreas Gohr $raw = confToHash($file); 463d041397SAndreas Gohr 473d041397SAndreas Gohr // check if all required fields are present 483d041397SAndreas Gohr $msg = 'Extension %s does not provide a valid %s in %s'; 493d041397SAndreas Gohr foreach (array_keys($base) as $line) { 50*2c978aedSAndreas Gohr if (empty($raw[$line])) Logger::error(sprintf($msg, $ext, $line, $file)); 513d041397SAndreas Gohr } 523d041397SAndreas Gohr 533d041397SAndreas Gohr return array_merge($base, $raw); 543d041397SAndreas Gohr } 553d041397SAndreas Gohr 563d041397SAndreas Gohr Logger::error(sprintf('Extension %s does not provide a plugin.info.txt in %s', $ext, $file)); 573d041397SAndreas Gohr return $base; 58e1d9dcc8SAndreas Gohr } 59e1d9dcc8SAndreas Gohr 60e1d9dcc8SAndreas Gohr /** 61e1d9dcc8SAndreas Gohr * @see PluginInterface::isSingleton() 62e1d9dcc8SAndreas Gohr */ 63e1d9dcc8SAndreas Gohr public function isSingleton() 64e1d9dcc8SAndreas Gohr { 65e1d9dcc8SAndreas Gohr return true; 66e1d9dcc8SAndreas Gohr } 67e1d9dcc8SAndreas Gohr 68e1d9dcc8SAndreas Gohr /** 69e1d9dcc8SAndreas Gohr * @see PluginInterface::loadHelper() 70e1d9dcc8SAndreas Gohr */ 71e1d9dcc8SAndreas Gohr public function loadHelper($name, $msg = true) 72e1d9dcc8SAndreas Gohr { 73e1d9dcc8SAndreas Gohr $obj = plugin_load('helper', $name); 74e1d9dcc8SAndreas Gohr if (is_null($obj) && $msg) msg("Helper plugin $name is not available or invalid.", -1); 75e1d9dcc8SAndreas Gohr return $obj; 76e1d9dcc8SAndreas Gohr } 77e1d9dcc8SAndreas Gohr 78e1d9dcc8SAndreas Gohr // region introspection methods 79e1d9dcc8SAndreas Gohr 80e1d9dcc8SAndreas Gohr /** 81e1d9dcc8SAndreas Gohr * @see PluginInterface::getPluginType() 82e1d9dcc8SAndreas Gohr */ 83e1d9dcc8SAndreas Gohr public function getPluginType() 84e1d9dcc8SAndreas Gohr { 851490c177SAndreas Gohr [$t] = explode('_', get_class($this), 2); 86e1d9dcc8SAndreas Gohr return $t; 87e1d9dcc8SAndreas Gohr } 88e1d9dcc8SAndreas Gohr 89e1d9dcc8SAndreas Gohr /** 90e1d9dcc8SAndreas Gohr * @see PluginInterface::getPluginName() 91e1d9dcc8SAndreas Gohr */ 92e1d9dcc8SAndreas Gohr public function getPluginName() 93e1d9dcc8SAndreas Gohr { 94a19c9aa0SGerrit Uitslag [/* t */, /* p */, $n] = sexplode('_', get_class($this), 4, ''); 95e1d9dcc8SAndreas Gohr return $n; 96e1d9dcc8SAndreas Gohr } 97e1d9dcc8SAndreas Gohr 98e1d9dcc8SAndreas Gohr /** 99e1d9dcc8SAndreas Gohr * @see PluginInterface::getPluginComponent() 100e1d9dcc8SAndreas Gohr */ 101e1d9dcc8SAndreas Gohr public function getPluginComponent() 102e1d9dcc8SAndreas Gohr { 103a19c9aa0SGerrit Uitslag [/* t */, /* p */, /* n */, $c] = sexplode('_', get_class($this), 4, ''); 104ec34bb30SAndreas Gohr return $c; 105e1d9dcc8SAndreas Gohr } 106e1d9dcc8SAndreas Gohr 107e1d9dcc8SAndreas Gohr // endregion 108e1d9dcc8SAndreas Gohr // region localization methods 109e1d9dcc8SAndreas Gohr 110e1d9dcc8SAndreas Gohr /** 111e1d9dcc8SAndreas Gohr * @see PluginInterface::getLang() 112e1d9dcc8SAndreas Gohr */ 113e1d9dcc8SAndreas Gohr public function getLang($id) 114e1d9dcc8SAndreas Gohr { 115e1d9dcc8SAndreas Gohr if (!$this->localised) $this->setupLocale(); 116e1d9dcc8SAndreas Gohr 1171490c177SAndreas Gohr return ($this->lang[$id] ?? ''); 118e1d9dcc8SAndreas Gohr } 119e1d9dcc8SAndreas Gohr 120e1d9dcc8SAndreas Gohr /** 121e1d9dcc8SAndreas Gohr * @see PluginInterface::locale_xhtml() 122e1d9dcc8SAndreas Gohr */ 123e1d9dcc8SAndreas Gohr public function locale_xhtml($id) 124e1d9dcc8SAndreas Gohr { 125e1d9dcc8SAndreas Gohr return p_cached_output($this->localFN($id)); 126e1d9dcc8SAndreas Gohr } 127e1d9dcc8SAndreas Gohr 128e1d9dcc8SAndreas Gohr /** 129e1d9dcc8SAndreas Gohr * @see PluginInterface::localFN() 130e1d9dcc8SAndreas Gohr */ 131e1d9dcc8SAndreas Gohr public function localFN($id, $ext = 'txt') 132e1d9dcc8SAndreas Gohr { 133e1d9dcc8SAndreas Gohr global $conf; 134e1d9dcc8SAndreas Gohr $plugin = $this->getPluginName(); 135e1d9dcc8SAndreas Gohr $file = DOKU_CONF . 'plugin_lang/' . $plugin . '/' . $conf['lang'] . '/' . $id . '.' . $ext; 136e1d9dcc8SAndreas Gohr if (!file_exists($file)) { 137e1d9dcc8SAndreas Gohr $file = DOKU_PLUGIN . $plugin . '/lang/' . $conf['lang'] . '/' . $id . '.' . $ext; 138e1d9dcc8SAndreas Gohr if (!file_exists($file)) { 139e1d9dcc8SAndreas Gohr //fall back to english 140e1d9dcc8SAndreas Gohr $file = DOKU_PLUGIN . $plugin . '/lang/en/' . $id . '.' . $ext; 141e1d9dcc8SAndreas Gohr } 142e1d9dcc8SAndreas Gohr } 143e1d9dcc8SAndreas Gohr return $file; 144e1d9dcc8SAndreas Gohr } 145e1d9dcc8SAndreas Gohr 146e1d9dcc8SAndreas Gohr /** 147e1d9dcc8SAndreas Gohr * @see PluginInterface::setupLocale() 148e1d9dcc8SAndreas Gohr */ 149e1d9dcc8SAndreas Gohr public function setupLocale() 150e1d9dcc8SAndreas Gohr { 151e1d9dcc8SAndreas Gohr if ($this->localised) return; 152e1d9dcc8SAndreas Gohr 153e1d9dcc8SAndreas Gohr global $conf, $config_cascade; // definitely don't invoke "global $lang" 154e1d9dcc8SAndreas Gohr $path = DOKU_PLUGIN . $this->getPluginName() . '/lang/'; 155e1d9dcc8SAndreas Gohr 1561490c177SAndreas Gohr $lang = []; 157e1d9dcc8SAndreas Gohr 158e1d9dcc8SAndreas Gohr // don't include once, in case several plugin components require the same language file 159e1d9dcc8SAndreas Gohr @include($path . 'en/lang.php'); 160e1d9dcc8SAndreas Gohr foreach ($config_cascade['lang']['plugin'] as $config_file) { 161e1d9dcc8SAndreas Gohr if (file_exists($config_file . $this->getPluginName() . '/en/lang.php')) { 162e1d9dcc8SAndreas Gohr include($config_file . $this->getPluginName() . '/en/lang.php'); 163e1d9dcc8SAndreas Gohr } 164e1d9dcc8SAndreas Gohr } 165e1d9dcc8SAndreas Gohr 166e1d9dcc8SAndreas Gohr if ($conf['lang'] != 'en') { 167e1d9dcc8SAndreas Gohr @include($path . $conf['lang'] . '/lang.php'); 168e1d9dcc8SAndreas Gohr foreach ($config_cascade['lang']['plugin'] as $config_file) { 169e1d9dcc8SAndreas Gohr if (file_exists($config_file . $this->getPluginName() . '/' . $conf['lang'] . '/lang.php')) { 170e1d9dcc8SAndreas Gohr include($config_file . $this->getPluginName() . '/' . $conf['lang'] . '/lang.php'); 171e1d9dcc8SAndreas Gohr } 172e1d9dcc8SAndreas Gohr } 173e1d9dcc8SAndreas Gohr } 174e1d9dcc8SAndreas Gohr 175e1d9dcc8SAndreas Gohr $this->lang = $lang; 176e1d9dcc8SAndreas Gohr $this->localised = true; 177e1d9dcc8SAndreas Gohr } 178e1d9dcc8SAndreas Gohr 179e1d9dcc8SAndreas Gohr // endregion 180e1d9dcc8SAndreas Gohr // region configuration methods 181e1d9dcc8SAndreas Gohr 182e1d9dcc8SAndreas Gohr /** 183e1d9dcc8SAndreas Gohr * @see PluginInterface::getConf() 184e1d9dcc8SAndreas Gohr */ 185e1d9dcc8SAndreas Gohr public function getConf($setting, $notset = false) 186e1d9dcc8SAndreas Gohr { 187e1d9dcc8SAndreas Gohr 188e1d9dcc8SAndreas Gohr if (!$this->configloaded) { 189e1d9dcc8SAndreas Gohr $this->loadConfig(); 190e1d9dcc8SAndreas Gohr } 191e1d9dcc8SAndreas Gohr 192e1d9dcc8SAndreas Gohr if (isset($this->conf[$setting])) { 193e1d9dcc8SAndreas Gohr return $this->conf[$setting]; 194e1d9dcc8SAndreas Gohr } else { 195e1d9dcc8SAndreas Gohr return $notset; 196e1d9dcc8SAndreas Gohr } 197e1d9dcc8SAndreas Gohr } 198e1d9dcc8SAndreas Gohr 199e1d9dcc8SAndreas Gohr /** 200e1d9dcc8SAndreas Gohr * @see PluginInterface::loadConfig() 201e1d9dcc8SAndreas Gohr */ 202e1d9dcc8SAndreas Gohr public function loadConfig() 203e1d9dcc8SAndreas Gohr { 204e1d9dcc8SAndreas Gohr global $conf; 205e1d9dcc8SAndreas Gohr 206e1d9dcc8SAndreas Gohr $defaults = $this->readDefaultSettings(); 207e1d9dcc8SAndreas Gohr $plugin = $this->getPluginName(); 208e1d9dcc8SAndreas Gohr 209e1d9dcc8SAndreas Gohr foreach ($defaults as $key => $value) { 210e1d9dcc8SAndreas Gohr if (isset($conf['plugin'][$plugin][$key])) continue; 211e1d9dcc8SAndreas Gohr $conf['plugin'][$plugin][$key] = $value; 212e1d9dcc8SAndreas Gohr } 213e1d9dcc8SAndreas Gohr 214e1d9dcc8SAndreas Gohr $this->configloaded = true; 215e1d9dcc8SAndreas Gohr $this->conf =& $conf['plugin'][$plugin]; 216e1d9dcc8SAndreas Gohr } 217e1d9dcc8SAndreas Gohr 218e1d9dcc8SAndreas Gohr /** 219e1d9dcc8SAndreas Gohr * read the plugin's default configuration settings from conf/default.php 220e1d9dcc8SAndreas Gohr * this function is automatically called through getConf() 221e1d9dcc8SAndreas Gohr * 222e1d9dcc8SAndreas Gohr * @return array setting => value 223e1d9dcc8SAndreas Gohr */ 224e1d9dcc8SAndreas Gohr protected function readDefaultSettings() 225e1d9dcc8SAndreas Gohr { 226e1d9dcc8SAndreas Gohr 227e1d9dcc8SAndreas Gohr $path = DOKU_PLUGIN . $this->getPluginName() . '/conf/'; 2281490c177SAndreas Gohr $conf = []; 229e1d9dcc8SAndreas Gohr 230e1d9dcc8SAndreas Gohr if (file_exists($path . 'default.php')) { 231e1d9dcc8SAndreas Gohr include($path . 'default.php'); 232e1d9dcc8SAndreas Gohr } 233e1d9dcc8SAndreas Gohr 234e1d9dcc8SAndreas Gohr return $conf; 235e1d9dcc8SAndreas Gohr } 236e1d9dcc8SAndreas Gohr 237e1d9dcc8SAndreas Gohr // endregion 238e1d9dcc8SAndreas Gohr // region output methods 239e1d9dcc8SAndreas Gohr 240e1d9dcc8SAndreas Gohr /** 241e1d9dcc8SAndreas Gohr * @see PluginInterface::email() 242e1d9dcc8SAndreas Gohr */ 243e1d9dcc8SAndreas Gohr public function email($email, $name = '', $class = '', $more = '') 244e1d9dcc8SAndreas Gohr { 245e1d9dcc8SAndreas Gohr if (!$email) return $name; 246e1d9dcc8SAndreas Gohr $email = obfuscate($email); 247e1d9dcc8SAndreas Gohr if (!$name) $name = $email; 2481490c177SAndreas Gohr $class = "class='" . ($class ?: 'mail') . "'"; 249e1d9dcc8SAndreas Gohr return "<a href='mailto:$email' $class title='$email' $more>$name</a>"; 250e1d9dcc8SAndreas Gohr } 251e1d9dcc8SAndreas Gohr 252e1d9dcc8SAndreas Gohr /** 253e1d9dcc8SAndreas Gohr * @see PluginInterface::external_link() 254e1d9dcc8SAndreas Gohr */ 255e1d9dcc8SAndreas Gohr public function external_link($link, $title = '', $class = '', $target = '', $more = '') 256e1d9dcc8SAndreas Gohr { 257e1d9dcc8SAndreas Gohr global $conf; 258e1d9dcc8SAndreas Gohr 259e1d9dcc8SAndreas Gohr $link = htmlentities($link); 260e1d9dcc8SAndreas Gohr if (!$title) $title = $link; 261e1d9dcc8SAndreas Gohr if (!$target) $target = $conf['target']['extern']; 262e1d9dcc8SAndreas Gohr if ($conf['relnofollow']) $more .= ' rel="nofollow"'; 263e1d9dcc8SAndreas Gohr 264e1d9dcc8SAndreas Gohr if ($class) $class = " class='$class'"; 265e1d9dcc8SAndreas Gohr if ($target) $target = " target='$target'"; 266e1d9dcc8SAndreas Gohr if ($more) $more = " " . trim($more); 267e1d9dcc8SAndreas Gohr 268e1d9dcc8SAndreas Gohr return "<a href='$link'$class$target$more>$title</a>"; 269e1d9dcc8SAndreas Gohr } 270e1d9dcc8SAndreas Gohr 271e1d9dcc8SAndreas Gohr /** 272e1d9dcc8SAndreas Gohr * @see PluginInterface::render_text() 273e1d9dcc8SAndreas Gohr */ 274e1d9dcc8SAndreas Gohr public function render_text($text, $format = 'xhtml') 275e1d9dcc8SAndreas Gohr { 276e1d9dcc8SAndreas Gohr return p_render($format, p_get_instructions($text), $info); 277e1d9dcc8SAndreas Gohr } 278e1d9dcc8SAndreas Gohr 279e1d9dcc8SAndreas Gohr // endregion 280e1d9dcc8SAndreas Gohr} 281