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