1<?php
2
3/**
4 * DokuWiki Plugin doxycode (Tagmanager Helper Component)
5 *
6 * @license     GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
7 * @author      Lukas Probsthain <lukas.probsthain@gmail.com>
8 */
9
10use dokuwiki\Extension\Plugin;
11
12class helper_plugin_doxycode_tagmanager extends Plugin
13{
14    private $tagfile_dir;   // convenience variable for accessing the tag files
15
16    public function __construct()
17    {
18        $this->tagfile_dir = DOKU_PLUGIN . $this->getPluginName() . '/tagfiles/';
19    }
20
21    /**
22     * List tag files in the tag file directory.
23     *
24     * Returns an array with file names (without extension) as keys and empty
25     * arrays as values. This ensures compatibility with the tag file configuration from loadTagFileConfig().
26     * The list of tag files can then be merged with the tag file configuration.
27     *
28     * @return array associative array where the keys are the file names (without extension) of all XML files
29     * in the directory, and the values are empty arrays.
30     */
31    public function listTagFiles()
32    {
33        // Find all XML files in the directory
34        $files = glob($this->tagfile_dir . '*.xml');
35
36        // Array to hold file names without extension
37        $fileNames = [];
38
39        foreach ($files as $file) {
40            // Get the file name without extension
41            $fileNames[] = pathinfo($file, PATHINFO_FILENAME);
42        }
43
44        return array_fill_keys($fileNames, []);
45    }
46
47    public function getTagFileDir()
48    {
49        return $this->tagfile_dir;
50    }
51
52
53    /**
54     * The function `loadTagFileConfig()` reads and decodes the contents of a JSON file containing tagfile
55     * configuration, returning the decoded configuration array.
56     *
57     * @return array configuration array loaded from the tagconfig.json file. If the file does not exist or
58     * if there is an error reading or decoding the JSON content, an empty array is returned.
59     */
60    public function loadTagFileConfig()
61    {
62        // /path/to/dokuwiki/lib/plugins/doxycode/tagfiles/
63
64        $filename = $this->tagfile_dir . 'tagconfig.json';
65
66        // Check if the file exists
67        if (!file_exists($filename)) {
68            // admin needs to update the tagfile configuration
69            return [];
70        }
71
72        // Read the contents of the file
73        $jsonContent = file_get_contents($filename);
74        if ($jsonContent === false) {
75            return [];
76        }
77
78        // Decode the JSON content
79        $config = json_decode($jsonContent, true);
80        if ($config === null) {
81            return [];
82        }
83
84        return $config;
85    }
86
87    /**
88     * The function checks if a directory exists and creates it if it doesn't.
89     *
90     * @return bool either the result of the `mkdir()` function if the directory does not exist and is
91     * successfully created, or `true` if the directory already exists.
92     */
93    public function createTagFileDir()
94    {
95        if (!is_dir($this->tagfile_dir)) {
96            return mkdir($this->tagfile_dir);
97        } else {
98            return true;
99        }
100    }
101
102    /**
103     * Save the tag file configuration as json in the tag file directory.
104     *
105     * This function filters the relevant keys from the tag file configuration
106     * and saves all entries as a 'tagconfig.json' in the tag file directory.
107     *
108     * @param Array &$tag_config Array with tag file configuration entries.
109     * @param Bool $restore_mtime Restore the file modification time so
110     * that the cache files are not invalidated. Defaults to false.
111     */
112    public function saveTagFileConfig(&$tag_config, $restore_mtime = false)
113    {
114        /** @var String[] $save_key_selection Configuration keys that are allowed in the stored configuration file. */
115        $save_key_selection = [
116            'remote_url',
117            'update_period',
118            'docu_url',
119            'enabled',
120            'last_update',
121            'force_runner',
122            'description'];
123
124        /**
125         * @var String[] Copied tag file configuration entries.
126         *
127         * We copy over the allowed configuration $key => $value pairs so the original configuration is not modified.
128         */
129        $selected_config = [];
130
131        // create the tag file directory if not existent (might happen after installing the plugin)
132        $this->createTagFileDir();
133
134        $config_filename = $this->tagfile_dir . 'tagconfig.json';
135
136        // loop over all configuration entries
137        foreach ($tag_config as $name => $tag_conf) {
138            // loop over all keys in configuration
139            foreach ($tag_conf as $key => $value) {
140                if (in_array($key, $save_key_selection)) {
141                    $selected_config[$name][$key] = $value;
142                }
143            }
144        }
145
146        // Convert the selected configuration to JSON
147        $jsonData = json_encode($selected_config, JSON_PRETTY_PRINT);
148
149        // save the mtime so we can restore it later
150        $original_mtime = filemtime($config_filename);
151
152        file_put_contents($config_filename, $jsonData);
153
154        // restore the mtime if we have an original mtime and restoring is enabled
155        if ($original_mtime !== false && $restore_mtime) {
156            touch($config_filename, $original_mtime);
157        }
158    }
159
160    /**
161     * Convert the internal tag file name to a full file path with extension.
162     *
163     * @param String $tag_name Internal tag file name
164     * @return String Full file path with extension for this tag file
165     */
166    public function getFileName($tag_name)
167    {
168        return $this->tagfile_dir . $tag_name . '.xml';
169    }
170
171    /**
172     * Load the configuration of tag files and optionally filter them by names.
173     *
174     * @param String|Array $tag_names Internal tag file names (without extension) for filtering the configuration
175     * @return Array Filtered tag file configuration
176     */
177    public function getFilteredTagConfig($tag_names = null)
178    {
179        $tag_conf = $this->loadTagFileConfig();
180
181        // filter out tag files
182        $tag_conf = $this->filterConfig($tag_conf, 'isConfigEnabled');
183
184
185        if ($tag_names) {
186            // convert to array if only one tag_name was given
187            $tag_names = is_array($tag_names) ? $tag_names : [$tag_names];
188
189            // filter tag_config by tag_names
190            $tag_conf = array_intersect_key($tag_conf, array_flip($tag_names));
191        }
192
193        return $tag_conf;
194    }
195
196    /**
197     * Filter a tag file configuration array for entries that are enabled.
198     *
199     * @param Array &$tag_conf Array with tag file configuration entries.
200     * @return Array Array with enabled tag file configuration entries.
201     */
202    public function filterConfig($tag_config, $filter, $inverse = false)
203    {
204        $filter = is_array($filter) ? $filter : [$filter];
205
206        foreach ($filter as $function) {
207            if ($inverse) {
208                // Apply the inverse filter
209                $tag_config = array_filter($tag_config, function ($item) use ($function) {
210                    return !$this->$function($item);
211                });
212            } else {
213                // Apply the standard filter
214                $tag_config = array_filter($tag_config, array($this, $function));
215            }
216        }
217        return $tag_config;
218    }
219
220    /**
221     * Check if a tag file configuration is enabled.
222     *
223     * Tag file configurations can be disabled through the admin interface.
224     * The parameters of the tag file (remote config, ...) will still be saved.
225     * But the tag file can't be used.
226     *
227     * This function is used in @see filterEnabledConfig to filter a tag file configuration array for
228     * entries that are enabled.
229     *
230     * @param Array &$tag_config Tag file configuration entry
231     * @return bool Is this tag file configuration enabled?
232     */
233    public function isConfigEnabled(&$tag_config)
234    {
235        return boolval($tag_config['enabled']);
236    }
237
238    /**
239     * Check if a tag file configuration represents a remote tag File
240     *
241     * @param Array &$tag_config Tag file configuration entry
242     * @return bool Is this a remote tag file configuration?
243     */
244    public function isValidRemoteConfig(&$tag_config)
245    {
246
247        // TODO: should we check if the URL contains a valid XML extension?
248        // TODO: should we also check if a valid period was set?
249        // otherwise we could simply fall back to the default update period in the task runner action
250        if (strlen($tag_config['remote_url']) > 0) {
251            return true;
252        } else {
253            return false;
254        }
255    }
256
257    /**
258     * Check if a tag file configuration has the force runner flag enabled.
259     *
260     * Some tag files are huge and cause long building times.
261     * We want to build the doxygen code snippet through the dokuwiki task runner in those cases.
262     * Otherwise the loading time of the page might exceed the maximum php execution time.
263     * This flag can be set through the admin interface.
264     *
265     * @param Array &$tag_config Tag file configuration entry
266     * @return bool Is this the force runner flag enabled?
267     */
268    public function isForceRenderTaskSet(&$tag_config)
269    {
270        $force_render = false;
271        foreach ($tag_config as $key => $tag_conf) {
272            if ($tag_conf['force_runner']) {
273                $force_render = true;
274                break;
275            }
276        }
277
278        return $force_render;
279    }
280}
281