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