xref: /plugin/dev/cli.php (revision 8b06c9ddd5580bf297c4ccef265d047dcdf5f94e)
136c0b2b4SAndreas Gohr#!/usr/bin/env php
236c0b2b4SAndreas Gohr<?php
336c0b2b4SAndreas Gohr
436c0b2b4SAndreas Gohruse dokuwiki\Extension\CLIPlugin;
536c0b2b4SAndreas Gohruse dokuwiki\Extension\PluginController;
636c0b2b4SAndreas Gohruse splitbrain\phpcli\Exception as CliException;
736c0b2b4SAndreas Gohruse splitbrain\phpcli\Options;
836c0b2b4SAndreas Gohr
936c0b2b4SAndreas Gohr/**
1036c0b2b4SAndreas Gohr * @license GPL2
1136c0b2b4SAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
1236c0b2b4SAndreas Gohr */
1336c0b2b4SAndreas Gohrclass cli_plugin_dev extends CLIPlugin
1436c0b2b4SAndreas Gohr{
1536c0b2b4SAndreas Gohr
1636c0b2b4SAndreas Gohr    /**
1736c0b2b4SAndreas Gohr     * Register options and arguments on the given $options object
1836c0b2b4SAndreas Gohr     *
1936c0b2b4SAndreas Gohr     * @param Options $options
2036c0b2b4SAndreas Gohr     * @return void
2136c0b2b4SAndreas Gohr     */
2236c0b2b4SAndreas Gohr    protected function setup(Options $options)
2336c0b2b4SAndreas Gohr    {
2436c0b2b4SAndreas Gohr        $options->setHelp(
2536c0b2b4SAndreas Gohr            "CLI to help with plugin and template development\n\n" .
2636c0b2b4SAndreas Gohr            "Run this script from within the extension's directory."
2736c0b2b4SAndreas Gohr        );
2836c0b2b4SAndreas Gohr
2936c0b2b4SAndreas Gohr        $options->registerCommand('init', 'initialize a new plugin or template');
3036c0b2b4SAndreas Gohr        $options->registerCommand('addTest', 'add the testing framework files and optionall a new test file');
3136c0b2b4SAndreas Gohr        $options->registerArgument('test', 'The name of the new test, leave out for general test.', false, 'addTest');
3236c0b2b4SAndreas Gohr        $options->registerCommand('addConf', 'add the configuration files');
3336c0b2b4SAndreas Gohr        $options->registerCommand('addLang', 'add the language files');
3436c0b2b4SAndreas Gohr
3536c0b2b4SAndreas Gohr        $options->registerCommand('addComponent', 'add a component');
3636c0b2b4SAndreas Gohr        $options->registerArgument('type', 'The type of the component. Needs to be one of ' .
3736c0b2b4SAndreas Gohr            join(', ', PluginController::PLUGIN_TYPES),
3836c0b2b4SAndreas Gohr            true, 'addComponent'
3936c0b2b4SAndreas Gohr        );
4036c0b2b4SAndreas Gohr        $options->registerArgument('name', 'Optional name of the component', false, 'addComponent');
4136c0b2b4SAndreas Gohr
4236c0b2b4SAndreas Gohr        $options->registerCommand('deletedFiles', 'create the list of deleted files base on the git history');
4336c0b2b4SAndreas Gohr    }
4436c0b2b4SAndreas Gohr
4536c0b2b4SAndreas Gohr    /** @inheritDoc */
4636c0b2b4SAndreas Gohr    protected function main(Options $options)
4736c0b2b4SAndreas Gohr    {
4836c0b2b4SAndreas Gohr        switch ($options->getCmd()) {
4936c0b2b4SAndreas Gohr            case 'init':
5036c0b2b4SAndreas Gohr                return $this->cmdInit();
5136c0b2b4SAndreas Gohr            case 'addTest':
5236c0b2b4SAndreas Gohr                $args = $options->getArgs();
5336c0b2b4SAndreas Gohr                $test = array_shift($args);
5436c0b2b4SAndreas Gohr                return $this->cmdAddTest($test);
5536c0b2b4SAndreas Gohr            case 'addConf':
5636c0b2b4SAndreas Gohr                return $this->cmdAddConf();
5736c0b2b4SAndreas Gohr            case 'addLang':
5836c0b2b4SAndreas Gohr                return $this->cmdAddLang();
5936c0b2b4SAndreas Gohr            case 'addComponent':
6036c0b2b4SAndreas Gohr                $args = $options->getArgs();
6136c0b2b4SAndreas Gohr                $type = array_shift($args);
6236c0b2b4SAndreas Gohr                $component = array_shift($args);
6336c0b2b4SAndreas Gohr                return $this->cmdAddComponent($type, $component);
6436c0b2b4SAndreas Gohr            case 'deletedFiles':
6536c0b2b4SAndreas Gohr                return $this->cmdDeletedFiles();
6636c0b2b4SAndreas Gohr            default:
6736c0b2b4SAndreas Gohr                echo $options->help();
6836c0b2b4SAndreas Gohr                return 0;
6936c0b2b4SAndreas Gohr        }
7036c0b2b4SAndreas Gohr    }
7136c0b2b4SAndreas Gohr
7236c0b2b4SAndreas Gohr    /**
7336c0b2b4SAndreas Gohr     * Get the extension name from the current working directory
7436c0b2b4SAndreas Gohr     *
7536c0b2b4SAndreas Gohr     * @throws CliException if something's wrong
7636c0b2b4SAndreas Gohr     * @param string $dir
7736c0b2b4SAndreas Gohr     * @return string[] name, type
7836c0b2b4SAndreas Gohr     */
7936c0b2b4SAndreas Gohr    protected function getTypedNameFromDir($dir)
8036c0b2b4SAndreas Gohr    {
8136c0b2b4SAndreas Gohr        $pdir = fullpath(DOKU_PLUGIN);
8236c0b2b4SAndreas Gohr        $tdir = fullpath(tpl_incdir() . '../');
8336c0b2b4SAndreas Gohr
8436c0b2b4SAndreas Gohr        if (strpos($dir, $pdir) === 0) {
8536c0b2b4SAndreas Gohr            $ldir = substr($dir, strlen($pdir));
8636c0b2b4SAndreas Gohr            $type = 'plugin';
8736c0b2b4SAndreas Gohr        } elseif (strpos($dir, $tdir) === 0) {
8836c0b2b4SAndreas Gohr            $ldir = substr($dir, strlen($tdir));
8936c0b2b4SAndreas Gohr            $type = 'template';
9036c0b2b4SAndreas Gohr        } else {
9136c0b2b4SAndreas Gohr            throw new CliException('Current directory needs to be in plugin or template directory');
9236c0b2b4SAndreas Gohr        }
9336c0b2b4SAndreas Gohr
9436c0b2b4SAndreas Gohr        $ldir = trim($ldir, '/');
9536c0b2b4SAndreas Gohr
9636c0b2b4SAndreas Gohr        if (strpos($ldir, '/') !== false) {
9736c0b2b4SAndreas Gohr            throw new CliException('Current directory has to be main extension directory');
9836c0b2b4SAndreas Gohr        }
9936c0b2b4SAndreas Gohr
10036c0b2b4SAndreas Gohr        return [$ldir, $type];
10136c0b2b4SAndreas Gohr    }
10236c0b2b4SAndreas Gohr
10336c0b2b4SAndreas Gohr    /**
10436c0b2b4SAndreas Gohr     * Interactively ask for a value from the user
10536c0b2b4SAndreas Gohr     *
10636c0b2b4SAndreas Gohr     * @param string $prompt
10736c0b2b4SAndreas Gohr     * @param bool $cache cache given value for next time?
10836c0b2b4SAndreas Gohr     * @return string
10936c0b2b4SAndreas Gohr     */
11036c0b2b4SAndreas Gohr    protected function readLine($prompt, $cache = false)
11136c0b2b4SAndreas Gohr    {
11236c0b2b4SAndreas Gohr        $value = '';
11336c0b2b4SAndreas Gohr        $default = '';
11436c0b2b4SAndreas Gohr        $cachename = getCacheName($prompt, '.readline');
11536c0b2b4SAndreas Gohr        if ($cache && file_exists($cachename)) {
11636c0b2b4SAndreas Gohr            $default = file_get_contents($cachename);
11736c0b2b4SAndreas Gohr        }
11836c0b2b4SAndreas Gohr
11936c0b2b4SAndreas Gohr        while ($value === '') {
12036c0b2b4SAndreas Gohr            echo $prompt;
12136c0b2b4SAndreas Gohr            if ($default) echo ' [' . $default . ']';
12236c0b2b4SAndreas Gohr            echo ': ';
12336c0b2b4SAndreas Gohr
12436c0b2b4SAndreas Gohr            $fh = fopen('php://stdin', 'r');
12536c0b2b4SAndreas Gohr            $value = trim(fgets($fh));
12636c0b2b4SAndreas Gohr            fclose($fh);
12736c0b2b4SAndreas Gohr
12836c0b2b4SAndreas Gohr            if ($value === '') $value = $default;
12936c0b2b4SAndreas Gohr        }
13036c0b2b4SAndreas Gohr
13136c0b2b4SAndreas Gohr        if ($cache) {
13236c0b2b4SAndreas Gohr            file_put_contents($cachename, $value);
13336c0b2b4SAndreas Gohr        }
13436c0b2b4SAndreas Gohr
13536c0b2b4SAndreas Gohr        return $value;
13636c0b2b4SAndreas Gohr    }
13736c0b2b4SAndreas Gohr
13836c0b2b4SAndreas Gohr    /**
13936c0b2b4SAndreas Gohr     * Download a skeleton file and do the replacements
14036c0b2b4SAndreas Gohr     *
14136c0b2b4SAndreas Gohr     * @param string $skel Skeleton relative to the skel dir in the repo
14236c0b2b4SAndreas Gohr     * @param string $target Target file relative to the main directory
14336c0b2b4SAndreas Gohr     * @param array $replacements
14436c0b2b4SAndreas Gohr     */
14536c0b2b4SAndreas Gohr    protected function loadSkeleton($skel, $target, $replacements)
14636c0b2b4SAndreas Gohr    {
14736c0b2b4SAndreas Gohr        if (file_exists($target)) {
14836c0b2b4SAndreas Gohr            $this->error($target . ' already exists');
14936c0b2b4SAndreas Gohr            return;
15036c0b2b4SAndreas Gohr        }
15136c0b2b4SAndreas Gohr
15236c0b2b4SAndreas Gohr        $base = 'https://raw.githubusercontent.com/dokufreaks/dokuwiki-plugin-wizard/master/skel/';
15336c0b2b4SAndreas Gohr        $http = new \dokuwiki\HTTP\DokuHTTPClient();
15436c0b2b4SAndreas Gohr        $content = $http->get($base . $skel);
15536c0b2b4SAndreas Gohr
15636c0b2b4SAndreas Gohr        $content = str_replace(
15736c0b2b4SAndreas Gohr            array_keys($replacements),
15836c0b2b4SAndreas Gohr            array_values($replacements),
15936c0b2b4SAndreas Gohr            $content
16036c0b2b4SAndreas Gohr        );
16136c0b2b4SAndreas Gohr
16236c0b2b4SAndreas Gohr        io_makeFileDir($target);
16336c0b2b4SAndreas Gohr        file_put_contents($target, $content);
16436c0b2b4SAndreas Gohr        $this->success('Added ' . $target);
16536c0b2b4SAndreas Gohr    }
16636c0b2b4SAndreas Gohr
16736c0b2b4SAndreas Gohr    /**
16836c0b2b4SAndreas Gohr     * Prepare the string replacements
16936c0b2b4SAndreas Gohr     *
17036c0b2b4SAndreas Gohr     * @param array $replacements override defaults
17136c0b2b4SAndreas Gohr     * @return array
17236c0b2b4SAndreas Gohr     */
17336c0b2b4SAndreas Gohr    protected function prepareReplacements($replacements = [])
17436c0b2b4SAndreas Gohr    {
17536c0b2b4SAndreas Gohr        // defaults
17636c0b2b4SAndreas Gohr        $data = [
17736c0b2b4SAndreas Gohr            '@@AUTHOR_NAME@@' => '',
17836c0b2b4SAndreas Gohr            '@@AUTHOR_MAIL@@' => '',
17936c0b2b4SAndreas Gohr            '@@PLUGIN_NAME@@' => '',
18036c0b2b4SAndreas Gohr            '@@PLUGIN_DESC@@' => '',
18136c0b2b4SAndreas Gohr            '@@PLUGIN_URL@@' => '',
18236c0b2b4SAndreas Gohr            '@@PLUGIN_TYPE@@' => '',
18336c0b2b4SAndreas Gohr            '@@INSTALL_DIR@@' => 'plugins',
18436c0b2b4SAndreas Gohr            '@@DATE@@' => date('Y-m-d'),
18536c0b2b4SAndreas Gohr        ];
18636c0b2b4SAndreas Gohr
18736c0b2b4SAndreas Gohr        // load from existing plugin.info
18836c0b2b4SAndreas Gohr        $dir = fullpath(getcwd());
18936c0b2b4SAndreas Gohr        [$name, $type] = $this->getTypedNameFromDir($dir);
19036c0b2b4SAndreas Gohr        if (file_exists("$type.info.txt")) {
19136c0b2b4SAndreas Gohr            $info = confToHash("$type.info.txt");
19236c0b2b4SAndreas Gohr            $data['@@AUTHOR_NAME@@'] = $info['author'];
19336c0b2b4SAndreas Gohr            $data['@@AUTHOR_MAIL@@'] = $info['email'];
19436c0b2b4SAndreas Gohr            $data['@@PLUGIN_DESC@@'] = $info['desc'];
19536c0b2b4SAndreas Gohr            $data['@@PLUGIN_URL@@'] = $info['url'];
19636c0b2b4SAndreas Gohr        }
19736c0b2b4SAndreas Gohr        $data['@@PLUGIN_NAME@@'] = $name;
19836c0b2b4SAndreas Gohr        $data['@@PLUGIN_TYPE@@'] = $type;
19936c0b2b4SAndreas Gohr
20036c0b2b4SAndreas Gohr        if ($type == 'template') {
20136c0b2b4SAndreas Gohr            $data['@@INSTALL_DIR@@'] = 'tpl';
20236c0b2b4SAndreas Gohr        }
20336c0b2b4SAndreas Gohr
20436c0b2b4SAndreas Gohr        // merge given overrides
20536c0b2b4SAndreas Gohr        $data = array_merge($data, $replacements);
20636c0b2b4SAndreas Gohr
20736c0b2b4SAndreas Gohr        // set inherited defaults
20836c0b2b4SAndreas Gohr        if (empty($data['@@PLUGIN_URL@@'])) {
20936c0b2b4SAndreas Gohr            $data['@@PLUGIN_URL@@'] =
21036c0b2b4SAndreas Gohr                'https://www.dokuwiki.org/' .
21136c0b2b4SAndreas Gohr                $data['@@PLUGIN_TYPE@@'] . ':' .
21236c0b2b4SAndreas Gohr                $data['@@PLUGIN_NAME@@'];
21336c0b2b4SAndreas Gohr        }
21436c0b2b4SAndreas Gohr
21536c0b2b4SAndreas Gohr        return $data;
21636c0b2b4SAndreas Gohr    }
21736c0b2b4SAndreas Gohr
21836c0b2b4SAndreas Gohr    /**
21936c0b2b4SAndreas Gohr     * Replacements needed for action components.
22036c0b2b4SAndreas Gohr     *
22136c0b2b4SAndreas Gohr     * Not cool but that' what we need currently
22236c0b2b4SAndreas Gohr     *
22336c0b2b4SAndreas Gohr     * @return string[]
22436c0b2b4SAndreas Gohr     */
22536c0b2b4SAndreas Gohr    protected function actionReplacements()
22636c0b2b4SAndreas Gohr    {
22736c0b2b4SAndreas Gohr        $fn = 'handleEventName';
22836c0b2b4SAndreas Gohr        $register = '        $controller->register_hook(\'EVENT_NAME\', \'AFTER|BEFORE\', $this, \'' . $fn . '\');';
22936c0b2b4SAndreas Gohr        $handler = '    public function ' . $fn . '(Doku_Event $event, $param)' . "\n"
23036c0b2b4SAndreas Gohr            . "    {\n"
23136c0b2b4SAndreas Gohr            . "    }\n";
23236c0b2b4SAndreas Gohr
23336c0b2b4SAndreas Gohr        return [
23436c0b2b4SAndreas Gohr            '@@REGISTER@@' => $register . "\n   ",
23536c0b2b4SAndreas Gohr            '@@HANDLERS@@' => $handler,
23636c0b2b4SAndreas Gohr        ];
23736c0b2b4SAndreas Gohr    }
23836c0b2b4SAndreas Gohr
23936c0b2b4SAndreas Gohr    /**
24036c0b2b4SAndreas Gohr     * Intialize the current directory as a plugin or template
24136c0b2b4SAndreas Gohr     *
24236c0b2b4SAndreas Gohr     * @return int
24336c0b2b4SAndreas Gohr     */
24436c0b2b4SAndreas Gohr    protected function cmdInit()
24536c0b2b4SAndreas Gohr    {
24636c0b2b4SAndreas Gohr        $dir = fullpath(getcwd());
24736c0b2b4SAndreas Gohr        if ((new FilesystemIterator($dir))->valid()) {
24836c0b2b4SAndreas Gohr            throw new CliException('Current directory needs to be empty');
24936c0b2b4SAndreas Gohr        }
25036c0b2b4SAndreas Gohr
25136c0b2b4SAndreas Gohr        [$name, $type] = $this->getTypedNameFromDir($dir);
25236c0b2b4SAndreas Gohr        $user = $this->readLine('Your Name', true);
25336c0b2b4SAndreas Gohr        $mail = $this->readLine('Your E-Mail', true);
25436c0b2b4SAndreas Gohr        $desc = $this->readLine('Short description');
25536c0b2b4SAndreas Gohr
25636c0b2b4SAndreas Gohr        $replacements = [
25736c0b2b4SAndreas Gohr            '@@AUTHOR_NAME@@' => $user,
25836c0b2b4SAndreas Gohr            '@@AUTHOR_MAIL@@' => $mail,
25936c0b2b4SAndreas Gohr            '@@PLUGIN_NAME@@' => $name,
26036c0b2b4SAndreas Gohr            '@@PLUGIN_DESC@@' => $desc,
26136c0b2b4SAndreas Gohr            '@@PLUGIN_TYPE@@' => $type,
26236c0b2b4SAndreas Gohr        ];
26336c0b2b4SAndreas Gohr        $replacements = $this->prepareReplacements($replacements);
26436c0b2b4SAndreas Gohr
26536c0b2b4SAndreas Gohr        $this->loadSkeleton('info.skel', $type . '.info.txt', $replacements);
26636c0b2b4SAndreas Gohr        $this->loadSkeleton('README.skel', 'README', $replacements); // fixme needs to be type specific
26736c0b2b4SAndreas Gohr        $this->loadSkeleton('LICENSE.skel', 'LICENSE', $replacements);
26836c0b2b4SAndreas Gohr
269*8b06c9ddSAndreas Gohr        try {
270*8b06c9ddSAndreas Gohr            $this->git('init');
271*8b06c9ddSAndreas Gohr        } catch (CliException $e) {
272*8b06c9ddSAndreas Gohr            $this->error($e->getMessage());
273*8b06c9ddSAndreas Gohr        }
274*8b06c9ddSAndreas Gohr
27536c0b2b4SAndreas Gohr        return 0;
27636c0b2b4SAndreas Gohr    }
27736c0b2b4SAndreas Gohr
27836c0b2b4SAndreas Gohr    /**
27936c0b2b4SAndreas Gohr     * Add test framework
28036c0b2b4SAndreas Gohr     *
28136c0b2b4SAndreas Gohr     * @param string $test Name of the Test to add
28236c0b2b4SAndreas Gohr     * @return int
28336c0b2b4SAndreas Gohr     */
28436c0b2b4SAndreas Gohr    protected function cmdAddTest($test = '')
28536c0b2b4SAndreas Gohr    {
28636c0b2b4SAndreas Gohr        $test = ucfirst(strtolower($test));
28736c0b2b4SAndreas Gohr
28836c0b2b4SAndreas Gohr        $replacements = $this->prepareReplacements(['@@TEST@@' => $test]);
28936c0b2b4SAndreas Gohr        $this->loadSkeleton('.github/workflows/phpTestLinux.skel', '.github/workflows/phpTestLinux.yml', $replacements);
29036c0b2b4SAndreas Gohr        if ($test) {
29136c0b2b4SAndreas Gohr            $this->loadSkeleton('_test/StandardTest.skel', '_test/' . $test . 'Test.php', $replacements);
29236c0b2b4SAndreas Gohr        } else {
29336c0b2b4SAndreas Gohr            $this->loadSkeleton('_test/GeneralTest.skel', '_test/GeneralTest.php', $replacements);
29436c0b2b4SAndreas Gohr        }
29536c0b2b4SAndreas Gohr
29636c0b2b4SAndreas Gohr        return 0;
29736c0b2b4SAndreas Gohr    }
29836c0b2b4SAndreas Gohr
29936c0b2b4SAndreas Gohr    /**
30036c0b2b4SAndreas Gohr     * Add configuration
30136c0b2b4SAndreas Gohr     *
30236c0b2b4SAndreas Gohr     * @return int
30336c0b2b4SAndreas Gohr     */
30436c0b2b4SAndreas Gohr    protected function cmdAddConf()
30536c0b2b4SAndreas Gohr    {
30636c0b2b4SAndreas Gohr        $replacements = $this->prepareReplacements();
30736c0b2b4SAndreas Gohr        $this->loadSkeleton('conf/default.skel', 'conf/default.php', $replacements);
30836c0b2b4SAndreas Gohr        $this->loadSkeleton('conf/metadata.skel', 'conf/metadata.php', $replacements);
30936c0b2b4SAndreas Gohr        if (is_dir('lang')) {
31036c0b2b4SAndreas Gohr            $this->loadSkeleton('lang/settings.skel', 'lang/en/settings.php', $replacements);
31136c0b2b4SAndreas Gohr        }
31236c0b2b4SAndreas Gohr
31336c0b2b4SAndreas Gohr        return 0;
31436c0b2b4SAndreas Gohr    }
31536c0b2b4SAndreas Gohr
31636c0b2b4SAndreas Gohr    /**
31736c0b2b4SAndreas Gohr     * Add language
31836c0b2b4SAndreas Gohr     *
31936c0b2b4SAndreas Gohr     * @return int
32036c0b2b4SAndreas Gohr     */
32136c0b2b4SAndreas Gohr    protected function cmdAddLang()
32236c0b2b4SAndreas Gohr    {
32336c0b2b4SAndreas Gohr        $replacements = $this->prepareReplacements();
32436c0b2b4SAndreas Gohr        $this->loadSkeleton('lang/lang.skel', 'lang/en/lang.php', $replacements);
32536c0b2b4SAndreas Gohr        if (is_dir('conf')) {
32636c0b2b4SAndreas Gohr            $this->loadSkeleton('lang/settings.skel', 'lang/en/settings.php', $replacements);
32736c0b2b4SAndreas Gohr        }
32836c0b2b4SAndreas Gohr
32936c0b2b4SAndreas Gohr        return 0;
33036c0b2b4SAndreas Gohr    }
33136c0b2b4SAndreas Gohr
33236c0b2b4SAndreas Gohr    /**
33336c0b2b4SAndreas Gohr     * Add another component to the plugin
33436c0b2b4SAndreas Gohr     *
33536c0b2b4SAndreas Gohr     * @param string $type
33636c0b2b4SAndreas Gohr     * @param string $component
33736c0b2b4SAndreas Gohr     */
33836c0b2b4SAndreas Gohr    protected function cmdAddComponent($type, $component = '')
33936c0b2b4SAndreas Gohr    {
34036c0b2b4SAndreas Gohr        $dir = fullpath(getcwd());
34136c0b2b4SAndreas Gohr        list($plugin, $extension) = $this->getTypedNameFromDir($dir);
34236c0b2b4SAndreas Gohr        if ($extension != 'plugin') throw  new CliException('Components can only be added to plugins');
34336c0b2b4SAndreas Gohr        if (!in_array($type, PluginController::PLUGIN_TYPES)) {
34436c0b2b4SAndreas Gohr            throw new CliException('Invalid type ' . $type);
34536c0b2b4SAndreas Gohr        }
34636c0b2b4SAndreas Gohr
34736c0b2b4SAndreas Gohr        if ($component) {
34836c0b2b4SAndreas Gohr            $path = $type . '/' . $component . '.php';
34936c0b2b4SAndreas Gohr            $class = $type . '_plugin_' . $plugin . '_' . $component;
35036c0b2b4SAndreas Gohr            $self = $plugin . '_' . $component;
35136c0b2b4SAndreas Gohr        } else {
35236c0b2b4SAndreas Gohr            $path = $type . '.php';
35336c0b2b4SAndreas Gohr            $class = $type . '_plugin_' . $plugin;
35436c0b2b4SAndreas Gohr            $self = $plugin;
35536c0b2b4SAndreas Gohr        }
35636c0b2b4SAndreas Gohr
35736c0b2b4SAndreas Gohr        $replacements = $this->actionReplacements();
35836c0b2b4SAndreas Gohr        $replacements['@@PLUGIN_COMPONENT_NAME@@'] = $class;
35936c0b2b4SAndreas Gohr        $replacements['@@SYNTAX_COMPONENT_NAME@@'] = $self;
36036c0b2b4SAndreas Gohr        $replacements = $this->prepareReplacements($replacements);
36136c0b2b4SAndreas Gohr        $this->loadSkeleton($type . '.skel', $path, $replacements);
36236c0b2b4SAndreas Gohr
36336c0b2b4SAndreas Gohr        return 0;
36436c0b2b4SAndreas Gohr    }
36536c0b2b4SAndreas Gohr
36636c0b2b4SAndreas Gohr    /**
36736c0b2b4SAndreas Gohr     * Generate a list of deleted files from git
36836c0b2b4SAndreas Gohr     *
36936c0b2b4SAndreas Gohr     * @link https://stackoverflow.com/a/6018049/172068
37036c0b2b4SAndreas Gohr     */
37136c0b2b4SAndreas Gohr    protected function cmdDeletedFiles()
37236c0b2b4SAndreas Gohr    {
373*8b06c9ddSAndreas Gohr        if(!is_dir('.git')) throw new CliException('This extension seems not to be managed by git');
37436c0b2b4SAndreas Gohr
375*8b06c9ddSAndreas Gohr        $output = $this->git('log', '--no-renames', '--pretty=format:', '--name-only', '--diff-filter=D');
37636c0b2b4SAndreas Gohr        $output = array_map('trim', $output);
37736c0b2b4SAndreas Gohr        $output = array_filter($output);
37836c0b2b4SAndreas Gohr        $output = array_unique($output);
37936c0b2b4SAndreas Gohr        $output = array_filter($output, function ($item) {
38036c0b2b4SAndreas Gohr            return !file_exists($item);
38136c0b2b4SAndreas Gohr        });
38236c0b2b4SAndreas Gohr        sort($output);
38336c0b2b4SAndreas Gohr
38436c0b2b4SAndreas Gohr        if (!count($output)) {
38536c0b2b4SAndreas Gohr            $this->info('No deleted files found');
38636c0b2b4SAndreas Gohr            return 0;
38736c0b2b4SAndreas Gohr        }
38836c0b2b4SAndreas Gohr
38936c0b2b4SAndreas Gohr        $content = "# This is a list of files that were present in previous releases\n" .
39036c0b2b4SAndreas Gohr            "# but were removed later. They should not exist in your installation.\n" .
39136c0b2b4SAndreas Gohr            join("\n", $output) . "\n";
39236c0b2b4SAndreas Gohr
39336c0b2b4SAndreas Gohr        file_put_contents('deleted.files', $content);
39436c0b2b4SAndreas Gohr        $this->success('written deleted.files');
39536c0b2b4SAndreas Gohr        return 0;
39636c0b2b4SAndreas Gohr    }
397*8b06c9ddSAndreas Gohr
398*8b06c9ddSAndreas Gohr    /**
399*8b06c9ddSAndreas Gohr     * Run git with the given arguments and return the output
400*8b06c9ddSAndreas Gohr     *
401*8b06c9ddSAndreas Gohr     * @throws CliException when the command can't be run
402*8b06c9ddSAndreas Gohr     * @param string ...$args
403*8b06c9ddSAndreas Gohr     * @return string[]
404*8b06c9ddSAndreas Gohr     */
405*8b06c9ddSAndreas Gohr    protected function git(...$args)
406*8b06c9ddSAndreas Gohr    {
407*8b06c9ddSAndreas Gohr        $args = array_map('escapeshellarg', $args);
408*8b06c9ddSAndreas Gohr        $cmd = 'git ' . join(' ', $args);
409*8b06c9ddSAndreas Gohr        $output = [];
410*8b06c9ddSAndreas Gohr        $result = 0;
411*8b06c9ddSAndreas Gohr
412*8b06c9ddSAndreas Gohr        $this->info($cmd);
413*8b06c9ddSAndreas Gohr        $last = exec($cmd, $output, $result);
414*8b06c9ddSAndreas Gohr        if ($last === false || $result !== 0) {
415*8b06c9ddSAndreas Gohr            throw new CliException('Running git failed');
416*8b06c9ddSAndreas Gohr        }
417*8b06c9ddSAndreas Gohr
418*8b06c9ddSAndreas Gohr        return $output;
419*8b06c9ddSAndreas Gohr    }
42036c0b2b4SAndreas Gohr}
421