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