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