xref: /dokuwiki/bin/dwpage.php (revision e82b082ef3300c5b2d8f03f74873ef31f0b57933)
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();