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