xref: /dokuwiki/lib/plugins/extension/cli.php (revision 01b2a2823dcc82af6193cb9ea39d835e2a85f9e2)
1a8d2f3cbSAndreas Gohr<?php
2a8d2f3cbSAndreas Gohr
38553d24dSAndreas Gohruse dokuwiki\Extension\CLIPlugin;
47c9966a5SAndreas Gohruse dokuwiki\plugin\extension\Exception as ExtensionException;
57c9966a5SAndreas Gohruse dokuwiki\plugin\extension\Extension;
625d28a01SAndreas Gohruse dokuwiki\plugin\extension\Installer;
77c9966a5SAndreas Gohruse dokuwiki\plugin\extension\Local;
84fd6a1d7SAndreas Gohruse dokuwiki\plugin\extension\Notice;
97c9966a5SAndreas Gohruse dokuwiki\plugin\extension\Repository;
107c9966a5SAndreas Gohruse splitbrain\phpcli\Colors;
117c9966a5SAndreas Gohruse splitbrain\phpcli\Exception;
12fe2dcfd5SAndreas Gohruse splitbrain\phpcli\Options;
13fe2dcfd5SAndreas Gohruse splitbrain\phpcli\TableFormatter;
14a8d2f3cbSAndreas Gohr
15a8d2f3cbSAndreas Gohr/**
16a8d2f3cbSAndreas Gohr * Class cli_plugin_extension
17a8d2f3cbSAndreas Gohr *
18a8d2f3cbSAndreas Gohr * Command Line component for the extension manager
19a8d2f3cbSAndreas Gohr *
20a8d2f3cbSAndreas Gohr * @license GPL2
21a8d2f3cbSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
22a8d2f3cbSAndreas Gohr */
238553d24dSAndreas Gohrclass cli_plugin_extension extends CLIPlugin
24a8d2f3cbSAndreas Gohr{
25a8d2f3cbSAndreas Gohr    /** @inheritdoc */
26fe2dcfd5SAndreas Gohr    protected function setup(Options $options)
27a8d2f3cbSAndreas Gohr    {
28a8d2f3cbSAndreas Gohr        // general setup
297c9966a5SAndreas Gohr        $options->useCompactHelp();
30b9daa2f5SAndreas Gohr        $options->setHelp(
31b9daa2f5SAndreas Gohr            "Manage plugins and templates for this DokuWiki instance\n\n" .
32b9daa2f5SAndreas Gohr            "Status codes:\n" .
33d2d4b908SAndreas Gohr            "   i - installed                    " . Notice::symbol(Notice::SECURITY) . " - security issue\n" .
34d2d4b908SAndreas Gohr            "   b - bundled with DokuWiki        " . Notice::symbol(Notice::ERROR) . " - extension error\n" .
35d2d4b908SAndreas Gohr            "   g - installed via git            " . Notice::symbol(Notice::WARNING) . " - extension warning\n" .
36d2d4b908SAndreas Gohr            "   d - disabled                     " . Notice::symbol(Notice::INFO) . " - extension info\n" .
37160d3688SAndreas Gohr            "   u - update available\n"
38b9daa2f5SAndreas Gohr        );
39a8d2f3cbSAndreas Gohr
40a8d2f3cbSAndreas Gohr        // search
41a8d2f3cbSAndreas Gohr        $options->registerCommand('search', 'Search for an extension');
42a8d2f3cbSAndreas Gohr        $options->registerOption('max', 'Maximum number of results (default 10)', 'm', 'number', 'search');
43a8d2f3cbSAndreas Gohr        $options->registerOption('verbose', 'Show detailed extension information', 'v', false, 'search');
44a8d2f3cbSAndreas Gohr        $options->registerArgument('query', 'The keyword(s) to search for', true, 'search');
45a8d2f3cbSAndreas Gohr
46a8d2f3cbSAndreas Gohr        // list
47a8d2f3cbSAndreas Gohr        $options->registerCommand('list', 'List installed extensions');
48a8d2f3cbSAndreas Gohr        $options->registerOption('verbose', 'Show detailed extension information', 'v', false, 'list');
49b9daa2f5SAndreas Gohr        $options->registerOption('filter', 'Filter by this status', 'f', 'status', 'list');
50a8d2f3cbSAndreas Gohr
51a8d2f3cbSAndreas Gohr        // upgrade
52a8d2f3cbSAndreas Gohr        $options->registerCommand('upgrade', 'Update all installed extensions to their latest versions');
5371eea990SAndreas Gohr        $options->registerOption('git-overwrite', 'Do not skip git-controlled extensions', 'g', false, 'upgrade');
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) {
8479ff0cd0SAndreas Gohr            $this->warning($e->getMessage());
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':
11171eea990SAndreas Gohr                $ret = $this->cmdUpgrade($options->getOpt('git-overwrite', false));
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     */
12671eea990SAndreas Gohr    protected function cmdUpgrade($gitOverwrite)
127a8d2f3cbSAndreas Gohr    {
128160d3688SAndreas Gohr        $local = new Local();
129160d3688SAndreas Gohr        $extensions = [];
130160d3688SAndreas Gohr        foreach ($local->getExtensions() as $ext) {
13171eea990SAndreas Gohr            if ($ext->isGitControlled() && !$gitOverwrite) continue; // skip git controlled extensions
1324fd6a1d7SAndreas Gohr            if ($ext->isUpdateAvailable()) $extensions[] = $ext->getID();
133a8d2f3cbSAndreas Gohr        }
134160d3688SAndreas Gohr        return $this->cmdInstall($extensions);
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        $ok = 0;
147a8d2f3cbSAndreas Gohr        foreach ($extensions as $extname) {
148160d3688SAndreas Gohr            $ext = Extension::createFromId($extname);
149a8d2f3cbSAndreas Gohr
150160d3688SAndreas Gohr            try {
151a8d2f3cbSAndreas Gohr                if ($set) {
152160d3688SAndreas Gohr                    $ext->enable();
153a8d2f3cbSAndreas Gohr                    $msg = 'msg_enabled';
154a8d2f3cbSAndreas Gohr                } else {
155160d3688SAndreas Gohr                    $ext->disable();
156a8d2f3cbSAndreas Gohr                    $msg = 'msg_disabled';
157a8d2f3cbSAndreas Gohr                }
158160d3688SAndreas Gohr                $this->success(sprintf($this->getLang($msg), $ext->getID()));
159160d3688SAndreas Gohr            } catch (ExtensionException $e) {
160160d3688SAndreas Gohr                $this->error($e->getMessage());
161fe2dcfd5SAndreas Gohr                ++$ok;
162a8d2f3cbSAndreas Gohr                continue;
163a8d2f3cbSAndreas Gohr            }
164a8d2f3cbSAndreas Gohr        }
165a8d2f3cbSAndreas Gohr
166a8d2f3cbSAndreas Gohr        return $ok;
167a8d2f3cbSAndreas Gohr    }
168a8d2f3cbSAndreas Gohr
169a8d2f3cbSAndreas Gohr    /**
170a8d2f3cbSAndreas Gohr     * Uninstall one or more extensions
171a8d2f3cbSAndreas Gohr     *
172a8d2f3cbSAndreas Gohr     * @param string[] $extensions
173a8d2f3cbSAndreas Gohr     * @return int
174a8d2f3cbSAndreas Gohr     */
175a8d2f3cbSAndreas Gohr    protected function cmdUnInstall($extensions)
176a8d2f3cbSAndreas Gohr    {
177160d3688SAndreas Gohr        $installer = new Installer();
178a8d2f3cbSAndreas Gohr
179a8d2f3cbSAndreas Gohr        $ok = 0;
180a8d2f3cbSAndreas Gohr        foreach ($extensions as $extname) {
181160d3688SAndreas Gohr            $ext = Extension::createFromId($extname);
182a8d2f3cbSAndreas Gohr
183160d3688SAndreas Gohr            try {
184160d3688SAndreas Gohr                $installer->uninstall($ext);
185a8d2f3cbSAndreas Gohr                $this->success(sprintf($this->getLang('msg_delete_success'), $ext->getID()));
186160d3688SAndreas Gohr            } catch (ExtensionException $e) {
187160d3688SAndreas Gohr                $this->debug($e->getTraceAsString());
188160d3688SAndreas Gohr                $this->error($e->getMessage());
189160d3688SAndreas Gohr                $ok++; // error code is number of failed uninstalls
190a8d2f3cbSAndreas Gohr            }
191a8d2f3cbSAndreas Gohr        }
192a8d2f3cbSAndreas Gohr        return $ok;
193a8d2f3cbSAndreas Gohr    }
194a8d2f3cbSAndreas Gohr
195a8d2f3cbSAndreas Gohr    /**
196a8d2f3cbSAndreas Gohr     * Install one or more extensions
197a8d2f3cbSAndreas Gohr     *
198a8d2f3cbSAndreas Gohr     * @param string[] $extensions
199a8d2f3cbSAndreas Gohr     * @return int
200a8d2f3cbSAndreas Gohr     */
201a8d2f3cbSAndreas Gohr    protected function cmdInstall($extensions)
202a8d2f3cbSAndreas Gohr    {
203a8d2f3cbSAndreas Gohr        $ok = 0;
204a8d2f3cbSAndreas Gohr        foreach ($extensions as $extname) {
205160d3688SAndreas Gohr            $installer = new Installer(true);
206160d3688SAndreas Gohr
20725d28a01SAndreas Gohr            try {
208cc16762dSLocness                if (preg_match("/^https?:\/\//i", $extname)) {
20925d28a01SAndreas Gohr                    $installer->installFromURL($extname, true);
210cc16762dSLocness                } else {
21125d28a01SAndreas Gohr                    $installer->installFromId($extname);
212a8d2f3cbSAndreas Gohr                }
21325d28a01SAndreas Gohr            } catch (ExtensionException $e) {
21425d28a01SAndreas Gohr                $this->debug($e->getTraceAsString());
2155aaea2b0SLocness                $this->error($e->getMessage());
21625d28a01SAndreas Gohr                $ok++; // error code is number of failed installs
2175aaea2b0SLocness            }
2185aaea2b0SLocness
21925d28a01SAndreas Gohr            $processed = $installer->getProcessed();
22025d28a01SAndreas Gohr            foreach ($processed as $id => $status) {
22125d28a01SAndreas Gohr                if ($status == Installer::STATUS_INSTALLED) {
22225d28a01SAndreas Gohr                    $this->success(sprintf($this->getLang('msg_install_success'), $id));
22325d28a01SAndreas Gohr                } elseif ($status == Installer::STATUS_UPDATED) {
22425d28a01SAndreas Gohr                    $this->success(sprintf($this->getLang('msg_update_success'), $id));
225a8d2f3cbSAndreas Gohr                }
226cc16762dSLocness            }
227160d3688SAndreas Gohr        }
22825d28a01SAndreas Gohr
229a8d2f3cbSAndreas Gohr        return $ok;
230a8d2f3cbSAndreas Gohr    }
231a8d2f3cbSAndreas Gohr
232a8d2f3cbSAndreas Gohr    /**
233a8d2f3cbSAndreas Gohr     * Search for an extension
234a8d2f3cbSAndreas Gohr     *
235a8d2f3cbSAndreas Gohr     * @param string $query
236a8d2f3cbSAndreas Gohr     * @param bool $showdetails
237a8d2f3cbSAndreas Gohr     * @param int $max
238a8d2f3cbSAndreas Gohr     * @return int
2399b36c1fcSsplitbrain     * @throws Exception
240a8d2f3cbSAndreas Gohr     */
241a8d2f3cbSAndreas Gohr    protected function cmdSearch($query, $showdetails, $max)
242a8d2f3cbSAndreas Gohr    {
2437c9966a5SAndreas Gohr        $repo = Repository::getInstance();
2447c9966a5SAndreas Gohr        $result = $repo->searchExtensions($query);
245a8d2f3cbSAndreas Gohr        if ($max) {
246a8d2f3cbSAndreas Gohr            $result = array_slice($result, 0, $max);
247a8d2f3cbSAndreas Gohr        }
248a8d2f3cbSAndreas Gohr
249a8d2f3cbSAndreas Gohr        $this->listExtensions($result, $showdetails);
250a8d2f3cbSAndreas Gohr        return 0;
251a8d2f3cbSAndreas Gohr    }
252a8d2f3cbSAndreas Gohr
253a8d2f3cbSAndreas Gohr    /**
254a8d2f3cbSAndreas Gohr     * @param bool $showdetails
255b9daa2f5SAndreas Gohr     * @param string $filter
256a8d2f3cbSAndreas Gohr     * @return int
2579b36c1fcSsplitbrain     * @throws Exception
258a8d2f3cbSAndreas Gohr     */
259b9daa2f5SAndreas Gohr    protected function cmdList($showdetails, $filter)
260a8d2f3cbSAndreas Gohr    {
261*01b2a282SAndreas Gohr        $extensions = (new Local())->getExtensions();
262*01b2a282SAndreas Gohr        // initialize remote data in one go
263*01b2a282SAndreas Gohr        Repository::getInstance()->initExtensions(array_keys($extensions));
264*01b2a282SAndreas Gohr
265*01b2a282SAndreas Gohr        $this->listExtensions($extensions, $showdetails, $filter);
266a8d2f3cbSAndreas Gohr        return 0;
267a8d2f3cbSAndreas Gohr    }
268a8d2f3cbSAndreas Gohr
269a8d2f3cbSAndreas Gohr    /**
270a8d2f3cbSAndreas Gohr     * List the given extensions
271a8d2f3cbSAndreas Gohr     *
2727c9966a5SAndreas Gohr     * @param Extension[] $list
273a8d2f3cbSAndreas Gohr     * @param bool $details display details
274b9daa2f5SAndreas Gohr     * @param string $filter filter for this status
2759b36c1fcSsplitbrain     * @throws Exception
276160d3688SAndreas Gohr     * @todo break into smaller methods
277a8d2f3cbSAndreas Gohr     */
278b9daa2f5SAndreas Gohr    protected function listExtensions($list, $details, $filter = '')
279a8d2f3cbSAndreas Gohr    {
280fe2dcfd5SAndreas Gohr        $tr = new TableFormatter($this->colors);
2817c9966a5SAndreas Gohr        foreach ($list as $ext) {
282a8d2f3cbSAndreas Gohr            $status = '';
283a8d2f3cbSAndreas Gohr            if ($ext->isInstalled()) {
284a8d2f3cbSAndreas Gohr                $date = $ext->getInstalledVersion();
285a8d2f3cbSAndreas Gohr                $avail = $ext->getLastUpdate();
286a8d2f3cbSAndreas Gohr                $status = 'i';
287a8d2f3cbSAndreas Gohr                if ($avail && $avail > $date) {
288e5688dc7SAndreas Gohr                    $vcolor = Colors::C_RED;
289b9daa2f5SAndreas Gohr                    $status .= 'u';
290a8d2f3cbSAndreas Gohr                } else {
291e5688dc7SAndreas Gohr                    $vcolor = Colors::C_GREEN;
292a8d2f3cbSAndreas Gohr                }
293a8d2f3cbSAndreas Gohr                if ($ext->isGitControlled()) $status = 'g';
29499fd248dSAndreas Gohr                if ($ext->isBundled()) {
29599fd248dSAndreas Gohr                    $status = 'b';
29699fd248dSAndreas Gohr                    $date = '<bundled>';
29799fd248dSAndreas Gohr                    $vcolor = null;
29899fd248dSAndreas Gohr                }
299e5688dc7SAndreas Gohr                if ($ext->isEnabled()) {
300e5688dc7SAndreas Gohr                    $ecolor = Colors::C_BROWN;
301e5688dc7SAndreas Gohr                } else {
302e5688dc7SAndreas Gohr                    $ecolor = Colors::C_DARKGRAY;
303e5688dc7SAndreas Gohr                    $status .= 'd';
304e5688dc7SAndreas Gohr                }
305a8d2f3cbSAndreas Gohr            } else {
306d915fa09SAndreas Gohr                $ecolor = null;
307a8d2f3cbSAndreas Gohr                $date = $ext->getLastUpdate();
308e5688dc7SAndreas Gohr                $vcolor = null;
309a8d2f3cbSAndreas Gohr            }
310a8d2f3cbSAndreas Gohr
311b9daa2f5SAndreas Gohr            if ($filter && strpos($status, $filter) === false) {
312b9daa2f5SAndreas Gohr                continue;
313b9daa2f5SAndreas Gohr            }
314a8d2f3cbSAndreas Gohr
3154fd6a1d7SAndreas Gohr            $notices = Notice::list($ext);
316d2d4b908SAndreas Gohr            if ($notices[Notice::SECURITY]) $status .= Notice::symbol(Notice::SECURITY);
317d2d4b908SAndreas Gohr            if ($notices[Notice::ERROR]) $status .= Notice::symbol(Notice::ERROR);
318d2d4b908SAndreas Gohr            if ($notices[Notice::WARNING]) $status .= Notice::symbol(Notice::WARNING);
319d2d4b908SAndreas Gohr            if ($notices[Notice::INFO]) $status .= Notice::symbol(Notice::INFO);
32099fd248dSAndreas Gohr
321a8d2f3cbSAndreas Gohr            echo $tr->format(
322160d3688SAndreas Gohr                [20, 5, 12, '*'],
323a8d2f3cbSAndreas Gohr                [
324a8d2f3cbSAndreas Gohr                    $ext->getID(),
325a8d2f3cbSAndreas Gohr                    $status,
326a8d2f3cbSAndreas Gohr                    $date,
327a8d2f3cbSAndreas Gohr                    strip_tags(sprintf(
328a8d2f3cbSAndreas Gohr                        $this->getLang('extensionby'),
329a8d2f3cbSAndreas Gohr                        $ext->getDisplayName(),
330dccd6b2bSAndreas Gohr                        $this->colors->wrap($ext->getAuthor(), Colors::C_PURPLE)
331dccd6b2bSAndreas Gohr                    ))
332a8d2f3cbSAndreas Gohr                ],
333a8d2f3cbSAndreas Gohr                [
334e5688dc7SAndreas Gohr                    $ecolor,
335a8d2f3cbSAndreas Gohr                    Colors::C_YELLOW,
336e5688dc7SAndreas Gohr                    $vcolor,
337a8d2f3cbSAndreas Gohr                    null,
338a8d2f3cbSAndreas Gohr                ]
339a8d2f3cbSAndreas Gohr            );
340a8d2f3cbSAndreas Gohr
34199fd248dSAndreas Gohr
342a8d2f3cbSAndreas Gohr            if (!$details) continue;
343a8d2f3cbSAndreas Gohr
344a8d2f3cbSAndreas Gohr            echo $tr->format(
345160d3688SAndreas Gohr                [7, '*'],
346a8d2f3cbSAndreas Gohr                ['', $ext->getDescription()],
347a8d2f3cbSAndreas Gohr                [null, Colors::C_CYAN]
348a8d2f3cbSAndreas Gohr            );
3494fd6a1d7SAndreas Gohr            foreach ($notices as $type => $msgs) {
3504fd6a1d7SAndreas Gohr                if (!$msgs) continue;
3514fd6a1d7SAndreas Gohr                foreach ($msgs as $msg) {
35299fd248dSAndreas Gohr                    echo $tr->format(
353160d3688SAndreas Gohr                        [7, '*'],
354d2d4b908SAndreas Gohr                        ['', Notice::symbol($type) . ' ' . $msg],
35599fd248dSAndreas Gohr                        [null, Colors::C_LIGHTBLUE]
35699fd248dSAndreas Gohr                    );
35799fd248dSAndreas Gohr                }
358160d3688SAndreas Gohr            }
359a8d2f3cbSAndreas Gohr        }
360a8d2f3cbSAndreas Gohr    }
361a8d2f3cbSAndreas Gohr}
362