xref: /dokuwiki/lib/plugins/extension/cli.php (revision dccd6b2bba7367e4d1d2d7aa84c9f9d15584b593)
1a8d2f3cbSAndreas Gohr<?php
2a8d2f3cbSAndreas Gohr
38553d24dSAndreas Gohruse dokuwiki\Extension\CLIPlugin;
4fe2dcfd5SAndreas Gohruse splitbrain\phpcli\Options;
5fe2dcfd5SAndreas Gohruse splitbrain\phpcli\TableFormatter;
6a8d2f3cbSAndreas Gohruse splitbrain\phpcli\Colors;
7a8d2f3cbSAndreas Gohr
8a8d2f3cbSAndreas Gohr/**
9a8d2f3cbSAndreas Gohr * Class cli_plugin_extension
10a8d2f3cbSAndreas Gohr *
11a8d2f3cbSAndreas Gohr * Command Line component for the extension manager
12a8d2f3cbSAndreas Gohr *
13a8d2f3cbSAndreas Gohr * @license GPL2
14a8d2f3cbSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
15a8d2f3cbSAndreas Gohr */
168553d24dSAndreas Gohrclass cli_plugin_extension extends CLIPlugin
17a8d2f3cbSAndreas Gohr{
18a8d2f3cbSAndreas Gohr    /** @inheritdoc */
19fe2dcfd5SAndreas Gohr    protected function setup(Options $options)
20a8d2f3cbSAndreas Gohr    {
21a8d2f3cbSAndreas Gohr        // general setup
22b9daa2f5SAndreas Gohr        $options->setHelp(
23b9daa2f5SAndreas Gohr            "Manage plugins and templates for this DokuWiki instance\n\n" .
24b9daa2f5SAndreas Gohr            "Status codes:\n" .
25b9daa2f5SAndreas Gohr            "   i - installed\n" .
26b9daa2f5SAndreas Gohr            "   b - bundled with DokuWiki\n" .
27b9daa2f5SAndreas Gohr            "   g - installed via git\n" .
28b9daa2f5SAndreas Gohr            "   d - disabled\n" .
29b9daa2f5SAndreas Gohr            "   u - update available\n"
30b9daa2f5SAndreas Gohr        );
31a8d2f3cbSAndreas Gohr
32a8d2f3cbSAndreas Gohr        // search
33a8d2f3cbSAndreas Gohr        $options->registerCommand('search', 'Search for an extension');
34a8d2f3cbSAndreas Gohr        $options->registerOption('max', 'Maximum number of results (default 10)', 'm', 'number', 'search');
35a8d2f3cbSAndreas Gohr        $options->registerOption('verbose', 'Show detailed extension information', 'v', false, 'search');
36a8d2f3cbSAndreas Gohr        $options->registerArgument('query', 'The keyword(s) to search for', true, 'search');
37a8d2f3cbSAndreas Gohr
38a8d2f3cbSAndreas Gohr        // list
39a8d2f3cbSAndreas Gohr        $options->registerCommand('list', 'List installed extensions');
40a8d2f3cbSAndreas Gohr        $options->registerOption('verbose', 'Show detailed extension information', 'v', false, 'list');
41b9daa2f5SAndreas Gohr        $options->registerOption('filter', 'Filter by this status', 'f', 'status', 'list');
42a8d2f3cbSAndreas Gohr
43a8d2f3cbSAndreas Gohr        // upgrade
44a8d2f3cbSAndreas Gohr        $options->registerCommand('upgrade', 'Update all installed extensions to their latest versions');
45a8d2f3cbSAndreas Gohr
46a8d2f3cbSAndreas Gohr        // install
47a8d2f3cbSAndreas Gohr        $options->registerCommand('install', 'Install or upgrade extensions');
48*dccd6b2bSAndreas Gohr        $options->registerArgument(
49*dccd6b2bSAndreas Gohr            'extensions...',
50*dccd6b2bSAndreas Gohr            'One or more extensions to install. Either by name or download URL',
51*dccd6b2bSAndreas Gohr            true,
52*dccd6b2bSAndreas Gohr            'install'
53e2170488SAndreas Gohr        );
54a8d2f3cbSAndreas Gohr
55a8d2f3cbSAndreas Gohr        // uninstall
56a8d2f3cbSAndreas Gohr        $options->registerCommand('uninstall', 'Uninstall a new extension');
57a8d2f3cbSAndreas Gohr        $options->registerArgument('extensions...', 'One or more extensions to install', true, 'uninstall');
58a8d2f3cbSAndreas Gohr
59a8d2f3cbSAndreas Gohr        // enable
60a8d2f3cbSAndreas Gohr        $options->registerCommand('enable', 'Enable installed extensions');
61a8d2f3cbSAndreas Gohr        $options->registerArgument('extensions...', 'One or more extensions to enable', true, 'enable');
62a8d2f3cbSAndreas Gohr
63a8d2f3cbSAndreas Gohr        // disable
64a8d2f3cbSAndreas Gohr        $options->registerCommand('disable', 'Disable installed extensions');
65a8d2f3cbSAndreas Gohr        $options->registerArgument('extensions...', 'One or more extensions to disable', true, 'disable');
66a8d2f3cbSAndreas Gohr
67a8d2f3cbSAndreas Gohr
68a8d2f3cbSAndreas Gohr    }
69a8d2f3cbSAndreas Gohr
70a8d2f3cbSAndreas Gohr    /** @inheritdoc */
71fe2dcfd5SAndreas Gohr    protected function main(Options $options)
72a8d2f3cbSAndreas Gohr    {
73ed3520eeSAndreas Gohr        /** @var helper_plugin_extension_repository $repo */
74ed3520eeSAndreas Gohr        $repo = plugin_load('helper', 'extension_repository');
75ed3520eeSAndreas Gohr        if (!$repo->hasAccess(false)) {
76ed3520eeSAndreas Gohr            $this->warning('Extension Repository API is not accessible, no remote info available!');
77ed3520eeSAndreas Gohr        }
78ed3520eeSAndreas Gohr
79a8d2f3cbSAndreas Gohr        switch ($options->getCmd()) {
80a8d2f3cbSAndreas Gohr            case 'list':
81b9daa2f5SAndreas Gohr                $ret = $this->cmdList($options->getOpt('verbose'), $options->getOpt('filter', ''));
82a8d2f3cbSAndreas Gohr                break;
83a8d2f3cbSAndreas Gohr            case 'search':
84a8d2f3cbSAndreas Gohr                $ret = $this->cmdSearch(
85a8d2f3cbSAndreas Gohr                    implode(' ', $options->getArgs()),
86a8d2f3cbSAndreas Gohr                    $options->getOpt('verbose'),
87a8d2f3cbSAndreas Gohr                    (int)$options->getOpt('max', 10)
88a8d2f3cbSAndreas Gohr                );
89a8d2f3cbSAndreas Gohr                break;
90a8d2f3cbSAndreas Gohr            case 'install':
91a8d2f3cbSAndreas Gohr                $ret = $this->cmdInstall($options->getArgs());
92a8d2f3cbSAndreas Gohr                break;
93a8d2f3cbSAndreas Gohr            case 'uninstall':
94a8d2f3cbSAndreas Gohr                $ret = $this->cmdUnInstall($options->getArgs());
95a8d2f3cbSAndreas Gohr                break;
96a8d2f3cbSAndreas Gohr            case 'enable':
97a8d2f3cbSAndreas Gohr                $ret = $this->cmdEnable(true, $options->getArgs());
98a8d2f3cbSAndreas Gohr                break;
99a8d2f3cbSAndreas Gohr            case 'disable':
100a8d2f3cbSAndreas Gohr                $ret = $this->cmdEnable(false, $options->getArgs());
101a8d2f3cbSAndreas Gohr                break;
102a8d2f3cbSAndreas Gohr            case 'upgrade':
103a8d2f3cbSAndreas Gohr                $ret = $this->cmdUpgrade();
104a8d2f3cbSAndreas Gohr                break;
105a8d2f3cbSAndreas Gohr            default:
106a8d2f3cbSAndreas Gohr                echo $options->help();
107a8d2f3cbSAndreas Gohr                $ret = 0;
108a8d2f3cbSAndreas Gohr        }
109a8d2f3cbSAndreas Gohr
110a8d2f3cbSAndreas Gohr        exit($ret);
111a8d2f3cbSAndreas Gohr    }
112a8d2f3cbSAndreas Gohr
113a8d2f3cbSAndreas Gohr    /**
114a8d2f3cbSAndreas Gohr     * Upgrade all extensions
115a8d2f3cbSAndreas Gohr     *
116a8d2f3cbSAndreas Gohr     * @return int
117a8d2f3cbSAndreas Gohr     */
118a8d2f3cbSAndreas Gohr    protected function cmdUpgrade()
119a8d2f3cbSAndreas Gohr    {
120a8d2f3cbSAndreas Gohr        /* @var helper_plugin_extension_extension $ext */
121a8d2f3cbSAndreas Gohr        $ext = $this->loadHelper('extension_extension');
122a8d2f3cbSAndreas Gohr        $list = $this->getInstalledExtensions();
123a8d2f3cbSAndreas Gohr
124a8d2f3cbSAndreas Gohr        $ok = 0;
125a8d2f3cbSAndreas Gohr        foreach ($list as $extname) {
126a8d2f3cbSAndreas Gohr            $ext->setExtension($extname);
127a8d2f3cbSAndreas Gohr            $date = $ext->getInstalledVersion();
128a8d2f3cbSAndreas Gohr            $avail = $ext->getLastUpdate();
129be15e516SAndreas Gohr            if ($avail && $avail > $date && !$ext->isBundled()) {
130a8d2f3cbSAndreas Gohr                $ok += $this->cmdInstall([$extname]);
131a8d2f3cbSAndreas Gohr            }
132a8d2f3cbSAndreas Gohr        }
133a8d2f3cbSAndreas Gohr
134a8d2f3cbSAndreas Gohr        return $ok;
135a8d2f3cbSAndreas Gohr    }
136a8d2f3cbSAndreas Gohr
137a8d2f3cbSAndreas Gohr    /**
138a8d2f3cbSAndreas Gohr     * Enable or disable one or more extensions
139a8d2f3cbSAndreas Gohr     *
140a8d2f3cbSAndreas Gohr     * @param bool $set
141a8d2f3cbSAndreas Gohr     * @param string[] $extensions
142a8d2f3cbSAndreas Gohr     * @return int
143a8d2f3cbSAndreas Gohr     */
144a8d2f3cbSAndreas Gohr    protected function cmdEnable($set, $extensions)
145a8d2f3cbSAndreas Gohr    {
146a8d2f3cbSAndreas Gohr        /* @var helper_plugin_extension_extension $ext */
147a8d2f3cbSAndreas Gohr        $ext = $this->loadHelper('extension_extension');
148a8d2f3cbSAndreas Gohr
149a8d2f3cbSAndreas Gohr        $ok = 0;
150a8d2f3cbSAndreas Gohr        foreach ($extensions as $extname) {
151a8d2f3cbSAndreas Gohr            $ext->setExtension($extname);
152a8d2f3cbSAndreas Gohr            if (!$ext->isInstalled()) {
153a8d2f3cbSAndreas Gohr                $this->error(sprintf('Extension %s is not installed', $ext->getID()));
154fe2dcfd5SAndreas Gohr                ++$ok;
155a8d2f3cbSAndreas Gohr                continue;
156a8d2f3cbSAndreas Gohr            }
157a8d2f3cbSAndreas Gohr
158a8d2f3cbSAndreas Gohr            if ($set) {
159a8d2f3cbSAndreas Gohr                $status = $ext->enable();
160a8d2f3cbSAndreas Gohr                $msg = 'msg_enabled';
161a8d2f3cbSAndreas Gohr            } else {
162a8d2f3cbSAndreas Gohr                $status = $ext->disable();
163a8d2f3cbSAndreas Gohr                $msg = 'msg_disabled';
164a8d2f3cbSAndreas Gohr            }
165a8d2f3cbSAndreas Gohr
166a8d2f3cbSAndreas Gohr            if ($status !== true) {
167a8d2f3cbSAndreas Gohr                $this->error($status);
168fe2dcfd5SAndreas Gohr                ++$ok;
169a8d2f3cbSAndreas Gohr                continue;
170a8d2f3cbSAndreas Gohr            } else {
171a8d2f3cbSAndreas Gohr                $this->success(sprintf($this->getLang($msg), $ext->getID()));
172a8d2f3cbSAndreas Gohr            }
173a8d2f3cbSAndreas Gohr        }
174a8d2f3cbSAndreas Gohr
175a8d2f3cbSAndreas Gohr        return $ok;
176a8d2f3cbSAndreas Gohr    }
177a8d2f3cbSAndreas Gohr
178a8d2f3cbSAndreas Gohr    /**
179a8d2f3cbSAndreas Gohr     * Uninstall one or more extensions
180a8d2f3cbSAndreas Gohr     *
181a8d2f3cbSAndreas Gohr     * @param string[] $extensions
182a8d2f3cbSAndreas Gohr     * @return int
183a8d2f3cbSAndreas Gohr     */
184a8d2f3cbSAndreas Gohr    protected function cmdUnInstall($extensions)
185a8d2f3cbSAndreas Gohr    {
186a8d2f3cbSAndreas Gohr        /* @var helper_plugin_extension_extension $ext */
187a8d2f3cbSAndreas Gohr        $ext = $this->loadHelper('extension_extension');
188a8d2f3cbSAndreas Gohr
189a8d2f3cbSAndreas Gohr        $ok = 0;
190a8d2f3cbSAndreas Gohr        foreach ($extensions as $extname) {
191a8d2f3cbSAndreas Gohr            $ext->setExtension($extname);
192a8d2f3cbSAndreas Gohr            if (!$ext->isInstalled()) {
193a8d2f3cbSAndreas Gohr                $this->error(sprintf('Extension %s is not installed', $ext->getID()));
194fe2dcfd5SAndreas Gohr                ++$ok;
195a8d2f3cbSAndreas Gohr                continue;
196a8d2f3cbSAndreas Gohr            }
197a8d2f3cbSAndreas Gohr
198a8d2f3cbSAndreas Gohr            $status = $ext->uninstall();
199a8d2f3cbSAndreas Gohr            if ($status) {
200a8d2f3cbSAndreas Gohr                $this->success(sprintf($this->getLang('msg_delete_success'), $ext->getID()));
201a8d2f3cbSAndreas Gohr            } else {
202a8d2f3cbSAndreas Gohr                $this->error(sprintf($this->getLang('msg_delete_failed'), hsc($ext->getID())));
203a8d2f3cbSAndreas Gohr                $ok = 1;
204a8d2f3cbSAndreas Gohr            }
205a8d2f3cbSAndreas Gohr        }
206a8d2f3cbSAndreas Gohr
207a8d2f3cbSAndreas Gohr        return $ok;
208a8d2f3cbSAndreas Gohr    }
209a8d2f3cbSAndreas Gohr
210a8d2f3cbSAndreas Gohr    /**
211a8d2f3cbSAndreas Gohr     * Install one or more extensions
212a8d2f3cbSAndreas Gohr     *
213a8d2f3cbSAndreas Gohr     * @param string[] $extensions
214a8d2f3cbSAndreas Gohr     * @return int
215a8d2f3cbSAndreas Gohr     */
216a8d2f3cbSAndreas Gohr    protected function cmdInstall($extensions)
217a8d2f3cbSAndreas Gohr    {
218a8d2f3cbSAndreas Gohr        /* @var helper_plugin_extension_extension $ext */
219a8d2f3cbSAndreas Gohr        $ext = $this->loadHelper('extension_extension');
220a8d2f3cbSAndreas Gohr
221a8d2f3cbSAndreas Gohr        $ok = 0;
222a8d2f3cbSAndreas Gohr        foreach ($extensions as $extname) {
2235aaea2b0SLocness            $installed = [];
2245aaea2b0SLocness
225cc16762dSLocness            if (preg_match("/^https?:\/\//i", $extname)) {
226cc16762dSLocness                try {
227cc16762dSLocness                    $installed = $ext->installFromURL($extname, true);
228cc16762dSLocness                } catch (Exception $e) {
229cc16762dSLocness                    $this->error($e->getMessage());
230fe2dcfd5SAndreas Gohr                    ++$ok;
231cc16762dSLocness                }
232cc16762dSLocness            } else {
233a8d2f3cbSAndreas Gohr                $ext->setExtension($extname);
234a8d2f3cbSAndreas Gohr
235a8d2f3cbSAndreas Gohr                if (!$ext->getDownloadURL()) {
236fe2dcfd5SAndreas Gohr                    ++$ok;
237a8d2f3cbSAndreas Gohr                    $this->error(
238a8d2f3cbSAndreas Gohr                        sprintf('Could not find download for %s', $ext->getID())
239a8d2f3cbSAndreas Gohr                    );
240a8d2f3cbSAndreas Gohr                    continue;
241a8d2f3cbSAndreas Gohr                }
242a8d2f3cbSAndreas Gohr
243a8d2f3cbSAndreas Gohr                try {
244a8d2f3cbSAndreas Gohr                    $installed = $ext->installOrUpdate();
2455aaea2b0SLocness                } catch (Exception $e) {
2465aaea2b0SLocness                    $this->error($e->getMessage());
247fe2dcfd5SAndreas Gohr                    ++$ok;
2485aaea2b0SLocness                }
2495aaea2b0SLocness            }
2505aaea2b0SLocness
251fe2dcfd5SAndreas Gohr            foreach ($installed as $info) {
252cc16762dSLocness                $this->success(
253cc16762dSLocness                    sprintf(
254a8d2f3cbSAndreas Gohr                        $this->getLang('msg_' . $info['type'] . '_' . $info['action'] . '_success'),
255cc16762dSLocness                        $info['base']
256cc16762dSLocness                    )
257a8d2f3cbSAndreas Gohr                );
258a8d2f3cbSAndreas Gohr            }
259cc16762dSLocness        }
260a8d2f3cbSAndreas Gohr        return $ok;
261a8d2f3cbSAndreas Gohr    }
262a8d2f3cbSAndreas Gohr
263a8d2f3cbSAndreas Gohr    /**
264a8d2f3cbSAndreas Gohr     * Search for an extension
265a8d2f3cbSAndreas Gohr     *
266a8d2f3cbSAndreas Gohr     * @param string $query
267a8d2f3cbSAndreas Gohr     * @param bool $showdetails
268a8d2f3cbSAndreas Gohr     * @param int $max
269a8d2f3cbSAndreas Gohr     * @return int
270a8d2f3cbSAndreas Gohr     * @throws \splitbrain\phpcli\Exception
271a8d2f3cbSAndreas Gohr     */
272a8d2f3cbSAndreas Gohr    protected function cmdSearch($query, $showdetails, $max)
273a8d2f3cbSAndreas Gohr    {
274a8d2f3cbSAndreas Gohr        /** @var helper_plugin_extension_repository $repository */
275a8d2f3cbSAndreas Gohr        $repository = $this->loadHelper('extension_repository');
276a8d2f3cbSAndreas Gohr        $result = $repository->search($query);
277a8d2f3cbSAndreas Gohr        if ($max) {
278a8d2f3cbSAndreas Gohr            $result = array_slice($result, 0, $max);
279a8d2f3cbSAndreas Gohr        }
280a8d2f3cbSAndreas Gohr
281a8d2f3cbSAndreas Gohr        $this->listExtensions($result, $showdetails);
282a8d2f3cbSAndreas Gohr        return 0;
283a8d2f3cbSAndreas Gohr    }
284a8d2f3cbSAndreas Gohr
285a8d2f3cbSAndreas Gohr    /**
286a8d2f3cbSAndreas Gohr     * @param bool $showdetails
287b9daa2f5SAndreas Gohr     * @param string $filter
288a8d2f3cbSAndreas Gohr     * @return int
289a8d2f3cbSAndreas Gohr     * @throws \splitbrain\phpcli\Exception
290a8d2f3cbSAndreas Gohr     */
291b9daa2f5SAndreas Gohr    protected function cmdList($showdetails, $filter)
292a8d2f3cbSAndreas Gohr    {
293a8d2f3cbSAndreas Gohr        $list = $this->getInstalledExtensions();
294b9daa2f5SAndreas Gohr        $this->listExtensions($list, $showdetails, $filter);
295a8d2f3cbSAndreas Gohr
296a8d2f3cbSAndreas Gohr        return 0;
297a8d2f3cbSAndreas Gohr    }
298a8d2f3cbSAndreas Gohr
299a8d2f3cbSAndreas Gohr    /**
300a8d2f3cbSAndreas Gohr     * Get all installed extensions
301a8d2f3cbSAndreas Gohr     *
302a8d2f3cbSAndreas Gohr     * @return array
303a8d2f3cbSAndreas Gohr     */
304a8d2f3cbSAndreas Gohr    protected function getInstalledExtensions()
305a8d2f3cbSAndreas Gohr    {
306a8d2f3cbSAndreas Gohr        /** @var Doku_Plugin_Controller $plugin_controller */
307a8d2f3cbSAndreas Gohr        global $plugin_controller;
308a8d2f3cbSAndreas Gohr        $pluginlist = $plugin_controller->getList('', true);
309a8d2f3cbSAndreas Gohr        $tpllist = glob(DOKU_INC . 'lib/tpl/*', GLOB_ONLYDIR);
310fe2dcfd5SAndreas Gohr        $tpllist = array_map(static fn($path) => 'template:' . basename($path), $tpllist);
31154cc7aa4SAndreas Gohr
312a8d2f3cbSAndreas Gohr        $list = array_merge($pluginlist, $tpllist);
313a8d2f3cbSAndreas Gohr        sort($list);
314a8d2f3cbSAndreas Gohr        return $list;
315a8d2f3cbSAndreas Gohr    }
316a8d2f3cbSAndreas Gohr
317a8d2f3cbSAndreas Gohr    /**
318a8d2f3cbSAndreas Gohr     * List the given extensions
319a8d2f3cbSAndreas Gohr     *
320a8d2f3cbSAndreas Gohr     * @param string[] $list
321a8d2f3cbSAndreas Gohr     * @param bool $details display details
322b9daa2f5SAndreas Gohr     * @param string $filter filter for this status
323a8d2f3cbSAndreas Gohr     * @throws \splitbrain\phpcli\Exception
324a8d2f3cbSAndreas Gohr     */
325b9daa2f5SAndreas Gohr    protected function listExtensions($list, $details, $filter = '')
326a8d2f3cbSAndreas Gohr    {
327a8d2f3cbSAndreas Gohr        /** @var helper_plugin_extension_extension $ext */
328a8d2f3cbSAndreas Gohr        $ext = $this->loadHelper('extension_extension');
329fe2dcfd5SAndreas Gohr        $tr = new TableFormatter($this->colors);
330a8d2f3cbSAndreas Gohr
331a8d2f3cbSAndreas Gohr
332a8d2f3cbSAndreas Gohr        foreach ($list as $name) {
333a8d2f3cbSAndreas Gohr            $ext->setExtension($name);
334a8d2f3cbSAndreas Gohr
335a8d2f3cbSAndreas Gohr            $status = '';
336a8d2f3cbSAndreas Gohr            if ($ext->isInstalled()) {
337a8d2f3cbSAndreas Gohr                $date = $ext->getInstalledVersion();
338a8d2f3cbSAndreas Gohr                $avail = $ext->getLastUpdate();
339a8d2f3cbSAndreas Gohr                $status = 'i';
340a8d2f3cbSAndreas Gohr                if ($avail && $avail > $date) {
341e5688dc7SAndreas Gohr                    $vcolor = Colors::C_RED;
342b9daa2f5SAndreas Gohr                    $status .= 'u';
343a8d2f3cbSAndreas Gohr                } else {
344e5688dc7SAndreas Gohr                    $vcolor = Colors::C_GREEN;
345a8d2f3cbSAndreas Gohr                }
346a8d2f3cbSAndreas Gohr                if ($ext->isGitControlled()) $status = 'g';
347a8d2f3cbSAndreas Gohr                if ($ext->isBundled()) $status = 'b';
348e5688dc7SAndreas Gohr                if ($ext->isEnabled()) {
349e5688dc7SAndreas Gohr                    $ecolor = Colors::C_BROWN;
350e5688dc7SAndreas Gohr                } else {
351e5688dc7SAndreas Gohr                    $ecolor = Colors::C_DARKGRAY;
352e5688dc7SAndreas Gohr                    $status .= 'd';
353e5688dc7SAndreas Gohr                }
354a8d2f3cbSAndreas Gohr            } else {
355d915fa09SAndreas Gohr                $ecolor = null;
356a8d2f3cbSAndreas Gohr                $date = $ext->getLastUpdate();
357e5688dc7SAndreas Gohr                $vcolor = null;
358a8d2f3cbSAndreas Gohr            }
359a8d2f3cbSAndreas Gohr
360b9daa2f5SAndreas Gohr            if ($filter && strpos($status, $filter) === false) {
361b9daa2f5SAndreas Gohr                continue;
362b9daa2f5SAndreas Gohr            }
363a8d2f3cbSAndreas Gohr
364a8d2f3cbSAndreas Gohr            echo $tr->format(
365a8d2f3cbSAndreas Gohr                [20, 3, 12, '*'],
366a8d2f3cbSAndreas Gohr                [
367a8d2f3cbSAndreas Gohr                    $ext->getID(),
368a8d2f3cbSAndreas Gohr                    $status,
369a8d2f3cbSAndreas Gohr                    $date,
370a8d2f3cbSAndreas Gohr                    strip_tags(sprintf(
371a8d2f3cbSAndreas Gohr                        $this->getLang('extensionby'),
372a8d2f3cbSAndreas Gohr                        $ext->getDisplayName(),
373*dccd6b2bSAndreas Gohr                        $this->colors->wrap($ext->getAuthor(), Colors::C_PURPLE)
374*dccd6b2bSAndreas Gohr                    ))
375a8d2f3cbSAndreas Gohr                ],
376a8d2f3cbSAndreas Gohr                [
377e5688dc7SAndreas Gohr                    $ecolor,
378a8d2f3cbSAndreas Gohr                    Colors::C_YELLOW,
379e5688dc7SAndreas Gohr                    $vcolor,
380a8d2f3cbSAndreas Gohr                    null,
381a8d2f3cbSAndreas Gohr                ]
382a8d2f3cbSAndreas Gohr            );
383a8d2f3cbSAndreas Gohr
384a8d2f3cbSAndreas Gohr            if (!$details) continue;
385a8d2f3cbSAndreas Gohr
386a8d2f3cbSAndreas Gohr            echo $tr->format(
387a8d2f3cbSAndreas Gohr                [5, '*'],
388a8d2f3cbSAndreas Gohr                ['', $ext->getDescription()],
389a8d2f3cbSAndreas Gohr                [null, Colors::C_CYAN]
390a8d2f3cbSAndreas Gohr            );
391a8d2f3cbSAndreas Gohr        }
392a8d2f3cbSAndreas Gohr    }
393a8d2f3cbSAndreas Gohr}
394