cli.php 12 KB

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