summaryrefslogtreecommitdiff
path: root/platform/www/bin
diff options
context:
space:
mode:
Diffstat (limited to 'platform/www/bin')
-rw-r--r--platform/www/bin/.htaccess7
-rwxr-xr-xplatform/www/bin/dwpage.php322
-rwxr-xr-xplatform/www/bin/gittool.php340
-rwxr-xr-xplatform/www/bin/indexer.php107
-rwxr-xr-xplatform/www/bin/plugin.php103
-rwxr-xr-xplatform/www/bin/render.php64
-rwxr-xr-xplatform/www/bin/striplangs.php114
-rwxr-xr-xplatform/www/bin/wantedpages.php186
8 files changed, 1243 insertions, 0 deletions
diff --git a/platform/www/bin/.htaccess b/platform/www/bin/.htaccess
new file mode 100644
index 0000000..7625314
--- /dev/null
+++ b/platform/www/bin/.htaccess
@@ -0,0 +1,7 @@
+<IfModule mod_authz_core.c>
+ Require all denied
+</IfModule>
+<IfModule !mod_authz_core.c>
+ Order allow,deny
+ Deny from all
+</IfModule>
diff --git a/platform/www/bin/dwpage.php b/platform/www/bin/dwpage.php
new file mode 100755
index 0000000..dee8039
--- /dev/null
+++ b/platform/www/bin/dwpage.php
@@ -0,0 +1,322 @@
+#!/usr/bin/env php
+<?php
+
+use splitbrain\phpcli\CLI;
+use splitbrain\phpcli\Options;
+
+if(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__) . '/../') . '/');
+define('NOSESSION', 1);
+require_once(DOKU_INC . 'inc/init.php');
+
+/**
+ * Checkout and commit pages from the command line while maintaining the history
+ */
+class PageCLI extends CLI {
+
+ protected $force = false;
+ protected $username = '';
+
+ /**
+ * Register options and arguments on the given $options object
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function setup(Options $options) {
+ /* global */
+ $options->registerOption(
+ 'force',
+ 'force obtaining a lock for the page (generally bad idea)',
+ 'f'
+ );
+ $options->registerOption(
+ 'user',
+ 'work as this user. defaults to current CLI user',
+ 'u',
+ 'username'
+ );
+ $options->setHelp(
+ 'Utility to help command line Dokuwiki page editing, allow ' .
+ 'pages to be checked out for editing then committed after changes'
+ );
+
+ /* checkout command */
+ $options->registerCommand(
+ 'checkout',
+ 'Checks out a file from the repository, using the wiki id and obtaining ' .
+ 'a lock for the page. ' . "\n" .
+ 'If a working_file is specified, this is where the page is copied to. ' .
+ 'Otherwise defaults to the same as the wiki page in the current ' .
+ 'working directory.'
+ );
+ $options->registerArgument(
+ 'wikipage',
+ 'The wiki page to checkout',
+ true,
+ 'checkout'
+ );
+ $options->registerArgument(
+ 'workingfile',
+ 'How to name the local checkout',
+ false,
+ 'checkout'
+ );
+
+ /* commit command */
+ $options->registerCommand(
+ 'commit',
+ 'Checks in the working_file into the repository using the specified ' .
+ 'wiki id, archiving the previous version.'
+ );
+ $options->registerArgument(
+ 'workingfile',
+ 'The local file to commit',
+ true,
+ 'commit'
+ );
+ $options->registerArgument(
+ 'wikipage',
+ 'The wiki page to create or update',
+ true,
+ 'commit'
+ );
+ $options->registerOption(
+ 'message',
+ 'Summary describing the change (required)',
+ 'm',
+ 'summary',
+ 'commit'
+ );
+ $options->registerOption(
+ 'trivial',
+ 'minor change',
+ 't',
+ false,
+ 'commit'
+ );
+
+ /* lock command */
+ $options->registerCommand(
+ 'lock',
+ 'Obtains or updates a lock for a wiki page'
+ );
+ $options->registerArgument(
+ 'wikipage',
+ 'The wiki page to lock',
+ true,
+ 'lock'
+ );
+
+ /* unlock command */
+ $options->registerCommand(
+ 'unlock',
+ 'Removes a lock for a wiki page.'
+ );
+ $options->registerArgument(
+ 'wikipage',
+ 'The wiki page to unlock',
+ true,
+ 'unlock'
+ );
+ }
+
+ /**
+ * Your main program
+ *
+ * Arguments and options have been parsed when this is run
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function main(Options $options) {
+ $this->force = $options->getOpt('force', false);
+ $this->username = $options->getOpt('user', $this->getUser());
+
+ $command = $options->getCmd();
+ $args = $options->getArgs();
+ switch($command) {
+ case 'checkout':
+ $wiki_id = array_shift($args);
+ $localfile = array_shift($args);
+ $this->commandCheckout($wiki_id, $localfile);
+ break;
+ case 'commit':
+ $localfile = array_shift($args);
+ $wiki_id = array_shift($args);
+ $this->commandCommit(
+ $localfile,
+ $wiki_id,
+ $options->getOpt('message', ''),
+ $options->getOpt('trivial', false)
+ );
+ break;
+ case 'lock':
+ $wiki_id = array_shift($args);
+ $this->obtainLock($wiki_id);
+ $this->success("$wiki_id locked");
+ break;
+ case 'unlock':
+ $wiki_id = array_shift($args);
+ $this->clearLock($wiki_id);
+ $this->success("$wiki_id unlocked");
+ break;
+ default:
+ echo $options->help();
+ }
+ }
+
+ /**
+ * Check out a file
+ *
+ * @param string $wiki_id
+ * @param string $localfile
+ */
+ protected function commandCheckout($wiki_id, $localfile) {
+ global $conf;
+
+ $wiki_id = cleanID($wiki_id);
+ $wiki_fn = wikiFN($wiki_id);
+
+ if(!file_exists($wiki_fn)) {
+ $this->fatal("$wiki_id does not yet exist");
+ }
+
+ if(empty($localfile)) {
+ $localfile = getcwd() . '/' . \dokuwiki\Utf8\PhpString::basename($wiki_fn);
+ }
+
+ if(!file_exists(dirname($localfile))) {
+ $this->fatal("Directory " . dirname($localfile) . " does not exist");
+ }
+
+ if(stristr(realpath(dirname($localfile)), realpath($conf['datadir'])) !== false) {
+ $this->fatal("Attempt to check out file into data directory - not allowed");
+ }
+
+ $this->obtainLock($wiki_id);
+
+ if(!copy($wiki_fn, $localfile)) {
+ $this->clearLock($wiki_id);
+ $this->fatal("Unable to copy $wiki_fn to $localfile");
+ }
+
+ $this->success("$wiki_id > $localfile");
+ }
+
+ /**
+ * Save a file as a new page revision
+ *
+ * @param string $localfile
+ * @param string $wiki_id
+ * @param string $message
+ * @param bool $minor
+ */
+ protected function commandCommit($localfile, $wiki_id, $message, $minor) {
+ $wiki_id = cleanID($wiki_id);
+ $message = trim($message);
+
+ if(!file_exists($localfile)) {
+ $this->fatal("$localfile does not exist");
+ }
+
+ if(!is_readable($localfile)) {
+ $this->fatal("Cannot read from $localfile");
+ }
+
+ if(!$message) {
+ $this->fatal("Summary message required");
+ }
+
+ $this->obtainLock($wiki_id);
+
+ saveWikiText($wiki_id, file_get_contents($localfile), $message, $minor);
+
+ $this->clearLock($wiki_id);
+
+ $this->success("$localfile > $wiki_id");
+ }
+
+ /**
+ * Lock the given page or exit
+ *
+ * @param string $wiki_id
+ */
+ protected function obtainLock($wiki_id) {
+ if($this->force) $this->deleteLock($wiki_id);
+
+ $_SERVER['REMOTE_USER'] = $this->username;
+
+ if(checklock($wiki_id)) {
+ $this->error("Page $wiki_id is already locked by another user");
+ exit(1);
+ }
+
+ lock($wiki_id);
+
+ if(checklock($wiki_id)) {
+ $this->error("Unable to obtain lock for $wiki_id ");
+ var_dump(checklock($wiki_id));
+ exit(1);
+ }
+ }
+
+ /**
+ * Clear the lock on the given page
+ *
+ * @param string $wiki_id
+ */
+ protected function clearLock($wiki_id) {
+ if($this->force) $this->deleteLock($wiki_id);
+
+ $_SERVER['REMOTE_USER'] = $this->username;
+ if(checklock($wiki_id)) {
+ $this->error("Page $wiki_id is locked by another user");
+ exit(1);
+ }
+
+ unlock($wiki_id);
+
+ if(file_exists(wikiLockFN($wiki_id))) {
+ $this->error("Unable to clear lock for $wiki_id");
+ exit(1);
+ }
+ }
+
+ /**
+ * Forcefully remove a lock on the page given
+ *
+ * @param string $wiki_id
+ */
+ protected function deleteLock($wiki_id) {
+ $wikiLockFN = wikiLockFN($wiki_id);
+
+ if(file_exists($wikiLockFN)) {
+ if(!unlink($wikiLockFN)) {
+ $this->error("Unable to delete $wikiLockFN");
+ exit(1);
+ }
+ }
+ }
+
+ /**
+ * Get the current user's username from the environment
+ *
+ * @return string
+ */
+ protected function getUser() {
+ $user = getenv('USER');
+ if(empty ($user)) {
+ $user = getenv('USERNAME');
+ } else {
+ return $user;
+ }
+ if(empty ($user)) {
+ $user = 'admin';
+ }
+ return $user;
+ }
+}
+
+// Main
+$cli = new PageCLI();
+$cli->run();
diff --git a/platform/www/bin/gittool.php b/platform/www/bin/gittool.php
new file mode 100755
index 0000000..5ebd0c5
--- /dev/null
+++ b/platform/www/bin/gittool.php
@@ -0,0 +1,340 @@
+#!/usr/bin/env php
+<?php
+
+use splitbrain\phpcli\CLI;
+use splitbrain\phpcli\Options;
+
+if(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__) . '/../') . '/');
+define('NOSESSION', 1);
+require_once(DOKU_INC . 'inc/init.php');
+
+/**
+ * Easily manage DokuWiki git repositories
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+class GitToolCLI extends CLI {
+
+ /**
+ * Register options and arguments on the given $options object
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function setup(Options $options) {
+ $options->setHelp(
+ "Manage git repositories for DokuWiki and its plugins and templates.\n\n" .
+ "$> ./bin/gittool.php clone gallery template:ach\n" .
+ "$> ./bin/gittool.php repos\n" .
+ "$> ./bin/gittool.php origin -v"
+ );
+
+ $options->registerArgument(
+ 'command',
+ 'Command to execute. See below',
+ true
+ );
+
+ $options->registerCommand(
+ 'clone',
+ 'Tries to install a known plugin or template (prefix with template:) via git. Uses the DokuWiki.org ' .
+ 'plugin repository to find the proper git repository. Multiple extensions can be given as parameters'
+ );
+ $options->registerArgument(
+ 'extension',
+ 'name of the extension to install, prefix with \'template:\' for templates',
+ true,
+ 'clone'
+ );
+
+ $options->registerCommand(
+ 'install',
+ 'The same as clone, but when no git source repository can be found, the extension is installed via ' .
+ 'download'
+ );
+ $options->registerArgument(
+ 'extension',
+ 'name of the extension to install, prefix with \'template:\' for templates',
+ true,
+ 'install'
+ );
+
+ $options->registerCommand(
+ 'repos',
+ 'Lists all git repositories found in this DokuWiki installation'
+ );
+
+ $options->registerCommand(
+ '*',
+ 'Any unknown commands are assumed to be arguments to git and will be executed in all repositories ' .
+ 'found within this DokuWiki installation'
+ );
+ }
+
+ /**
+ * Your main program
+ *
+ * Arguments and options have been parsed when this is run
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function main(Options $options) {
+ $command = $options->getCmd();
+ $args = $options->getArgs();
+ if(!$command) $command = array_shift($args);
+
+ switch($command) {
+ case '':
+ echo $options->help();
+ break;
+ case 'clone':
+ $this->cmdClone($args);
+ break;
+ case 'install':
+ $this->cmdInstall($args);
+ break;
+ case 'repo':
+ case 'repos':
+ $this->cmdRepos();
+ break;
+ default:
+ $this->cmdGit($command, $args);
+ }
+ }
+
+ /**
+ * Tries to install the given extensions using git clone
+ *
+ * @param array $extensions
+ */
+ public function cmdClone($extensions) {
+ $errors = array();
+ $succeeded = array();
+
+ foreach($extensions as $ext) {
+ $repo = $this->getSourceRepo($ext);
+
+ if(!$repo) {
+ $this->error("could not find a repository for $ext");
+ $errors[] = $ext;
+ } else {
+ if($this->cloneExtension($ext, $repo)) {
+ $succeeded[] = $ext;
+ } else {
+ $errors[] = $ext;
+ }
+ }
+ }
+
+ echo "\n";
+ if($succeeded) $this->success('successfully cloned the following extensions: ' . join(', ', $succeeded));
+ if($errors) $this->error('failed to clone the following extensions: ' . join(', ', $errors));
+ }
+
+ /**
+ * Tries to install the given extensions using git clone with fallback to install
+ *
+ * @param array $extensions
+ */
+ public function cmdInstall($extensions) {
+ $errors = array();
+ $succeeded = array();
+
+ foreach($extensions as $ext) {
+ $repo = $this->getSourceRepo($ext);
+
+ if(!$repo) {
+ $this->info("could not find a repository for $ext");
+ if($this->downloadExtension($ext)) {
+ $succeeded[] = $ext;
+ } else {
+ $errors[] = $ext;
+ }
+ } else {
+ if($this->cloneExtension($ext, $repo)) {
+ $succeeded[] = $ext;
+ } else {
+ $errors[] = $ext;
+ }
+ }
+ }
+
+ echo "\n";
+ if($succeeded) $this->success('successfully installed the following extensions: ' . join(', ', $succeeded));
+ if($errors) $this->error('failed to install the following extensions: ' . join(', ', $errors));
+ }
+
+ /**
+ * Executes the given git command in every repository
+ *
+ * @param $cmd
+ * @param $arg
+ */
+ public function cmdGit($cmd, $arg) {
+ $repos = $this->findRepos();
+
+ $shell = array_merge(array('git', $cmd), $arg);
+ $shell = array_map('escapeshellarg', $shell);
+ $shell = join(' ', $shell);
+
+ foreach($repos as $repo) {
+ if(!@chdir($repo)) {
+ $this->error("Could not change into $repo");
+ continue;
+ }
+
+ $this->info("executing $shell in $repo");
+ $ret = 0;
+ system($shell, $ret);
+
+ if($ret == 0) {
+ $this->success("git succeeded in $repo");
+ } else {
+ $this->error("git failed in $repo");
+ }
+ }
+ }
+
+ /**
+ * Simply lists the repositories
+ */
+ public function cmdRepos() {
+ $repos = $this->findRepos();
+ foreach($repos as $repo) {
+ echo "$repo\n";
+ }
+ }
+
+ /**
+ * Install extension from the given download URL
+ *
+ * @param string $ext
+ * @return bool|null
+ */
+ private function downloadExtension($ext) {
+ /** @var helper_plugin_extension_extension $plugin */
+ $plugin = plugin_load('helper', 'extension_extension');
+ if(!$ext) die("extension plugin not available, can't continue");
+
+ $plugin->setExtension($ext);
+
+ $url = $plugin->getDownloadURL();
+ if(!$url) {
+ $this->error("no download URL for $ext");
+ return false;
+ }
+
+ $ok = false;
+ try {
+ $this->info("installing $ext via download from $url");
+ $ok = $plugin->installFromURL($url);
+ } catch(Exception $e) {
+ $this->error($e->getMessage());
+ }
+
+ if($ok) {
+ $this->success("installed $ext via download");
+ return true;
+ } else {
+ $this->success("failed to install $ext via download");
+ return false;
+ }
+ }
+
+ /**
+ * Clones the extension from the given repository
+ *
+ * @param string $ext
+ * @param string $repo
+ * @return bool
+ */
+ private function cloneExtension($ext, $repo) {
+ if(substr($ext, 0, 9) == 'template:') {
+ $target = fullpath(tpl_incdir() . '../' . substr($ext, 9));
+ } else {
+ $target = DOKU_PLUGIN . $ext;
+ }
+
+ $this->info("cloning $ext from $repo to $target");
+ $ret = 0;
+ system("git clone $repo $target", $ret);
+ if($ret === 0) {
+ $this->success("cloning of $ext succeeded");
+ return true;
+ } else {
+ $this->error("cloning of $ext failed");
+ return false;
+ }
+ }
+
+ /**
+ * Returns all git repositories in this DokuWiki install
+ *
+ * Looks in root, template and plugin directories only.
+ *
+ * @return array
+ */
+ private function findRepos() {
+ $this->info('Looking for .git directories');
+ $data = array_merge(
+ glob(DOKU_INC . '.git', GLOB_ONLYDIR),
+ glob(DOKU_PLUGIN . '*/.git', GLOB_ONLYDIR),
+ glob(fullpath(tpl_incdir() . '../') . '/*/.git', GLOB_ONLYDIR)
+ );
+
+ if(!$data) {
+ $this->error('Found no .git directories');
+ } else {
+ $this->success('Found ' . count($data) . ' .git directories');
+ }
+ $data = array_map('fullpath', array_map('dirname', $data));
+ return $data;
+ }
+
+ /**
+ * Returns the repository for the given extension
+ *
+ * @param $extension
+ * @return false|string
+ */
+ private function getSourceRepo($extension) {
+ /** @var helper_plugin_extension_extension $ext */
+ $ext = plugin_load('helper', 'extension_extension');
+ if(!$ext) die("extension plugin not available, can't continue");
+
+ $ext->setExtension($extension);
+
+ $repourl = $ext->getSourcerepoURL();
+ if(!$repourl) return false;
+
+ // match github repos
+ if(preg_match('/github\.com\/([^\/]+)\/([^\/]+)/i', $repourl, $m)) {
+ $user = $m[1];
+ $repo = $m[2];
+ return 'https://github.com/' . $user . '/' . $repo . '.git';
+ }
+
+ // match gitorious repos
+ if(preg_match('/gitorious.org\/([^\/]+)\/([^\/]+)?/i', $repourl, $m)) {
+ $user = $m[1];
+ $repo = $m[2];
+ if(!$repo) $repo = $user;
+
+ return 'https://git.gitorious.org/' . $user . '/' . $repo . '.git';
+ }
+
+ // match bitbucket repos - most people seem to use mercurial there though
+ if(preg_match('/bitbucket\.org\/([^\/]+)\/([^\/]+)/i', $repourl, $m)) {
+ $user = $m[1];
+ $repo = $m[2];
+ return 'https://bitbucket.org/' . $user . '/' . $repo . '.git';
+ }
+
+ return false;
+ }
+}
+
+// Main
+$cli = new GitToolCLI();
+$cli->run();
diff --git a/platform/www/bin/indexer.php b/platform/www/bin/indexer.php
new file mode 100755
index 0000000..c8c8361
--- /dev/null
+++ b/platform/www/bin/indexer.php
@@ -0,0 +1,107 @@
+#!/usr/bin/env php
+<?php
+
+use splitbrain\phpcli\CLI;
+use splitbrain\phpcli\Options;
+
+if(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__) . '/../') . '/');
+define('NOSESSION', 1);
+require_once(DOKU_INC . 'inc/init.php');
+
+/**
+ * Update the Search Index from command line
+ */
+class IndexerCLI extends CLI {
+
+ private $quiet = false;
+ private $clear = false;
+
+ /**
+ * Register options and arguments on the given $options object
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function setup(Options $options) {
+ $options->setHelp(
+ 'Updates the searchindex by indexing all new or changed pages. When the -c option is ' .
+ 'given the index is cleared first.'
+ );
+
+ $options->registerOption(
+ 'clear',
+ 'clear the index before updating',
+ 'c'
+ );
+ $options->registerOption(
+ 'quiet',
+ 'don\'t produce any output',
+ 'q'
+ );
+ }
+
+ /**
+ * Your main program
+ *
+ * Arguments and options have been parsed when this is run
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function main(Options $options) {
+ $this->clear = $options->getOpt('clear');
+ $this->quiet = $options->getOpt('quiet');
+
+ if($this->clear) $this->clearindex();
+
+ $this->update();
+ }
+
+ /**
+ * Update the index
+ */
+ protected function update() {
+ global $conf;
+ $data = array();
+ $this->quietecho("Searching pages... ");
+ search($data, $conf['datadir'], 'search_allpages', array('skipacl' => true));
+ $this->quietecho(count($data) . " pages found.\n");
+
+ foreach($data as $val) {
+ $this->index($val['id']);
+ }
+ }
+
+ /**
+ * Index the given page
+ *
+ * @param string $id
+ */
+ protected function index($id) {
+ $this->quietecho("$id... ");
+ idx_addPage($id, !$this->quiet, $this->clear);
+ $this->quietecho("done.\n");
+ }
+
+ /**
+ * Clear all index files
+ */
+ protected function clearindex() {
+ $this->quietecho("Clearing index... ");
+ idx_get_indexer()->clear();
+ $this->quietecho("done.\n");
+ }
+
+ /**
+ * Print message if not supressed
+ *
+ * @param string $msg
+ */
+ protected function quietecho($msg) {
+ if(!$this->quiet) echo $msg;
+ }
+}
+
+// Main
+$cli = new IndexerCLI();
+$cli->run();
diff --git a/platform/www/bin/plugin.php b/platform/www/bin/plugin.php
new file mode 100755
index 0000000..84a800e
--- /dev/null
+++ b/platform/www/bin/plugin.php
@@ -0,0 +1,103 @@
+#!/usr/bin/env php
+<?php
+
+use dokuwiki\Extension\PluginController;
+use splitbrain\phpcli\CLI;
+use splitbrain\phpcli\Colors;
+use splitbrain\phpcli\Options;
+
+if(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__) . '/../') . '/');
+define('NOSESSION', 1);
+require_once(DOKU_INC . 'inc/init.php');
+
+class PluginCLI extends CLI {
+
+ /**
+ * Register options and arguments on the given $options object
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function setup(Options $options) {
+ $options->setHelp('Excecutes Plugin command line tools');
+ $options->registerArgument('plugin', 'The plugin CLI you want to run. Leave off to see list', false);
+ }
+
+ /**
+ * Your main program
+ *
+ * Arguments and options have been parsed when this is run
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function main(Options $options) {
+ global $argv;
+ $argv = $options->getArgs();
+
+ if($argv) {
+ $plugin = $this->loadPlugin($argv[0]);
+ if($plugin !== null) {
+ $plugin->run();
+ } else {
+ $this->fatal('Command {cmd} not found.', ['cmd' => $argv[0]]);
+ }
+ } else {
+ echo $options->help();
+ $this->listPlugins();
+ }
+ }
+
+ /**
+ * List available plugins
+ */
+ protected function listPlugins() {
+ /** @var PluginController $plugin_controller */
+ global $plugin_controller;
+
+ echo "\n";
+ echo "\n";
+ echo $this->colors->wrap('AVAILABLE PLUGINS:', Colors::C_BROWN);
+ echo "\n";
+
+ $list = $plugin_controller->getList('cli');
+ sort($list);
+ if(!count($list)) {
+ echo $this->colors->wrap(" No plugins providing CLI components available\n", Colors::C_RED);
+ } else {
+ $tf = new \splitbrain\phpcli\TableFormatter($this->colors);
+
+ foreach($list as $name) {
+ $plugin = $this->loadPlugin($name);
+ if($plugin === null) continue;
+ $info = $plugin->getInfo();
+
+ echo $tf->format(
+ [2, '30%', '*'],
+ ['', $name, $info['desc']],
+ ['', Colors::C_CYAN, '']
+
+ );
+ }
+ }
+ }
+
+ /**
+ * Instantiate a CLI plugin
+ *
+ * @param string $name
+ * @return \dokuwiki\Extension\CLIPlugin|null
+ */
+ protected function loadPlugin($name) {
+ // execute the plugin CLI
+ $class = "cli_plugin_$name";
+ if(class_exists($class)) {
+ return new $class();
+ }
+ return null;
+ }
+}
+
+// Main
+$cli = new PluginCLI();
+$cli->run();
diff --git a/platform/www/bin/render.php b/platform/www/bin/render.php
new file mode 100755
index 0000000..6076951
--- /dev/null
+++ b/platform/www/bin/render.php
@@ -0,0 +1,64 @@
+#!/usr/bin/env php
+<?php
+
+use splitbrain\phpcli\CLI;
+use splitbrain\phpcli\Options;
+
+if(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__) . '/../') . '/');
+define('NOSESSION', 1);
+require_once(DOKU_INC . 'inc/init.php');
+
+/**
+ * A simple commandline tool to render some DokuWiki syntax with a given
+ * renderer.
+ *
+ * This may not work for plugins that expect a certain environment to be
+ * set up before rendering, but should work for most or even all standard
+ * DokuWiki markup
+ *
+ * @license GPL2
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+class RenderCLI extends CLI {
+
+ /**
+ * Register options and arguments on the given $options object
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function setup(Options $options) {
+ $options->setHelp(
+ 'A simple commandline tool to render some DokuWiki syntax with a given renderer.' .
+ "\n\n" .
+ 'This may not work for plugins that expect a certain environment to be ' .
+ 'set up before rendering, but should work for most or even all standard ' .
+ 'DokuWiki markup'
+ );
+ $options->registerOption('renderer', 'The renderer mode to use. Defaults to xhtml', 'r', 'mode');
+ }
+
+ /**
+ * Your main program
+ *
+ * Arguments and options have been parsed when this is run
+ *
+ * @param Options $options
+ * @throws DokuCLI_Exception
+ * @return void
+ */
+ protected function main(Options $options) {
+ $renderer = $options->getOpt('renderer', 'xhtml');
+
+ // do the action
+ $source = stream_get_contents(STDIN);
+ $info = array();
+ $result = p_render($renderer, p_get_instructions($source), $info);
+ if(is_null($result)) throw new DokuCLI_Exception("No such renderer $renderer");
+ echo $result;
+ }
+}
+
+// Main
+$cli = new RenderCLI();
+$cli->run();
diff --git a/platform/www/bin/striplangs.php b/platform/www/bin/striplangs.php
new file mode 100755
index 0000000..91805d5
--- /dev/null
+++ b/platform/www/bin/striplangs.php
@@ -0,0 +1,114 @@
+#!/usr/bin/env php
+<?php
+
+use splitbrain\phpcli\CLI;
+use splitbrain\phpcli\Options;
+
+if(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__) . '/../') . '/');
+define('NOSESSION', 1);
+require_once(DOKU_INC . 'inc/init.php');
+
+/**
+ * Remove unwanted languages from a DokuWiki install
+ */
+class StripLangsCLI extends CLI {
+
+ /**
+ * Register options and arguments on the given $options object
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function setup(Options $options) {
+
+ $options->setHelp(
+ 'Remove all languages from the installation, besides the ones specified. English language ' .
+ 'is never removed!'
+ );
+
+ $options->registerOption(
+ 'keep',
+ 'Comma separated list of languages to keep in addition to English.',
+ 'k',
+ 'langcodes'
+ );
+ $options->registerOption(
+ 'english-only',
+ 'Remove all languages except English',
+ 'e'
+ );
+ }
+
+ /**
+ * Your main program
+ *
+ * Arguments and options have been parsed when this is run
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function main(Options $options) {
+ if($options->getOpt('keep')) {
+ $keep = explode(',', $options->getOpt('keep'));
+ if(!in_array('en', $keep)) $keep[] = 'en';
+ } elseif($options->getOpt('english-only')) {
+ $keep = array('en');
+ } else {
+ echo $options->help();
+ exit(0);
+ }
+
+ // Kill all language directories in /inc/lang and /lib/plugins besides those in $langs array
+ $this->stripDirLangs(realpath(dirname(__FILE__) . '/../inc/lang'), $keep);
+ $this->processExtensions(realpath(dirname(__FILE__) . '/../lib/plugins'), $keep);
+ $this->processExtensions(realpath(dirname(__FILE__) . '/../lib/tpl'), $keep);
+ }
+
+ /**
+ * Strip languages from extensions
+ *
+ * @param string $path path to plugin or template dir
+ * @param array $keep_langs languages to keep
+ */
+ protected function processExtensions($path, $keep_langs) {
+ if(is_dir($path)) {
+ $entries = scandir($path);
+
+ foreach($entries as $entry) {
+ if($entry != "." && $entry != "..") {
+ if(is_dir($path . '/' . $entry)) {
+
+ $plugin_langs = $path . '/' . $entry . '/lang';
+
+ if(is_dir($plugin_langs)) {
+ $this->stripDirLangs($plugin_langs, $keep_langs);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Strip languages from path
+ *
+ * @param string $path path to lang dir
+ * @param array $keep_langs languages to keep
+ */
+ protected function stripDirLangs($path, $keep_langs) {
+ $dir = dir($path);
+
+ while(($cur_dir = $dir->read()) !== false) {
+ if($cur_dir != '.' and $cur_dir != '..' and is_dir($path . '/' . $cur_dir)) {
+
+ if(!in_array($cur_dir, $keep_langs, true)) {
+ io_rmdir($path . '/' . $cur_dir, true);
+ }
+ }
+ }
+ $dir->close();
+ }
+}
+
+$cli = new StripLangsCLI();
+$cli->run();
diff --git a/platform/www/bin/wantedpages.php b/platform/www/bin/wantedpages.php
new file mode 100755
index 0000000..6b3f787
--- /dev/null
+++ b/platform/www/bin/wantedpages.php
@@ -0,0 +1,186 @@
+#!/usr/bin/env php
+<?php
+
+use splitbrain\phpcli\CLI;
+use splitbrain\phpcli\Options;
+
+if(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__) . '/../') . '/');
+define('NOSESSION', 1);
+require_once(DOKU_INC . 'inc/init.php');
+
+/**
+ * Find wanted pages
+ */
+class WantedPagesCLI extends CLI {
+
+ const DIR_CONTINUE = 1;
+ const DIR_NS = 2;
+ const DIR_PAGE = 3;
+
+ private $skip = false;
+ private $sort = 'wanted';
+
+ private $result = array();
+
+ /**
+ * Register options and arguments on the given $options object
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function setup(Options $options) {
+ $options->setHelp(
+ 'Outputs a list of wanted pages (pages that do not exist yet) and their origin pages ' .
+ ' (the pages that are linkin to these missing pages).'
+ );
+ $options->registerArgument(
+ 'namespace',
+ 'The namespace to lookup. Defaults to root namespace',
+ false
+ );
+
+ $options->registerOption(
+ 'sort',
+ 'Sort by wanted or origin page',
+ 's',
+ '(wanted|origin)'
+ );
+
+ $options->registerOption(
+ 'skip',
+ 'Do not show the second dimension',
+ 'k'
+ );
+ }
+
+ /**
+ * Your main program
+ *
+ * Arguments and options have been parsed when this is run
+ *
+ * @param Options $options
+ * @return void
+ */
+ protected function main(Options $options) {
+ $args = $options->getArgs();
+ if($args) {
+ $startdir = dirname(wikiFN($args[0] . ':xxx'));
+ } else {
+ $startdir = dirname(wikiFN('xxx'));
+ }
+
+ $this->skip = $options->getOpt('skip');
+ $this->sort = $options->getOpt('sort');
+
+ $this->info("searching $startdir");
+
+ foreach($this->getPages($startdir) as $page) {
+ $this->internalLinks($page);
+ }
+ ksort($this->result);
+ foreach($this->result as $main => $subs) {
+ if($this->skip) {
+ print "$main\n";
+ } else {
+ $subs = array_unique($subs);
+ sort($subs);
+ foreach($subs as $sub) {
+ printf("%-40s %s\n", $main, $sub);
+ }
+ }
+ }
+ }
+
+ /**
+ * Determine directions of the search loop
+ *
+ * @param string $entry
+ * @param string $basepath
+ * @return int
+ */
+ protected function dirFilter($entry, $basepath) {
+ if($entry == '.' || $entry == '..') {
+ return WantedPagesCLI::DIR_CONTINUE;
+ }
+ if(is_dir($basepath . '/' . $entry)) {
+ if(strpos($entry, '_') === 0) {
+ return WantedPagesCLI::DIR_CONTINUE;
+ }
+ return WantedPagesCLI::DIR_NS;
+ }
+ if(preg_match('/\.txt$/', $entry)) {
+ return WantedPagesCLI::DIR_PAGE;
+ }
+ return WantedPagesCLI::DIR_CONTINUE;
+ }
+
+ /**
+ * Collects recursively the pages in a namespace
+ *
+ * @param string $dir
+ * @return array
+ * @throws DokuCLI_Exception
+ */
+ protected function getPages($dir) {
+ static $trunclen = null;
+ if(!$trunclen) {
+ global $conf;
+ $trunclen = strlen($conf['datadir'] . ':');
+ }
+
+ if(!is_dir($dir)) {
+ throw new DokuCLI_Exception("Unable to read directory $dir");
+ }
+
+ $pages = array();
+ $dh = opendir($dir);
+ while(false !== ($entry = readdir($dh))) {
+ $status = $this->dirFilter($entry, $dir);
+ if($status == WantedPagesCLI::DIR_CONTINUE) {
+ continue;
+ } else if($status == WantedPagesCLI::DIR_NS) {
+ $pages = array_merge($pages, $this->getPages($dir . '/' . $entry));
+ } else {
+ $page = array(
+ 'id' => pathID(substr($dir . '/' . $entry, $trunclen)),
+ 'file' => $dir . '/' . $entry,
+ );
+ $pages[] = $page;
+ }
+ }
+ closedir($dh);
+ return $pages;
+ }
+
+ /**
+ * Parse instructions and add the non-existing links to the result array
+ *
+ * @param array $page array with page id and file path
+ */
+ protected function internalLinks($page) {
+ global $conf;
+ $instructions = p_get_instructions(file_get_contents($page['file']));
+ $cns = getNS($page['id']);
+ $exists = false;
+ $pid = $page['id'];
+ foreach($instructions as $ins) {
+ if($ins[0] == 'internallink' || ($conf['camelcase'] && $ins[0] == 'camelcaselink')) {
+ $mid = $ins[1][0];
+ resolve_pageid($cns, $mid, $exists);
+ if(!$exists) {
+ list($mid) = explode('#', $mid); //record pages without hashes
+
+ if($this->sort == 'origin') {
+ $this->result[$pid][] = $mid;
+ } else {
+ $this->result[$mid][] = $pid;
+ }
+ }
+ }
+ }
+ }
+}
+
+// Main
+$cli = new WantedPagesCLI();
+$cli->run();