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