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