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