1*cbfa4829SPhy#!/usr/bin/env php 21caeb00aSHarry Fuecks<?php 3cbeaa4a0SAndreas Gohr 4cbeaa4a0SAndreas Gohruse splitbrain\phpcli\CLI; 5cbeaa4a0SAndreas Gohruse splitbrain\phpcli\Options; 6cbeaa4a0SAndreas Gohr 71caeb00aSHarry Fuecksif(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__) . '/../') . '/'); 8e82b082eSAndreas Gohrdefine('NOSESSION', 1); 9e82b082eSAndreas Gohrrequire_once(DOKU_INC . 'inc/init.php'); 101caeb00aSHarry Fuecks 11e82b082eSAndreas Gohr/** 12e82b082eSAndreas Gohr * Checkout and commit pages from the command line while maintaining the history 13e82b082eSAndreas Gohr */ 14cbeaa4a0SAndreas Gohrclass PageCLI extends CLI { 15e82b082eSAndreas Gohr 16e82b082eSAndreas Gohr protected $force = false; 17e82b082eSAndreas Gohr protected $username = ''; 18e82b082eSAndreas Gohr 19e82b082eSAndreas Gohr /** 20e82b082eSAndreas Gohr * Register options and arguments on the given $options object 21e82b082eSAndreas Gohr * 22cbeaa4a0SAndreas Gohr * @param Options $options 23e82b082eSAndreas Gohr * @return void 24e82b082eSAndreas Gohr */ 25cbeaa4a0SAndreas Gohr protected function setup(Options $options) { 26e82b082eSAndreas Gohr /* global */ 27e82b082eSAndreas Gohr $options->registerOption( 28e82b082eSAndreas Gohr 'force', 29e82b082eSAndreas Gohr 'force obtaining a lock for the page (generally bad idea)', 30e82b082eSAndreas Gohr 'f' 31e82b082eSAndreas Gohr ); 32e82b082eSAndreas Gohr $options->registerOption( 33e82b082eSAndreas Gohr 'user', 34e82b082eSAndreas Gohr 'work as this user. defaults to current CLI user', 35bf00b396SAndreas Gohr 'u', 36bf00b396SAndreas Gohr 'username' 37e82b082eSAndreas Gohr ); 38e82b082eSAndreas Gohr $options->setHelp( 39e82b082eSAndreas Gohr 'Utility to help command line Dokuwiki page editing, allow ' . 40e82b082eSAndreas Gohr 'pages to be checked out for editing then committed after changes' 41e82b082eSAndreas Gohr ); 42e82b082eSAndreas Gohr 43e82b082eSAndreas Gohr /* checkout command */ 44e82b082eSAndreas Gohr $options->registerCommand( 45e82b082eSAndreas Gohr 'checkout', 46e82b082eSAndreas Gohr 'Checks out a file from the repository, using the wiki id and obtaining ' . 47e82b082eSAndreas Gohr 'a lock for the page. ' . "\n" . 48e82b082eSAndreas Gohr 'If a working_file is specified, this is where the page is copied to. ' . 49e82b082eSAndreas Gohr 'Otherwise defaults to the same as the wiki page in the current ' . 50e82b082eSAndreas Gohr 'working directory.' 51e82b082eSAndreas Gohr ); 52e82b082eSAndreas Gohr $options->registerArgument( 53e82b082eSAndreas Gohr 'wikipage', 54e82b082eSAndreas Gohr 'The wiki page to checkout', 55e82b082eSAndreas Gohr true, 56e82b082eSAndreas Gohr 'checkout' 57e82b082eSAndreas Gohr ); 58e82b082eSAndreas Gohr $options->registerArgument( 59e82b082eSAndreas Gohr 'workingfile', 60e82b082eSAndreas Gohr 'How to name the local checkout', 61e82b082eSAndreas Gohr false, 62e82b082eSAndreas Gohr 'checkout' 63e82b082eSAndreas Gohr ); 64e82b082eSAndreas Gohr 65e82b082eSAndreas Gohr /* commit command */ 66e82b082eSAndreas Gohr $options->registerCommand( 67e82b082eSAndreas Gohr 'commit', 68e82b082eSAndreas Gohr 'Checks in the working_file into the repository using the specified ' . 69e82b082eSAndreas Gohr 'wiki id, archiving the previous version.' 70e82b082eSAndreas Gohr ); 71e82b082eSAndreas Gohr $options->registerArgument( 72e82b082eSAndreas Gohr 'workingfile', 73e82b082eSAndreas Gohr 'The local file to commit', 74e82b082eSAndreas Gohr true, 75e82b082eSAndreas Gohr 'commit' 76e82b082eSAndreas Gohr ); 77e82b082eSAndreas Gohr $options->registerArgument( 78e82b082eSAndreas Gohr 'wikipage', 79e82b082eSAndreas Gohr 'The wiki page to create or update', 80e82b082eSAndreas Gohr true, 81e82b082eSAndreas Gohr 'commit' 82e82b082eSAndreas Gohr ); 83e82b082eSAndreas Gohr $options->registerOption( 84e82b082eSAndreas Gohr 'message', 85e82b082eSAndreas Gohr 'Summary describing the change (required)', 86e82b082eSAndreas Gohr 'm', 87e82b082eSAndreas Gohr 'summary', 88e82b082eSAndreas Gohr 'commit' 89e82b082eSAndreas Gohr ); 90e82b082eSAndreas Gohr $options->registerOption( 91e82b082eSAndreas Gohr 'trivial', 92e82b082eSAndreas Gohr 'minor change', 93e82b082eSAndreas Gohr 't', 94e82b082eSAndreas Gohr false, 95e82b082eSAndreas Gohr 'commit' 96e82b082eSAndreas Gohr ); 97e82b082eSAndreas Gohr 98e82b082eSAndreas Gohr /* lock command */ 99e82b082eSAndreas Gohr $options->registerCommand( 100e82b082eSAndreas Gohr 'lock', 101e82b082eSAndreas Gohr 'Obtains or updates a lock for a wiki page' 102e82b082eSAndreas Gohr ); 103e82b082eSAndreas Gohr $options->registerArgument( 104e82b082eSAndreas Gohr 'wikipage', 105e82b082eSAndreas Gohr 'The wiki page to lock', 106e82b082eSAndreas Gohr true, 107e82b082eSAndreas Gohr 'lock' 108e82b082eSAndreas Gohr ); 109e82b082eSAndreas Gohr 110e82b082eSAndreas Gohr /* unlock command */ 111e82b082eSAndreas Gohr $options->registerCommand( 112e82b082eSAndreas Gohr 'unlock', 113e82b082eSAndreas Gohr 'Removes a lock for a wiki page.' 114e82b082eSAndreas Gohr ); 115e82b082eSAndreas Gohr $options->registerArgument( 116e82b082eSAndreas Gohr 'wikipage', 117e82b082eSAndreas Gohr 'The wiki page to unlock', 118e82b082eSAndreas Gohr true, 119e82b082eSAndreas Gohr 'unlock' 120e82b082eSAndreas Gohr ); 121e82b082eSAndreas Gohr } 122e82b082eSAndreas Gohr 123e82b082eSAndreas Gohr /** 124e82b082eSAndreas Gohr * Your main program 125e82b082eSAndreas Gohr * 126e82b082eSAndreas Gohr * Arguments and options have been parsed when this is run 127e82b082eSAndreas Gohr * 128cbeaa4a0SAndreas Gohr * @param Options $options 129e82b082eSAndreas Gohr * @return void 130e82b082eSAndreas Gohr */ 131cbeaa4a0SAndreas Gohr protected function main(Options $options) { 132e82b082eSAndreas Gohr $this->force = $options->getOpt('force', false); 133e82b082eSAndreas Gohr $this->username = $options->getOpt('user', $this->getUser()); 134e82b082eSAndreas Gohr 135e82b082eSAndreas Gohr $command = $options->getCmd(); 136cbeaa4a0SAndreas Gohr $args = $options->getArgs(); 137e82b082eSAndreas Gohr switch($command) { 1381caeb00aSHarry Fuecks case 'checkout': 139cbeaa4a0SAndreas Gohr $wiki_id = array_shift($args); 140cbeaa4a0SAndreas Gohr $localfile = array_shift($args); 141e82b082eSAndreas Gohr $this->commandCheckout($wiki_id, $localfile); 1421caeb00aSHarry Fuecks break; 1431caeb00aSHarry Fuecks case 'commit': 144cbeaa4a0SAndreas Gohr $localfile = array_shift($args); 145cbeaa4a0SAndreas Gohr $wiki_id = array_shift($args); 146e82b082eSAndreas Gohr $this->commandCommit( 147e82b082eSAndreas Gohr $localfile, 148e82b082eSAndreas Gohr $wiki_id, 149e82b082eSAndreas Gohr $options->getOpt('message', ''), 150e82b082eSAndreas Gohr $options->getOpt('trivial', false) 151e82b082eSAndreas Gohr ); 1521caeb00aSHarry Fuecks break; 1531caeb00aSHarry Fuecks case 'lock': 154cbeaa4a0SAndreas Gohr $wiki_id = array_shift($args); 155e82b082eSAndreas Gohr $this->obtainLock($wiki_id); 156e82b082eSAndreas Gohr $this->success("$wiki_id locked"); 1571caeb00aSHarry Fuecks break; 1581caeb00aSHarry Fuecks case 'unlock': 159cbeaa4a0SAndreas Gohr $wiki_id = array_shift($args); 160e82b082eSAndreas Gohr $this->clearLock($wiki_id); 161e82b082eSAndreas Gohr $this->success("$wiki_id unlocked"); 1621caeb00aSHarry Fuecks break; 1631caeb00aSHarry Fuecks default: 164e82b082eSAndreas Gohr echo $options->help(); 1651caeb00aSHarry Fuecks } 1661caeb00aSHarry Fuecks } 1671caeb00aSHarry Fuecks 168e82b082eSAndreas Gohr /** 169e82b082eSAndreas Gohr * Check out a file 170e82b082eSAndreas Gohr * 171e82b082eSAndreas Gohr * @param string $wiki_id 172e82b082eSAndreas Gohr * @param string $localfile 173e82b082eSAndreas Gohr */ 174e82b082eSAndreas Gohr protected function commandCheckout($wiki_id, $localfile) { 175e82b082eSAndreas Gohr global $conf; 176e82b082eSAndreas Gohr 177e82b082eSAndreas Gohr $wiki_id = cleanID($wiki_id); 178e82b082eSAndreas Gohr $wiki_fn = wikiFN($wiki_id); 179e82b082eSAndreas Gohr 180e82b082eSAndreas Gohr if(!file_exists($wiki_fn)) { 181e82b082eSAndreas Gohr $this->fatal("$wiki_id does not yet exist"); 182e82b082eSAndreas Gohr } 183e82b082eSAndreas Gohr 184e82b082eSAndreas Gohr if(empty($localfile)) { 1858cbc5ee8SAndreas Gohr $localfile = getcwd() . '/' . \dokuwiki\Utf8\PhpString::basename($wiki_fn); 186e82b082eSAndreas Gohr } 187e82b082eSAndreas Gohr 188e82b082eSAndreas Gohr if(!file_exists(dirname($localfile))) { 189e82b082eSAndreas Gohr $this->fatal("Directory " . dirname($localfile) . " does not exist"); 190e82b082eSAndreas Gohr } 191e82b082eSAndreas Gohr 192e82b082eSAndreas Gohr if(stristr(realpath(dirname($localfile)), realpath($conf['datadir'])) !== false) { 193e82b082eSAndreas Gohr $this->fatal("Attempt to check out file into data directory - not allowed"); 194e82b082eSAndreas Gohr } 195e82b082eSAndreas Gohr 196e82b082eSAndreas Gohr $this->obtainLock($wiki_id); 197e82b082eSAndreas Gohr 198e82b082eSAndreas Gohr if(!copy($wiki_fn, $localfile)) { 199e82b082eSAndreas Gohr $this->clearLock($wiki_id); 200e82b082eSAndreas Gohr $this->fatal("Unable to copy $wiki_fn to $localfile"); 201e82b082eSAndreas Gohr } 202e82b082eSAndreas Gohr 203e82b082eSAndreas Gohr $this->success("$wiki_id > $localfile"); 204e82b082eSAndreas Gohr } 205e82b082eSAndreas Gohr 206e82b082eSAndreas Gohr /** 207e82b082eSAndreas Gohr * Save a file as a new page revision 208e82b082eSAndreas Gohr * 209e82b082eSAndreas Gohr * @param string $localfile 210e82b082eSAndreas Gohr * @param string $wiki_id 211e82b082eSAndreas Gohr * @param string $message 212e82b082eSAndreas Gohr * @param bool $minor 213e82b082eSAndreas Gohr */ 214e82b082eSAndreas Gohr protected function commandCommit($localfile, $wiki_id, $message, $minor) { 215e82b082eSAndreas Gohr $wiki_id = cleanID($wiki_id); 216e82b082eSAndreas Gohr $message = trim($message); 217e82b082eSAndreas Gohr 218e82b082eSAndreas Gohr if(!file_exists($localfile)) { 219e82b082eSAndreas Gohr $this->fatal("$localfile does not exist"); 220e82b082eSAndreas Gohr } 221e82b082eSAndreas Gohr 222e82b082eSAndreas Gohr if(!is_readable($localfile)) { 223e82b082eSAndreas Gohr $this->fatal("Cannot read from $localfile"); 224e82b082eSAndreas Gohr } 225e82b082eSAndreas Gohr 226e82b082eSAndreas Gohr if(!$message) { 227e82b082eSAndreas Gohr $this->fatal("Summary message required"); 228e82b082eSAndreas Gohr } 229e82b082eSAndreas Gohr 230e82b082eSAndreas Gohr $this->obtainLock($wiki_id); 231e82b082eSAndreas Gohr 232e82b082eSAndreas Gohr saveWikiText($wiki_id, file_get_contents($localfile), $message, $minor); 233e82b082eSAndreas Gohr 234e82b082eSAndreas Gohr $this->clearLock($wiki_id); 235e82b082eSAndreas Gohr 236e82b082eSAndreas Gohr $this->success("$localfile > $wiki_id"); 237e82b082eSAndreas Gohr } 238e82b082eSAndreas Gohr 239e82b082eSAndreas Gohr /** 240e82b082eSAndreas Gohr * Lock the given page or exit 241e82b082eSAndreas Gohr * 242e82b082eSAndreas Gohr * @param string $wiki_id 243e82b082eSAndreas Gohr */ 244e82b082eSAndreas Gohr protected function obtainLock($wiki_id) { 245e82b082eSAndreas Gohr if($this->force) $this->deleteLock($wiki_id); 246e82b082eSAndreas Gohr 247e82b082eSAndreas Gohr $_SERVER['REMOTE_USER'] = $this->username; 248bf00b396SAndreas Gohr 249e82b082eSAndreas Gohr if(checklock($wiki_id)) { 250e82b082eSAndreas Gohr $this->error("Page $wiki_id is already locked by another user"); 251e82b082eSAndreas Gohr exit(1); 252e82b082eSAndreas Gohr } 253e82b082eSAndreas Gohr 254e82b082eSAndreas Gohr lock($wiki_id); 255e82b082eSAndreas Gohr 256cb62a664SJustinTrouble if(checklock($wiki_id)) { 257e82b082eSAndreas Gohr $this->error("Unable to obtain lock for $wiki_id "); 258e82b082eSAndreas Gohr var_dump(checklock($wiki_id)); 259e82b082eSAndreas Gohr exit(1); 260e82b082eSAndreas Gohr } 261e82b082eSAndreas Gohr } 262e82b082eSAndreas Gohr 263e82b082eSAndreas Gohr /** 264e82b082eSAndreas Gohr * Clear the lock on the given page 265e82b082eSAndreas Gohr * 266e82b082eSAndreas Gohr * @param string $wiki_id 267e82b082eSAndreas Gohr */ 268e82b082eSAndreas Gohr protected function clearLock($wiki_id) { 269e82b082eSAndreas Gohr if($this->force) $this->deleteLock($wiki_id); 270e82b082eSAndreas Gohr 271e82b082eSAndreas Gohr $_SERVER['REMOTE_USER'] = $this->username; 272e82b082eSAndreas Gohr if(checklock($wiki_id)) { 273e82b082eSAndreas Gohr $this->error("Page $wiki_id is locked by another user"); 274e82b082eSAndreas Gohr exit(1); 275e82b082eSAndreas Gohr } 276e82b082eSAndreas Gohr 277e82b082eSAndreas Gohr unlock($wiki_id); 278e82b082eSAndreas Gohr 279e82b082eSAndreas Gohr if(file_exists(wikiLockFN($wiki_id))) { 280e82b082eSAndreas Gohr $this->error("Unable to clear lock for $wiki_id"); 281e82b082eSAndreas Gohr exit(1); 282e82b082eSAndreas Gohr } 283e82b082eSAndreas Gohr } 284e82b082eSAndreas Gohr 285e82b082eSAndreas Gohr /** 286e82b082eSAndreas Gohr * Forcefully remove a lock on the page given 287e82b082eSAndreas Gohr * 288e82b082eSAndreas Gohr * @param string $wiki_id 289e82b082eSAndreas Gohr */ 290e82b082eSAndreas Gohr protected function deleteLock($wiki_id) { 291e82b082eSAndreas Gohr $wikiLockFN = wikiLockFN($wiki_id); 292e82b082eSAndreas Gohr 293e82b082eSAndreas Gohr if(file_exists($wikiLockFN)) { 294e82b082eSAndreas Gohr if(!unlink($wikiLockFN)) { 295e82b082eSAndreas Gohr $this->error("Unable to delete $wikiLockFN"); 296e82b082eSAndreas Gohr exit(1); 297e82b082eSAndreas Gohr } 298e82b082eSAndreas Gohr } 299e82b082eSAndreas Gohr } 300e82b082eSAndreas Gohr 301e82b082eSAndreas Gohr /** 302e82b082eSAndreas Gohr * Get the current user's username from the environment 303e82b082eSAndreas Gohr * 304e82b082eSAndreas Gohr * @return string 305e82b082eSAndreas Gohr */ 306e82b082eSAndreas Gohr protected function getUser() { 3071caeb00aSHarry Fuecks $user = getenv('USER'); 308feec2ab4SAndreas Gohr if(empty ($user)) { 3091caeb00aSHarry Fuecks $user = getenv('USERNAME'); 3101caeb00aSHarry Fuecks } else { 3111caeb00aSHarry Fuecks return $user; 3121caeb00aSHarry Fuecks } 313feec2ab4SAndreas Gohr if(empty ($user)) { 3141caeb00aSHarry Fuecks $user = 'admin'; 3151caeb00aSHarry Fuecks } 3161caeb00aSHarry Fuecks return $user; 3171caeb00aSHarry Fuecks } 3181caeb00aSHarry Fuecks} 3191caeb00aSHarry Fuecks 320e82b082eSAndreas Gohr// Main 321e82b082eSAndreas Gohr$cli = new PageCLI(); 322e82b082eSAndreas Gohr$cli->run(); 323