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