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