1a8d2f3cbSAndreas Gohr<?php 2a8d2f3cbSAndreas Gohr 38553d24dSAndreas Gohruse dokuwiki\Extension\CLIPlugin; 47c9966a5SAndreas Gohruse dokuwiki\plugin\extension\Exception as ExtensionException; 57c9966a5SAndreas Gohruse dokuwiki\plugin\extension\Extension; 67c9966a5SAndreas Gohruse dokuwiki\plugin\extension\Local; 77c9966a5SAndreas Gohruse dokuwiki\plugin\extension\Repository; 87c9966a5SAndreas Gohruse splitbrain\phpcli\Colors; 97c9966a5SAndreas Gohruse splitbrain\phpcli\Exception; 10fe2dcfd5SAndreas Gohruse splitbrain\phpcli\Options; 11fe2dcfd5SAndreas Gohruse splitbrain\phpcli\TableFormatter; 12a8d2f3cbSAndreas Gohr 13a8d2f3cbSAndreas Gohr/** 14a8d2f3cbSAndreas Gohr * Class cli_plugin_extension 15a8d2f3cbSAndreas Gohr * 16a8d2f3cbSAndreas Gohr * Command Line component for the extension manager 17a8d2f3cbSAndreas Gohr * 18a8d2f3cbSAndreas Gohr * @license GPL2 19a8d2f3cbSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 20a8d2f3cbSAndreas Gohr */ 218553d24dSAndreas Gohrclass cli_plugin_extension extends CLIPlugin 22a8d2f3cbSAndreas Gohr{ 23a8d2f3cbSAndreas Gohr /** @inheritdoc */ 24fe2dcfd5SAndreas Gohr protected function setup(Options $options) 25a8d2f3cbSAndreas Gohr { 26a8d2f3cbSAndreas Gohr // general setup 277c9966a5SAndreas Gohr $options->useCompactHelp(); 28b9daa2f5SAndreas Gohr $options->setHelp( 29b9daa2f5SAndreas Gohr "Manage plugins and templates for this DokuWiki instance\n\n" . 30b9daa2f5SAndreas Gohr "Status codes:\n" . 31b9daa2f5SAndreas Gohr " i - installed\n" . 32b9daa2f5SAndreas Gohr " b - bundled with DokuWiki\n" . 33b9daa2f5SAndreas Gohr " g - installed via git\n" . 34b9daa2f5SAndreas Gohr " d - disabled\n" . 35*99fd248dSAndreas Gohr " u - update available\n" . 36*99fd248dSAndreas Gohr " ☠ - security issue\n" . 37*99fd248dSAndreas Gohr " ⚠ - security warning\n" . 38*99fd248dSAndreas Gohr " ▽ - update message\n" 39b9daa2f5SAndreas Gohr ); 40a8d2f3cbSAndreas Gohr 41a8d2f3cbSAndreas Gohr // search 42a8d2f3cbSAndreas Gohr $options->registerCommand('search', 'Search for an extension'); 43a8d2f3cbSAndreas Gohr $options->registerOption('max', 'Maximum number of results (default 10)', 'm', 'number', 'search'); 44a8d2f3cbSAndreas Gohr $options->registerOption('verbose', 'Show detailed extension information', 'v', false, 'search'); 45a8d2f3cbSAndreas Gohr $options->registerArgument('query', 'The keyword(s) to search for', true, 'search'); 46a8d2f3cbSAndreas Gohr 47a8d2f3cbSAndreas Gohr // list 48a8d2f3cbSAndreas Gohr $options->registerCommand('list', 'List installed extensions'); 49a8d2f3cbSAndreas Gohr $options->registerOption('verbose', 'Show detailed extension information', 'v', false, 'list'); 50b9daa2f5SAndreas Gohr $options->registerOption('filter', 'Filter by this status', 'f', 'status', 'list'); 51a8d2f3cbSAndreas Gohr 52a8d2f3cbSAndreas Gohr // upgrade 53a8d2f3cbSAndreas Gohr $options->registerCommand('upgrade', 'Update all installed extensions to their latest versions'); 54a8d2f3cbSAndreas Gohr 55a8d2f3cbSAndreas Gohr // install 56a8d2f3cbSAndreas Gohr $options->registerCommand('install', 'Install or upgrade extensions'); 57dccd6b2bSAndreas Gohr $options->registerArgument( 58dccd6b2bSAndreas Gohr 'extensions...', 59dccd6b2bSAndreas Gohr 'One or more extensions to install. Either by name or download URL', 60dccd6b2bSAndreas Gohr true, 61dccd6b2bSAndreas Gohr 'install' 62e2170488SAndreas Gohr ); 63a8d2f3cbSAndreas Gohr 64a8d2f3cbSAndreas Gohr // uninstall 65a8d2f3cbSAndreas Gohr $options->registerCommand('uninstall', 'Uninstall a new extension'); 66a8d2f3cbSAndreas Gohr $options->registerArgument('extensions...', 'One or more extensions to install', true, 'uninstall'); 67a8d2f3cbSAndreas Gohr 68a8d2f3cbSAndreas Gohr // enable 69a8d2f3cbSAndreas Gohr $options->registerCommand('enable', 'Enable installed extensions'); 70a8d2f3cbSAndreas Gohr $options->registerArgument('extensions...', 'One or more extensions to enable', true, 'enable'); 71a8d2f3cbSAndreas Gohr 72a8d2f3cbSAndreas Gohr // disable 73a8d2f3cbSAndreas Gohr $options->registerCommand('disable', 'Disable installed extensions'); 74a8d2f3cbSAndreas Gohr $options->registerArgument('extensions...', 'One or more extensions to disable', true, 'disable'); 75a8d2f3cbSAndreas Gohr } 76a8d2f3cbSAndreas Gohr 77a8d2f3cbSAndreas Gohr /** @inheritdoc */ 78fe2dcfd5SAndreas Gohr protected function main(Options $options) 79a8d2f3cbSAndreas Gohr { 807c9966a5SAndreas Gohr $repo = Repository::getInstance(); 817c9966a5SAndreas Gohr try { 827c9966a5SAndreas Gohr $repo->checkAccess(); 837c9966a5SAndreas Gohr } catch (ExtensionException $e) { 84ed3520eeSAndreas Gohr $this->warning('Extension Repository API is not accessible, no remote info available!'); 85ed3520eeSAndreas Gohr } 86ed3520eeSAndreas Gohr 87a8d2f3cbSAndreas Gohr switch ($options->getCmd()) { 88a8d2f3cbSAndreas Gohr case 'list': 89b9daa2f5SAndreas Gohr $ret = $this->cmdList($options->getOpt('verbose'), $options->getOpt('filter', '')); 90a8d2f3cbSAndreas Gohr break; 91a8d2f3cbSAndreas Gohr case 'search': 92a8d2f3cbSAndreas Gohr $ret = $this->cmdSearch( 93a8d2f3cbSAndreas Gohr implode(' ', $options->getArgs()), 94a8d2f3cbSAndreas Gohr $options->getOpt('verbose'), 95a8d2f3cbSAndreas Gohr (int)$options->getOpt('max', 10) 96a8d2f3cbSAndreas Gohr ); 97a8d2f3cbSAndreas Gohr break; 98a8d2f3cbSAndreas Gohr case 'install': 99a8d2f3cbSAndreas Gohr $ret = $this->cmdInstall($options->getArgs()); 100a8d2f3cbSAndreas Gohr break; 101a8d2f3cbSAndreas Gohr case 'uninstall': 102a8d2f3cbSAndreas Gohr $ret = $this->cmdUnInstall($options->getArgs()); 103a8d2f3cbSAndreas Gohr break; 104a8d2f3cbSAndreas Gohr case 'enable': 105a8d2f3cbSAndreas Gohr $ret = $this->cmdEnable(true, $options->getArgs()); 106a8d2f3cbSAndreas Gohr break; 107a8d2f3cbSAndreas Gohr case 'disable': 108a8d2f3cbSAndreas Gohr $ret = $this->cmdEnable(false, $options->getArgs()); 109a8d2f3cbSAndreas Gohr break; 110a8d2f3cbSAndreas Gohr case 'upgrade': 111a8d2f3cbSAndreas Gohr $ret = $this->cmdUpgrade(); 112a8d2f3cbSAndreas Gohr break; 113a8d2f3cbSAndreas Gohr default: 114a8d2f3cbSAndreas Gohr echo $options->help(); 115a8d2f3cbSAndreas Gohr $ret = 0; 116a8d2f3cbSAndreas Gohr } 117a8d2f3cbSAndreas Gohr 118a8d2f3cbSAndreas Gohr exit($ret); 119a8d2f3cbSAndreas Gohr } 120a8d2f3cbSAndreas Gohr 121a8d2f3cbSAndreas Gohr /** 122a8d2f3cbSAndreas Gohr * Upgrade all extensions 123a8d2f3cbSAndreas Gohr * 124a8d2f3cbSAndreas Gohr * @return int 125a8d2f3cbSAndreas Gohr */ 126a8d2f3cbSAndreas Gohr protected function cmdUpgrade() 127a8d2f3cbSAndreas Gohr { 128a8d2f3cbSAndreas Gohr /* @var helper_plugin_extension_extension $ext */ 129a8d2f3cbSAndreas Gohr $ext = $this->loadHelper('extension_extension'); 130a8d2f3cbSAndreas Gohr $list = $this->getInstalledExtensions(); 131a8d2f3cbSAndreas Gohr 132a8d2f3cbSAndreas Gohr $ok = 0; 133a8d2f3cbSAndreas Gohr foreach ($list as $extname) { 134a8d2f3cbSAndreas Gohr $ext->setExtension($extname); 135a8d2f3cbSAndreas Gohr $date = $ext->getInstalledVersion(); 136a8d2f3cbSAndreas Gohr $avail = $ext->getLastUpdate(); 137be15e516SAndreas Gohr if ($avail && $avail > $date && !$ext->isBundled()) { 138a8d2f3cbSAndreas Gohr $ok += $this->cmdInstall([$extname]); 139a8d2f3cbSAndreas Gohr } 140a8d2f3cbSAndreas Gohr } 141a8d2f3cbSAndreas Gohr 142a8d2f3cbSAndreas Gohr return $ok; 143a8d2f3cbSAndreas Gohr } 144a8d2f3cbSAndreas Gohr 145a8d2f3cbSAndreas Gohr /** 146a8d2f3cbSAndreas Gohr * Enable or disable one or more extensions 147a8d2f3cbSAndreas Gohr * 148a8d2f3cbSAndreas Gohr * @param bool $set 149a8d2f3cbSAndreas Gohr * @param string[] $extensions 150a8d2f3cbSAndreas Gohr * @return int 151a8d2f3cbSAndreas Gohr */ 152a8d2f3cbSAndreas Gohr protected function cmdEnable($set, $extensions) 153a8d2f3cbSAndreas Gohr { 154a8d2f3cbSAndreas Gohr /* @var helper_plugin_extension_extension $ext */ 155a8d2f3cbSAndreas Gohr $ext = $this->loadHelper('extension_extension'); 156a8d2f3cbSAndreas Gohr 157a8d2f3cbSAndreas Gohr $ok = 0; 158a8d2f3cbSAndreas Gohr foreach ($extensions as $extname) { 159a8d2f3cbSAndreas Gohr $ext->setExtension($extname); 160a8d2f3cbSAndreas Gohr if (!$ext->isInstalled()) { 161a8d2f3cbSAndreas Gohr $this->error(sprintf('Extension %s is not installed', $ext->getID())); 162fe2dcfd5SAndreas Gohr ++$ok; 163a8d2f3cbSAndreas Gohr continue; 164a8d2f3cbSAndreas Gohr } 165a8d2f3cbSAndreas Gohr 166a8d2f3cbSAndreas Gohr if ($set) { 167a8d2f3cbSAndreas Gohr $status = $ext->enable(); 168a8d2f3cbSAndreas Gohr $msg = 'msg_enabled'; 169a8d2f3cbSAndreas Gohr } else { 170a8d2f3cbSAndreas Gohr $status = $ext->disable(); 171a8d2f3cbSAndreas Gohr $msg = 'msg_disabled'; 172a8d2f3cbSAndreas Gohr } 173a8d2f3cbSAndreas Gohr 174a8d2f3cbSAndreas Gohr if ($status !== true) { 175a8d2f3cbSAndreas Gohr $this->error($status); 176fe2dcfd5SAndreas Gohr ++$ok; 177a8d2f3cbSAndreas Gohr continue; 178a8d2f3cbSAndreas Gohr } else { 179a8d2f3cbSAndreas Gohr $this->success(sprintf($this->getLang($msg), $ext->getID())); 180a8d2f3cbSAndreas Gohr } 181a8d2f3cbSAndreas Gohr } 182a8d2f3cbSAndreas Gohr 183a8d2f3cbSAndreas Gohr return $ok; 184a8d2f3cbSAndreas Gohr } 185a8d2f3cbSAndreas Gohr 186a8d2f3cbSAndreas Gohr /** 187a8d2f3cbSAndreas Gohr * Uninstall one or more extensions 188a8d2f3cbSAndreas Gohr * 189a8d2f3cbSAndreas Gohr * @param string[] $extensions 190a8d2f3cbSAndreas Gohr * @return int 191a8d2f3cbSAndreas Gohr */ 192a8d2f3cbSAndreas Gohr protected function cmdUnInstall($extensions) 193a8d2f3cbSAndreas Gohr { 194a8d2f3cbSAndreas Gohr /* @var helper_plugin_extension_extension $ext */ 195a8d2f3cbSAndreas Gohr $ext = $this->loadHelper('extension_extension'); 196a8d2f3cbSAndreas Gohr 197a8d2f3cbSAndreas Gohr $ok = 0; 198a8d2f3cbSAndreas Gohr foreach ($extensions as $extname) { 199a8d2f3cbSAndreas Gohr $ext->setExtension($extname); 200a8d2f3cbSAndreas Gohr if (!$ext->isInstalled()) { 201a8d2f3cbSAndreas Gohr $this->error(sprintf('Extension %s is not installed', $ext->getID())); 202fe2dcfd5SAndreas Gohr ++$ok; 203a8d2f3cbSAndreas Gohr continue; 204a8d2f3cbSAndreas Gohr } 205a8d2f3cbSAndreas Gohr 206a8d2f3cbSAndreas Gohr $status = $ext->uninstall(); 207a8d2f3cbSAndreas Gohr if ($status) { 208a8d2f3cbSAndreas Gohr $this->success(sprintf($this->getLang('msg_delete_success'), $ext->getID())); 209a8d2f3cbSAndreas Gohr } else { 210a8d2f3cbSAndreas Gohr $this->error(sprintf($this->getLang('msg_delete_failed'), hsc($ext->getID()))); 211a8d2f3cbSAndreas Gohr $ok = 1; 212a8d2f3cbSAndreas Gohr } 213a8d2f3cbSAndreas Gohr } 214a8d2f3cbSAndreas Gohr 215a8d2f3cbSAndreas Gohr return $ok; 216a8d2f3cbSAndreas Gohr } 217a8d2f3cbSAndreas Gohr 218a8d2f3cbSAndreas Gohr /** 219a8d2f3cbSAndreas Gohr * Install one or more extensions 220a8d2f3cbSAndreas Gohr * 221a8d2f3cbSAndreas Gohr * @param string[] $extensions 222a8d2f3cbSAndreas Gohr * @return int 223a8d2f3cbSAndreas Gohr */ 224a8d2f3cbSAndreas Gohr protected function cmdInstall($extensions) 225a8d2f3cbSAndreas Gohr { 226a8d2f3cbSAndreas Gohr 227a8d2f3cbSAndreas Gohr $ok = 0; 228a8d2f3cbSAndreas Gohr foreach ($extensions as $extname) { 2295aaea2b0SLocness $installed = []; 2305aaea2b0SLocness 231cc16762dSLocness if (preg_match("/^https?:\/\//i", $extname)) { 232cc16762dSLocness try { 233cc16762dSLocness $installed = $ext->installFromURL($extname, true); 234cc16762dSLocness } catch (Exception $e) { 235cc16762dSLocness $this->error($e->getMessage()); 236fe2dcfd5SAndreas Gohr ++$ok; 237cc16762dSLocness } 238cc16762dSLocness } else { 239a8d2f3cbSAndreas Gohr $ext->setExtension($extname); 240a8d2f3cbSAndreas Gohr 241a8d2f3cbSAndreas Gohr if (!$ext->getDownloadURL()) { 242fe2dcfd5SAndreas Gohr ++$ok; 243a8d2f3cbSAndreas Gohr $this->error( 244a8d2f3cbSAndreas Gohr sprintf('Could not find download for %s', $ext->getID()) 245a8d2f3cbSAndreas Gohr ); 246a8d2f3cbSAndreas Gohr continue; 247a8d2f3cbSAndreas Gohr } 248a8d2f3cbSAndreas Gohr 249a8d2f3cbSAndreas Gohr try { 250a8d2f3cbSAndreas Gohr $installed = $ext->installOrUpdate(); 2515aaea2b0SLocness } catch (Exception $e) { 2525aaea2b0SLocness $this->error($e->getMessage()); 253fe2dcfd5SAndreas Gohr ++$ok; 2545aaea2b0SLocness } 2555aaea2b0SLocness } 2565aaea2b0SLocness 257fe2dcfd5SAndreas Gohr foreach ($installed as $info) { 258cc16762dSLocness $this->success( 259cc16762dSLocness sprintf( 260a8d2f3cbSAndreas Gohr $this->getLang('msg_' . $info['type'] . '_' . $info['action'] . '_success'), 261cc16762dSLocness $info['base'] 262cc16762dSLocness ) 263a8d2f3cbSAndreas Gohr ); 264a8d2f3cbSAndreas Gohr } 265cc16762dSLocness } 266a8d2f3cbSAndreas Gohr return $ok; 267a8d2f3cbSAndreas Gohr } 268a8d2f3cbSAndreas Gohr 269a8d2f3cbSAndreas Gohr /** 270a8d2f3cbSAndreas Gohr * Search for an extension 271a8d2f3cbSAndreas Gohr * 272a8d2f3cbSAndreas Gohr * @param string $query 273a8d2f3cbSAndreas Gohr * @param bool $showdetails 274a8d2f3cbSAndreas Gohr * @param int $max 275a8d2f3cbSAndreas Gohr * @return int 2769b36c1fcSsplitbrain * @throws Exception 277a8d2f3cbSAndreas Gohr */ 278a8d2f3cbSAndreas Gohr protected function cmdSearch($query, $showdetails, $max) 279a8d2f3cbSAndreas Gohr { 2807c9966a5SAndreas Gohr $repo = Repository::getInstance(); 2817c9966a5SAndreas Gohr $result = $repo->searchExtensions($query); 282a8d2f3cbSAndreas Gohr if ($max) { 283a8d2f3cbSAndreas Gohr $result = array_slice($result, 0, $max); 284a8d2f3cbSAndreas Gohr } 285a8d2f3cbSAndreas Gohr 286a8d2f3cbSAndreas Gohr $this->listExtensions($result, $showdetails); 287a8d2f3cbSAndreas Gohr return 0; 288a8d2f3cbSAndreas Gohr } 289a8d2f3cbSAndreas Gohr 290a8d2f3cbSAndreas Gohr /** 291a8d2f3cbSAndreas Gohr * @param bool $showdetails 292b9daa2f5SAndreas Gohr * @param string $filter 293a8d2f3cbSAndreas Gohr * @return int 2949b36c1fcSsplitbrain * @throws Exception 295a8d2f3cbSAndreas Gohr */ 296b9daa2f5SAndreas Gohr protected function cmdList($showdetails, $filter) 297a8d2f3cbSAndreas Gohr { 2987c9966a5SAndreas Gohr $this->listExtensions((new Local())->getExtensions(), $showdetails, $filter); 299a8d2f3cbSAndreas Gohr return 0; 300a8d2f3cbSAndreas Gohr } 301a8d2f3cbSAndreas Gohr 302a8d2f3cbSAndreas Gohr /** 303a8d2f3cbSAndreas Gohr * List the given extensions 304a8d2f3cbSAndreas Gohr * 3057c9966a5SAndreas Gohr * @param Extension[] $list 306a8d2f3cbSAndreas Gohr * @param bool $details display details 307b9daa2f5SAndreas Gohr * @param string $filter filter for this status 3089b36c1fcSsplitbrain * @throws Exception 309a8d2f3cbSAndreas Gohr */ 310b9daa2f5SAndreas Gohr protected function listExtensions($list, $details, $filter = '') 311a8d2f3cbSAndreas Gohr { 312fe2dcfd5SAndreas Gohr $tr = new TableFormatter($this->colors); 3137c9966a5SAndreas Gohr foreach ($list as $ext) { 314a8d2f3cbSAndreas Gohr 315a8d2f3cbSAndreas Gohr $status = ''; 316a8d2f3cbSAndreas Gohr if ($ext->isInstalled()) { 317a8d2f3cbSAndreas Gohr $date = $ext->getInstalledVersion(); 318a8d2f3cbSAndreas Gohr $avail = $ext->getLastUpdate(); 319a8d2f3cbSAndreas Gohr $status = 'i'; 320a8d2f3cbSAndreas Gohr if ($avail && $avail > $date) { 321e5688dc7SAndreas Gohr $vcolor = Colors::C_RED; 322b9daa2f5SAndreas Gohr $status .= 'u'; 323a8d2f3cbSAndreas Gohr } else { 324e5688dc7SAndreas Gohr $vcolor = Colors::C_GREEN; 325a8d2f3cbSAndreas Gohr } 326a8d2f3cbSAndreas Gohr if ($ext->isGitControlled()) $status = 'g'; 327*99fd248dSAndreas Gohr if ($ext->isBundled()) { 328*99fd248dSAndreas Gohr $status = 'b'; 329*99fd248dSAndreas Gohr $date = '<bundled>'; 330*99fd248dSAndreas Gohr $vcolor = null; 331*99fd248dSAndreas Gohr } 332e5688dc7SAndreas Gohr if ($ext->isEnabled()) { 333e5688dc7SAndreas Gohr $ecolor = Colors::C_BROWN; 334e5688dc7SAndreas Gohr } else { 335e5688dc7SAndreas Gohr $ecolor = Colors::C_DARKGRAY; 336e5688dc7SAndreas Gohr $status .= 'd'; 337e5688dc7SAndreas Gohr } 338a8d2f3cbSAndreas Gohr } else { 339d915fa09SAndreas Gohr $ecolor = null; 340a8d2f3cbSAndreas Gohr $date = $ext->getLastUpdate(); 341e5688dc7SAndreas Gohr $vcolor = null; 342a8d2f3cbSAndreas Gohr } 343a8d2f3cbSAndreas Gohr 344b9daa2f5SAndreas Gohr if ($filter && strpos($status, $filter) === false) { 345b9daa2f5SAndreas Gohr continue; 346b9daa2f5SAndreas Gohr } 347a8d2f3cbSAndreas Gohr 348*99fd248dSAndreas Gohr 349*99fd248dSAndreas Gohr if ($ext->getSecurityIssue()) $status .= '☠'; 350*99fd248dSAndreas Gohr if ($ext->getSecurityWarning()) $status .= '⚠'; 351*99fd248dSAndreas Gohr if ($ext->getUpdateMessage()) $status .= '▽'; 352*99fd248dSAndreas Gohr 353a8d2f3cbSAndreas Gohr echo $tr->format( 354a8d2f3cbSAndreas Gohr [20, 3, 12, '*'], 355a8d2f3cbSAndreas Gohr [ 356a8d2f3cbSAndreas Gohr $ext->getID(), 357a8d2f3cbSAndreas Gohr $status, 358a8d2f3cbSAndreas Gohr $date, 359a8d2f3cbSAndreas Gohr strip_tags(sprintf( 360a8d2f3cbSAndreas Gohr $this->getLang('extensionby'), 361a8d2f3cbSAndreas Gohr $ext->getDisplayName(), 362dccd6b2bSAndreas Gohr $this->colors->wrap($ext->getAuthor(), Colors::C_PURPLE) 363dccd6b2bSAndreas Gohr )) 364a8d2f3cbSAndreas Gohr ], 365a8d2f3cbSAndreas Gohr [ 366e5688dc7SAndreas Gohr $ecolor, 367a8d2f3cbSAndreas Gohr Colors::C_YELLOW, 368e5688dc7SAndreas Gohr $vcolor, 369a8d2f3cbSAndreas Gohr null, 370a8d2f3cbSAndreas Gohr ] 371a8d2f3cbSAndreas Gohr ); 372a8d2f3cbSAndreas Gohr 373*99fd248dSAndreas Gohr 374a8d2f3cbSAndreas Gohr if (!$details) continue; 375a8d2f3cbSAndreas Gohr 376a8d2f3cbSAndreas Gohr echo $tr->format( 377a8d2f3cbSAndreas Gohr [5, '*'], 378a8d2f3cbSAndreas Gohr ['', $ext->getDescription()], 379a8d2f3cbSAndreas Gohr [null, Colors::C_CYAN] 380a8d2f3cbSAndreas Gohr ); 381*99fd248dSAndreas Gohr if ($ext->getSecurityWarning()) { 382*99fd248dSAndreas Gohr echo $tr->format( 383*99fd248dSAndreas Gohr [5, '*'], 384*99fd248dSAndreas Gohr ['', '⚠ ' . $ext->getSecurityWarning()], 385*99fd248dSAndreas Gohr [null, Colors::C_YELLOW] 386*99fd248dSAndreas Gohr ); 387*99fd248dSAndreas Gohr } 388*99fd248dSAndreas Gohr if ($ext->getSecurityIssue()) { 389*99fd248dSAndreas Gohr echo $tr->format( 390*99fd248dSAndreas Gohr [5, '*'], 391*99fd248dSAndreas Gohr ['', '☠ ' . $ext->getSecurityIssue()], 392*99fd248dSAndreas Gohr [null, Colors::C_LIGHTRED] 393*99fd248dSAndreas Gohr ); 394*99fd248dSAndreas Gohr } 395*99fd248dSAndreas Gohr if ($ext->getUpdateMessage()) { 396*99fd248dSAndreas Gohr echo $tr->format( 397*99fd248dSAndreas Gohr [5, '*'], 398*99fd248dSAndreas Gohr ['', '▽ ' . $ext->getUpdateMessage()], 399*99fd248dSAndreas Gohr [null, Colors::C_LIGHTBLUE] 400*99fd248dSAndreas Gohr ); 401*99fd248dSAndreas Gohr } 402a8d2f3cbSAndreas Gohr } 403a8d2f3cbSAndreas Gohr } 404a8d2f3cbSAndreas Gohr} 405