1*36c0b2b4SAndreas Gohr#!/usr/bin/env php 2*36c0b2b4SAndreas Gohr<?php 3*36c0b2b4SAndreas Gohr 4*36c0b2b4SAndreas Gohruse dokuwiki\Extension\CLIPlugin; 5*36c0b2b4SAndreas Gohruse dokuwiki\Extension\PluginController; 6*36c0b2b4SAndreas Gohruse splitbrain\phpcli\Exception as CliException; 7*36c0b2b4SAndreas Gohruse splitbrain\phpcli\Options; 8*36c0b2b4SAndreas Gohr 9*36c0b2b4SAndreas Gohr/** 10*36c0b2b4SAndreas Gohr * @license GPL2 11*36c0b2b4SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 12*36c0b2b4SAndreas Gohr */ 13*36c0b2b4SAndreas Gohrclass cli_plugin_dev extends CLIPlugin 14*36c0b2b4SAndreas Gohr{ 15*36c0b2b4SAndreas Gohr 16*36c0b2b4SAndreas Gohr /** 17*36c0b2b4SAndreas Gohr * Register options and arguments on the given $options object 18*36c0b2b4SAndreas Gohr * 19*36c0b2b4SAndreas Gohr * @param Options $options 20*36c0b2b4SAndreas Gohr * @return void 21*36c0b2b4SAndreas Gohr */ 22*36c0b2b4SAndreas Gohr protected function setup(Options $options) 23*36c0b2b4SAndreas Gohr { 24*36c0b2b4SAndreas Gohr $options->setHelp( 25*36c0b2b4SAndreas Gohr "CLI to help with plugin and template development\n\n" . 26*36c0b2b4SAndreas Gohr "Run this script from within the extension's directory." 27*36c0b2b4SAndreas Gohr ); 28*36c0b2b4SAndreas Gohr 29*36c0b2b4SAndreas Gohr $options->registerCommand('init', 'initialize a new plugin or template'); 30*36c0b2b4SAndreas Gohr $options->registerCommand('addTest', 'add the testing framework files and optionall a new test file'); 31*36c0b2b4SAndreas Gohr $options->registerArgument('test', 'The name of the new test, leave out for general test.', false, 'addTest'); 32*36c0b2b4SAndreas Gohr $options->registerCommand('addConf', 'add the configuration files'); 33*36c0b2b4SAndreas Gohr $options->registerCommand('addLang', 'add the language files'); 34*36c0b2b4SAndreas Gohr 35*36c0b2b4SAndreas Gohr $options->registerCommand('addComponent', 'add a component'); 36*36c0b2b4SAndreas Gohr $options->registerArgument('type', 'The type of the component. Needs to be one of ' . 37*36c0b2b4SAndreas Gohr join(', ', PluginController::PLUGIN_TYPES), 38*36c0b2b4SAndreas Gohr true, 'addComponent' 39*36c0b2b4SAndreas Gohr ); 40*36c0b2b4SAndreas Gohr $options->registerArgument('name', 'Optional name of the component', false, 'addComponent'); 41*36c0b2b4SAndreas Gohr 42*36c0b2b4SAndreas Gohr $options->registerCommand('deletedFiles', 'create the list of deleted files base on the git history'); 43*36c0b2b4SAndreas Gohr } 44*36c0b2b4SAndreas Gohr 45*36c0b2b4SAndreas Gohr /** @inheritDoc */ 46*36c0b2b4SAndreas Gohr protected function main(Options $options) 47*36c0b2b4SAndreas Gohr { 48*36c0b2b4SAndreas Gohr switch ($options->getCmd()) { 49*36c0b2b4SAndreas Gohr case 'init': 50*36c0b2b4SAndreas Gohr return $this->cmdInit(); 51*36c0b2b4SAndreas Gohr case 'addTest': 52*36c0b2b4SAndreas Gohr $args = $options->getArgs(); 53*36c0b2b4SAndreas Gohr $test = array_shift($args); 54*36c0b2b4SAndreas Gohr return $this->cmdAddTest($test); 55*36c0b2b4SAndreas Gohr case 'addConf': 56*36c0b2b4SAndreas Gohr return $this->cmdAddConf(); 57*36c0b2b4SAndreas Gohr case 'addLang': 58*36c0b2b4SAndreas Gohr return $this->cmdAddLang(); 59*36c0b2b4SAndreas Gohr case 'addComponent': 60*36c0b2b4SAndreas Gohr $args = $options->getArgs(); 61*36c0b2b4SAndreas Gohr $type = array_shift($args); 62*36c0b2b4SAndreas Gohr $component = array_shift($args); 63*36c0b2b4SAndreas Gohr return $this->cmdAddComponent($type, $component); 64*36c0b2b4SAndreas Gohr case 'deletedFiles': 65*36c0b2b4SAndreas Gohr return $this->cmdDeletedFiles(); 66*36c0b2b4SAndreas Gohr default: 67*36c0b2b4SAndreas Gohr echo $options->help(); 68*36c0b2b4SAndreas Gohr return 0; 69*36c0b2b4SAndreas Gohr } 70*36c0b2b4SAndreas Gohr } 71*36c0b2b4SAndreas Gohr 72*36c0b2b4SAndreas Gohr /** 73*36c0b2b4SAndreas Gohr * Get the extension name from the current working directory 74*36c0b2b4SAndreas Gohr * 75*36c0b2b4SAndreas Gohr * @throws CliException if something's wrong 76*36c0b2b4SAndreas Gohr * @param string $dir 77*36c0b2b4SAndreas Gohr * @return string[] name, type 78*36c0b2b4SAndreas Gohr */ 79*36c0b2b4SAndreas Gohr protected function getTypedNameFromDir($dir) 80*36c0b2b4SAndreas Gohr { 81*36c0b2b4SAndreas Gohr $pdir = fullpath(DOKU_PLUGIN); 82*36c0b2b4SAndreas Gohr $tdir = fullpath(tpl_incdir() . '../'); 83*36c0b2b4SAndreas Gohr 84*36c0b2b4SAndreas Gohr if (strpos($dir, $pdir) === 0) { 85*36c0b2b4SAndreas Gohr $ldir = substr($dir, strlen($pdir)); 86*36c0b2b4SAndreas Gohr $type = 'plugin'; 87*36c0b2b4SAndreas Gohr } elseif (strpos($dir, $tdir) === 0) { 88*36c0b2b4SAndreas Gohr $ldir = substr($dir, strlen($tdir)); 89*36c0b2b4SAndreas Gohr $type = 'template'; 90*36c0b2b4SAndreas Gohr } else { 91*36c0b2b4SAndreas Gohr throw new CliException('Current directory needs to be in plugin or template directory'); 92*36c0b2b4SAndreas Gohr } 93*36c0b2b4SAndreas Gohr 94*36c0b2b4SAndreas Gohr $ldir = trim($ldir, '/'); 95*36c0b2b4SAndreas Gohr 96*36c0b2b4SAndreas Gohr if (strpos($ldir, '/') !== false) { 97*36c0b2b4SAndreas Gohr throw new CliException('Current directory has to be main extension directory'); 98*36c0b2b4SAndreas Gohr } 99*36c0b2b4SAndreas Gohr 100*36c0b2b4SAndreas Gohr return [$ldir, $type]; 101*36c0b2b4SAndreas Gohr } 102*36c0b2b4SAndreas Gohr 103*36c0b2b4SAndreas Gohr /** 104*36c0b2b4SAndreas Gohr * Interactively ask for a value from the user 105*36c0b2b4SAndreas Gohr * 106*36c0b2b4SAndreas Gohr * @param string $prompt 107*36c0b2b4SAndreas Gohr * @param bool $cache cache given value for next time? 108*36c0b2b4SAndreas Gohr * @return string 109*36c0b2b4SAndreas Gohr */ 110*36c0b2b4SAndreas Gohr protected function readLine($prompt, $cache = false) 111*36c0b2b4SAndreas Gohr { 112*36c0b2b4SAndreas Gohr $value = ''; 113*36c0b2b4SAndreas Gohr $default = ''; 114*36c0b2b4SAndreas Gohr $cachename = getCacheName($prompt, '.readline'); 115*36c0b2b4SAndreas Gohr if ($cache && file_exists($cachename)) { 116*36c0b2b4SAndreas Gohr $default = file_get_contents($cachename); 117*36c0b2b4SAndreas Gohr } 118*36c0b2b4SAndreas Gohr 119*36c0b2b4SAndreas Gohr while ($value === '') { 120*36c0b2b4SAndreas Gohr echo $prompt; 121*36c0b2b4SAndreas Gohr if ($default) echo ' [' . $default . ']'; 122*36c0b2b4SAndreas Gohr echo ': '; 123*36c0b2b4SAndreas Gohr 124*36c0b2b4SAndreas Gohr $fh = fopen('php://stdin', 'r'); 125*36c0b2b4SAndreas Gohr $value = trim(fgets($fh)); 126*36c0b2b4SAndreas Gohr fclose($fh); 127*36c0b2b4SAndreas Gohr 128*36c0b2b4SAndreas Gohr if ($value === '') $value = $default; 129*36c0b2b4SAndreas Gohr } 130*36c0b2b4SAndreas Gohr 131*36c0b2b4SAndreas Gohr if ($cache) { 132*36c0b2b4SAndreas Gohr file_put_contents($cachename, $value); 133*36c0b2b4SAndreas Gohr } 134*36c0b2b4SAndreas Gohr 135*36c0b2b4SAndreas Gohr return $value; 136*36c0b2b4SAndreas Gohr } 137*36c0b2b4SAndreas Gohr 138*36c0b2b4SAndreas Gohr /** 139*36c0b2b4SAndreas Gohr * Download a skeleton file and do the replacements 140*36c0b2b4SAndreas Gohr * 141*36c0b2b4SAndreas Gohr * @param string $skel Skeleton relative to the skel dir in the repo 142*36c0b2b4SAndreas Gohr * @param string $target Target file relative to the main directory 143*36c0b2b4SAndreas Gohr * @param array $replacements 144*36c0b2b4SAndreas Gohr */ 145*36c0b2b4SAndreas Gohr protected function loadSkeleton($skel, $target, $replacements) 146*36c0b2b4SAndreas Gohr { 147*36c0b2b4SAndreas Gohr if (file_exists($target)) { 148*36c0b2b4SAndreas Gohr $this->error($target . ' already exists'); 149*36c0b2b4SAndreas Gohr return; 150*36c0b2b4SAndreas Gohr } 151*36c0b2b4SAndreas Gohr 152*36c0b2b4SAndreas Gohr $base = 'https://raw.githubusercontent.com/dokufreaks/dokuwiki-plugin-wizard/master/skel/'; 153*36c0b2b4SAndreas Gohr $http = new \dokuwiki\HTTP\DokuHTTPClient(); 154*36c0b2b4SAndreas Gohr $content = $http->get($base . $skel); 155*36c0b2b4SAndreas Gohr 156*36c0b2b4SAndreas Gohr $content = str_replace( 157*36c0b2b4SAndreas Gohr array_keys($replacements), 158*36c0b2b4SAndreas Gohr array_values($replacements), 159*36c0b2b4SAndreas Gohr $content 160*36c0b2b4SAndreas Gohr ); 161*36c0b2b4SAndreas Gohr 162*36c0b2b4SAndreas Gohr io_makeFileDir($target); 163*36c0b2b4SAndreas Gohr file_put_contents($target, $content); 164*36c0b2b4SAndreas Gohr $this->success('Added ' . $target); 165*36c0b2b4SAndreas Gohr } 166*36c0b2b4SAndreas Gohr 167*36c0b2b4SAndreas Gohr /** 168*36c0b2b4SAndreas Gohr * Prepare the string replacements 169*36c0b2b4SAndreas Gohr * 170*36c0b2b4SAndreas Gohr * @param array $replacements override defaults 171*36c0b2b4SAndreas Gohr * @return array 172*36c0b2b4SAndreas Gohr */ 173*36c0b2b4SAndreas Gohr protected function prepareReplacements($replacements = []) 174*36c0b2b4SAndreas Gohr { 175*36c0b2b4SAndreas Gohr // defaults 176*36c0b2b4SAndreas Gohr $data = [ 177*36c0b2b4SAndreas Gohr '@@AUTHOR_NAME@@' => '', 178*36c0b2b4SAndreas Gohr '@@AUTHOR_MAIL@@' => '', 179*36c0b2b4SAndreas Gohr '@@PLUGIN_NAME@@' => '', 180*36c0b2b4SAndreas Gohr '@@PLUGIN_DESC@@' => '', 181*36c0b2b4SAndreas Gohr '@@PLUGIN_URL@@' => '', 182*36c0b2b4SAndreas Gohr '@@PLUGIN_TYPE@@' => '', 183*36c0b2b4SAndreas Gohr '@@INSTALL_DIR@@' => 'plugins', 184*36c0b2b4SAndreas Gohr '@@DATE@@' => date('Y-m-d'), 185*36c0b2b4SAndreas Gohr ]; 186*36c0b2b4SAndreas Gohr 187*36c0b2b4SAndreas Gohr // load from existing plugin.info 188*36c0b2b4SAndreas Gohr $dir = fullpath(getcwd()); 189*36c0b2b4SAndreas Gohr [$name, $type] = $this->getTypedNameFromDir($dir); 190*36c0b2b4SAndreas Gohr if (file_exists("$type.info.txt")) { 191*36c0b2b4SAndreas Gohr $info = confToHash("$type.info.txt"); 192*36c0b2b4SAndreas Gohr $data['@@AUTHOR_NAME@@'] = $info['author']; 193*36c0b2b4SAndreas Gohr $data['@@AUTHOR_MAIL@@'] = $info['email']; 194*36c0b2b4SAndreas Gohr $data['@@PLUGIN_DESC@@'] = $info['desc']; 195*36c0b2b4SAndreas Gohr $data['@@PLUGIN_URL@@'] = $info['url']; 196*36c0b2b4SAndreas Gohr } 197*36c0b2b4SAndreas Gohr $data['@@PLUGIN_NAME@@'] = $name; 198*36c0b2b4SAndreas Gohr $data['@@PLUGIN_TYPE@@'] = $type; 199*36c0b2b4SAndreas Gohr 200*36c0b2b4SAndreas Gohr if ($type == 'template') { 201*36c0b2b4SAndreas Gohr $data['@@INSTALL_DIR@@'] = 'tpl'; 202*36c0b2b4SAndreas Gohr } 203*36c0b2b4SAndreas Gohr 204*36c0b2b4SAndreas Gohr // merge given overrides 205*36c0b2b4SAndreas Gohr $data = array_merge($data, $replacements); 206*36c0b2b4SAndreas Gohr 207*36c0b2b4SAndreas Gohr // set inherited defaults 208*36c0b2b4SAndreas Gohr if (empty($data['@@PLUGIN_URL@@'])) { 209*36c0b2b4SAndreas Gohr $data['@@PLUGIN_URL@@'] = 210*36c0b2b4SAndreas Gohr 'https://www.dokuwiki.org/' . 211*36c0b2b4SAndreas Gohr $data['@@PLUGIN_TYPE@@'] . ':' . 212*36c0b2b4SAndreas Gohr $data['@@PLUGIN_NAME@@']; 213*36c0b2b4SAndreas Gohr } 214*36c0b2b4SAndreas Gohr 215*36c0b2b4SAndreas Gohr return $data; 216*36c0b2b4SAndreas Gohr } 217*36c0b2b4SAndreas Gohr 218*36c0b2b4SAndreas Gohr /** 219*36c0b2b4SAndreas Gohr * Replacements needed for action components. 220*36c0b2b4SAndreas Gohr * 221*36c0b2b4SAndreas Gohr * Not cool but that' what we need currently 222*36c0b2b4SAndreas Gohr * 223*36c0b2b4SAndreas Gohr * @return string[] 224*36c0b2b4SAndreas Gohr */ 225*36c0b2b4SAndreas Gohr protected function actionReplacements() 226*36c0b2b4SAndreas Gohr { 227*36c0b2b4SAndreas Gohr $fn = 'handleEventName'; 228*36c0b2b4SAndreas Gohr $register = ' $controller->register_hook(\'EVENT_NAME\', \'AFTER|BEFORE\', $this, \'' . $fn . '\');'; 229*36c0b2b4SAndreas Gohr $handler = ' public function ' . $fn . '(Doku_Event $event, $param)' . "\n" 230*36c0b2b4SAndreas Gohr . " {\n" 231*36c0b2b4SAndreas Gohr . " }\n"; 232*36c0b2b4SAndreas Gohr 233*36c0b2b4SAndreas Gohr return [ 234*36c0b2b4SAndreas Gohr '@@REGISTER@@' => $register . "\n ", 235*36c0b2b4SAndreas Gohr '@@HANDLERS@@' => $handler, 236*36c0b2b4SAndreas Gohr ]; 237*36c0b2b4SAndreas Gohr } 238*36c0b2b4SAndreas Gohr 239*36c0b2b4SAndreas Gohr /** 240*36c0b2b4SAndreas Gohr * Intialize the current directory as a plugin or template 241*36c0b2b4SAndreas Gohr * 242*36c0b2b4SAndreas Gohr * @return int 243*36c0b2b4SAndreas Gohr */ 244*36c0b2b4SAndreas Gohr protected function cmdInit() 245*36c0b2b4SAndreas Gohr { 246*36c0b2b4SAndreas Gohr $dir = fullpath(getcwd()); 247*36c0b2b4SAndreas Gohr if ((new FilesystemIterator($dir))->valid()) { 248*36c0b2b4SAndreas Gohr throw new CliException('Current directory needs to be empty'); 249*36c0b2b4SAndreas Gohr } 250*36c0b2b4SAndreas Gohr 251*36c0b2b4SAndreas Gohr [$name, $type] = $this->getTypedNameFromDir($dir); 252*36c0b2b4SAndreas Gohr $user = $this->readLine('Your Name', true); 253*36c0b2b4SAndreas Gohr $mail = $this->readLine('Your E-Mail', true); 254*36c0b2b4SAndreas Gohr $desc = $this->readLine('Short description'); 255*36c0b2b4SAndreas Gohr 256*36c0b2b4SAndreas Gohr $replacements = [ 257*36c0b2b4SAndreas Gohr '@@AUTHOR_NAME@@' => $user, 258*36c0b2b4SAndreas Gohr '@@AUTHOR_MAIL@@' => $mail, 259*36c0b2b4SAndreas Gohr '@@PLUGIN_NAME@@' => $name, 260*36c0b2b4SAndreas Gohr '@@PLUGIN_DESC@@' => $desc, 261*36c0b2b4SAndreas Gohr '@@PLUGIN_TYPE@@' => $type, 262*36c0b2b4SAndreas Gohr ]; 263*36c0b2b4SAndreas Gohr $replacements = $this->prepareReplacements($replacements); 264*36c0b2b4SAndreas Gohr 265*36c0b2b4SAndreas Gohr $this->loadSkeleton('info.skel', $type . '.info.txt', $replacements); 266*36c0b2b4SAndreas Gohr $this->loadSkeleton('README.skel', 'README', $replacements); // fixme needs to be type specific 267*36c0b2b4SAndreas Gohr $this->loadSkeleton('LICENSE.skel', 'LICENSE', $replacements); 268*36c0b2b4SAndreas Gohr 269*36c0b2b4SAndreas Gohr return 0; 270*36c0b2b4SAndreas Gohr } 271*36c0b2b4SAndreas Gohr 272*36c0b2b4SAndreas Gohr /** 273*36c0b2b4SAndreas Gohr * Add test framework 274*36c0b2b4SAndreas Gohr * 275*36c0b2b4SAndreas Gohr * @param string $test Name of the Test to add 276*36c0b2b4SAndreas Gohr * @return int 277*36c0b2b4SAndreas Gohr */ 278*36c0b2b4SAndreas Gohr protected function cmdAddTest($test = '') 279*36c0b2b4SAndreas Gohr { 280*36c0b2b4SAndreas Gohr $test = ucfirst(strtolower($test)); 281*36c0b2b4SAndreas Gohr 282*36c0b2b4SAndreas Gohr $replacements = $this->prepareReplacements(['@@TEST@@' => $test]); 283*36c0b2b4SAndreas Gohr $this->loadSkeleton('.github/workflows/phpTestLinux.skel', '.github/workflows/phpTestLinux.yml', $replacements); 284*36c0b2b4SAndreas Gohr if ($test) { 285*36c0b2b4SAndreas Gohr $this->loadSkeleton('_test/StandardTest.skel', '_test/' . $test . 'Test.php', $replacements); 286*36c0b2b4SAndreas Gohr } else { 287*36c0b2b4SAndreas Gohr $this->loadSkeleton('_test/GeneralTest.skel', '_test/GeneralTest.php', $replacements); 288*36c0b2b4SAndreas Gohr } 289*36c0b2b4SAndreas Gohr 290*36c0b2b4SAndreas Gohr return 0; 291*36c0b2b4SAndreas Gohr } 292*36c0b2b4SAndreas Gohr 293*36c0b2b4SAndreas Gohr /** 294*36c0b2b4SAndreas Gohr * Add configuration 295*36c0b2b4SAndreas Gohr * 296*36c0b2b4SAndreas Gohr * @return int 297*36c0b2b4SAndreas Gohr */ 298*36c0b2b4SAndreas Gohr protected function cmdAddConf() 299*36c0b2b4SAndreas Gohr { 300*36c0b2b4SAndreas Gohr $replacements = $this->prepareReplacements(); 301*36c0b2b4SAndreas Gohr $this->loadSkeleton('conf/default.skel', 'conf/default.php', $replacements); 302*36c0b2b4SAndreas Gohr $this->loadSkeleton('conf/metadata.skel', 'conf/metadata.php', $replacements); 303*36c0b2b4SAndreas Gohr if (is_dir('lang')) { 304*36c0b2b4SAndreas Gohr $this->loadSkeleton('lang/settings.skel', 'lang/en/settings.php', $replacements); 305*36c0b2b4SAndreas Gohr } 306*36c0b2b4SAndreas Gohr 307*36c0b2b4SAndreas Gohr return 0; 308*36c0b2b4SAndreas Gohr } 309*36c0b2b4SAndreas Gohr 310*36c0b2b4SAndreas Gohr /** 311*36c0b2b4SAndreas Gohr * Add language 312*36c0b2b4SAndreas Gohr * 313*36c0b2b4SAndreas Gohr * @return int 314*36c0b2b4SAndreas Gohr */ 315*36c0b2b4SAndreas Gohr protected function cmdAddLang() 316*36c0b2b4SAndreas Gohr { 317*36c0b2b4SAndreas Gohr $replacements = $this->prepareReplacements(); 318*36c0b2b4SAndreas Gohr $this->loadSkeleton('lang/lang.skel', 'lang/en/lang.php', $replacements); 319*36c0b2b4SAndreas Gohr if (is_dir('conf')) { 320*36c0b2b4SAndreas Gohr $this->loadSkeleton('lang/settings.skel', 'lang/en/settings.php', $replacements); 321*36c0b2b4SAndreas Gohr } 322*36c0b2b4SAndreas Gohr 323*36c0b2b4SAndreas Gohr return 0; 324*36c0b2b4SAndreas Gohr } 325*36c0b2b4SAndreas Gohr 326*36c0b2b4SAndreas Gohr /** 327*36c0b2b4SAndreas Gohr * Add another component to the plugin 328*36c0b2b4SAndreas Gohr * 329*36c0b2b4SAndreas Gohr * @param string $type 330*36c0b2b4SAndreas Gohr * @param string $component 331*36c0b2b4SAndreas Gohr */ 332*36c0b2b4SAndreas Gohr protected function cmdAddComponent($type, $component = '') 333*36c0b2b4SAndreas Gohr { 334*36c0b2b4SAndreas Gohr $dir = fullpath(getcwd()); 335*36c0b2b4SAndreas Gohr list($plugin, $extension) = $this->getTypedNameFromDir($dir); 336*36c0b2b4SAndreas Gohr if ($extension != 'plugin') throw new CliException('Components can only be added to plugins'); 337*36c0b2b4SAndreas Gohr if (!in_array($type, PluginController::PLUGIN_TYPES)) { 338*36c0b2b4SAndreas Gohr throw new CliException('Invalid type ' . $type); 339*36c0b2b4SAndreas Gohr } 340*36c0b2b4SAndreas Gohr 341*36c0b2b4SAndreas Gohr if ($component) { 342*36c0b2b4SAndreas Gohr $path = $type . '/' . $component . '.php'; 343*36c0b2b4SAndreas Gohr $class = $type . '_plugin_' . $plugin . '_' . $component; 344*36c0b2b4SAndreas Gohr $self = $plugin . '_' . $component; 345*36c0b2b4SAndreas Gohr } else { 346*36c0b2b4SAndreas Gohr $path = $type . '.php'; 347*36c0b2b4SAndreas Gohr $class = $type . '_plugin_' . $plugin; 348*36c0b2b4SAndreas Gohr $self = $plugin; 349*36c0b2b4SAndreas Gohr } 350*36c0b2b4SAndreas Gohr 351*36c0b2b4SAndreas Gohr $replacements = $this->actionReplacements(); 352*36c0b2b4SAndreas Gohr $replacements['@@PLUGIN_COMPONENT_NAME@@'] = $class; 353*36c0b2b4SAndreas Gohr $replacements['@@SYNTAX_COMPONENT_NAME@@'] = $self; 354*36c0b2b4SAndreas Gohr $replacements = $this->prepareReplacements($replacements); 355*36c0b2b4SAndreas Gohr $this->loadSkeleton($type . '.skel', $path, $replacements); 356*36c0b2b4SAndreas Gohr 357*36c0b2b4SAndreas Gohr return 0; 358*36c0b2b4SAndreas Gohr } 359*36c0b2b4SAndreas Gohr 360*36c0b2b4SAndreas Gohr /** 361*36c0b2b4SAndreas Gohr * Generate a list of deleted files from git 362*36c0b2b4SAndreas Gohr * 363*36c0b2b4SAndreas Gohr * @link https://stackoverflow.com/a/6018049/172068 364*36c0b2b4SAndreas Gohr */ 365*36c0b2b4SAndreas Gohr protected function cmdDeletedFiles() 366*36c0b2b4SAndreas Gohr { 367*36c0b2b4SAndreas Gohr $cmd = 'git log --no-renames --pretty=format: --name-only --diff-filter=D'; 368*36c0b2b4SAndreas Gohr $output = []; 369*36c0b2b4SAndreas Gohr $result = 0; 370*36c0b2b4SAndreas Gohr 371*36c0b2b4SAndreas Gohr $last = exec($cmd, $output, $result); 372*36c0b2b4SAndreas Gohr if ($last === false || $result !== 0) { 373*36c0b2b4SAndreas Gohr throw new CliException('Running git failed'); 374*36c0b2b4SAndreas Gohr } 375*36c0b2b4SAndreas Gohr 376*36c0b2b4SAndreas Gohr $output = array_map('trim', $output); 377*36c0b2b4SAndreas Gohr $output = array_filter($output); 378*36c0b2b4SAndreas Gohr $output = array_unique($output); 379*36c0b2b4SAndreas Gohr $output = array_filter($output, function ($item) { 380*36c0b2b4SAndreas Gohr return !file_exists($item); 381*36c0b2b4SAndreas Gohr }); 382*36c0b2b4SAndreas Gohr sort($output); 383*36c0b2b4SAndreas Gohr 384*36c0b2b4SAndreas Gohr if (!count($output)) { 385*36c0b2b4SAndreas Gohr $this->info('No deleted files found'); 386*36c0b2b4SAndreas Gohr return 0; 387*36c0b2b4SAndreas Gohr } 388*36c0b2b4SAndreas Gohr 389*36c0b2b4SAndreas Gohr $content = "# This is a list of files that were present in previous releases\n" . 390*36c0b2b4SAndreas Gohr "# but were removed later. They should not exist in your installation.\n" . 391*36c0b2b4SAndreas Gohr join("\n", $output) . "\n"; 392*36c0b2b4SAndreas Gohr 393*36c0b2b4SAndreas Gohr file_put_contents('deleted.files', $content); 394*36c0b2b4SAndreas Gohr $this->success('written deleted.files'); 395*36c0b2b4SAndreas Gohr return 0; 396*36c0b2b4SAndreas Gohr } 397*36c0b2b4SAndreas Gohr} 398