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