1 <?php
2 
3 namespace dokuwiki\Extension;
4 
5 use dokuwiki\Logger;
6 
7 /**
8  * Provides standard DokuWiki plugin behaviour
9  */
10 trait 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 = get_class($this);
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('_', get_class($this), 2);
86         return $t;
87     }
88 
89     /**
90      * @see PluginInterface::getPluginName()
91      */
92     public function getPluginName()
93     {
94         [/* t */, /* p */, $n] = sexplode('_', get_class($this), 4, '');
95         return $n;
96     }
97 
98     /**
99      * @see PluginInterface::getPluginComponent()
100      */
101     public function getPluginComponent()
102     {
103         [/* t */, /* p */, /* n */, $c] = sexplode('_', get_class($this), 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         return p_cached_output($this->localFN($id));
126     }
127 
128     /**
129      * @see PluginInterface::localFN()
130      */
131     public function localFN($id, $ext = 'txt')
132     {
133         global $conf;
134         $plugin = $this->getPluginName();
135         $file = DOKU_CONF . 'plugin_lang/' . $plugin . '/' . $conf['lang'] . '/' . $id . '.' . $ext;
136         if (!file_exists($file)) {
137             $file = DOKU_PLUGIN . $plugin . '/lang/' . $conf['lang'] . '/' . $id . '.' . $ext;
138             if (!file_exists($file)) {
139                 //fall back to english
140                 $file = DOKU_PLUGIN . $plugin . '/lang/en/' . $id . '.' . $ext;
141             }
142         }
143         return $file;
144     }
145 
146     /**
147      * @see PluginInterface::setupLocale()
148      */
149     public function setupLocale()
150     {
151         if ($this->localised) return;
152 
153         global $conf, $config_cascade; // definitely don't invoke "global $lang"
154         $path = DOKU_PLUGIN . $this->getPluginName() . '/lang/';
155 
156         $lang = [];
157 
158         // don't include once, in case several plugin components require the same language file
159         @include($path . 'en/lang.php');
160         foreach ($config_cascade['lang']['plugin'] as $config_file) {
161             if (file_exists($config_file . $this->getPluginName() . '/en/lang.php')) {
162                 include($config_file . $this->getPluginName() . '/en/lang.php');
163             }
164         }
165 
166         if ($conf['lang'] != 'en') {
167             @include($path . $conf['lang'] . '/lang.php');
168             foreach ($config_cascade['lang']['plugin'] as $config_file) {
169                 if (file_exists($config_file . $this->getPluginName() . '/' . $conf['lang'] . '/lang.php')) {
170                     include($config_file . $this->getPluginName() . '/' . $conf['lang'] . '/lang.php');
171                 }
172             }
173         }
174 
175         $this->lang = $lang;
176         $this->localised = true;
177     }
178 
179     // endregion
180     // region configuration methods
181 
182     /**
183      * @see PluginInterface::getConf()
184      */
185     public function getConf($setting, $notset = false)
186     {
187 
188         if (!$this->configloaded) {
189             $this->loadConfig();
190         }
191 
192         if (isset($this->conf[$setting])) {
193             return $this->conf[$setting];
194         } else {
195             return $notset;
196         }
197     }
198 
199     /**
200      * @see PluginInterface::loadConfig()
201      */
202     public function loadConfig()
203     {
204         global $conf;
205 
206         $defaults = $this->readDefaultSettings();
207         $plugin = $this->getPluginName();
208 
209         foreach ($defaults as $key => $value) {
210             if (isset($conf['plugin'][$plugin][$key])) continue;
211             $conf['plugin'][$plugin][$key] = $value;
212         }
213 
214         $this->configloaded = true;
215         $this->conf =& $conf['plugin'][$plugin];
216     }
217 
218     /**
219      * read the plugin's default configuration settings from conf/default.php
220      * this function is automatically called through getConf()
221      *
222      * @return    array    setting => value
223      */
224     protected function readDefaultSettings()
225     {
226 
227         $path = DOKU_PLUGIN . $this->getPluginName() . '/conf/';
228         $conf = [];
229 
230         if (file_exists($path . 'default.php')) {
231             include($path . 'default.php');
232         }
233 
234         return $conf;
235     }
236 
237     // endregion
238     // region output methods
239 
240     /**
241      * @see PluginInterface::email()
242      */
243     public function email($email, $name = '', $class = '', $more = '')
244     {
245         if (!$email) return $name;
246         $email = obfuscate($email);
247         if (!$name) $name = $email;
248         $class = "class='" . ($class ?: 'mail') . "'";
249         return "<a href='mailto:$email' $class title='$email' $more>$name</a>";
250     }
251 
252     /**
253      * @see PluginInterface::external_link()
254      */
255     public function external_link($link, $title = '', $class = '', $target = '', $more = '')
256     {
257         global $conf;
258 
259         $link = htmlentities($link);
260         if (!$title) $title = $link;
261         if (!$target) $target = $conf['target']['extern'];
262         if ($conf['relnofollow']) $more .= ' rel="nofollow"';
263 
264         if ($class) $class = " class='$class'";
265         if ($target) $target = " target='$target'";
266         if ($more) $more = " " . trim($more);
267 
268         return "<a href='$link'$class$target$more>$title</a>";
269     }
270 
271     /**
272      * @see PluginInterface::render_text()
273      */
274     public function render_text($text, $format = 'xhtml')
275     {
276         return p_render($format, p_get_instructions($text), $info);
277     }
278 
279     // endregion
280 }
281