Merge pull request #4078 from dokuwiki-translate/lang_update_713_1696375517
[dokuwiki.git] / bin / dwpage.php
bloba343cb0ed6eea87c031d5ea2ce17d31959c03db8
1 #!/usr/bin/env php
2 <?php
4 use splitbrain\phpcli\CLI;
5 use splitbrain\phpcli\Options;
6 use dokuwiki\Utf8\PhpString;
8 if (!defined('DOKU_INC')) define('DOKU_INC', realpath(__DIR__ . '/../') . '/');
9 define('NOSESSION', 1);
10 require_once(DOKU_INC . 'inc/init.php');
12 /**
13 * Checkout and commit pages from the command line while maintaining the history
15 class PageCLI extends CLI
17 protected $force = false;
18 protected $username = '';
20 /**
21 * Register options and arguments on the given $options object
23 * @param Options $options
24 * @return void
26 protected function setup(Options $options)
28 /* global */
29 $options->registerOption(
30 'force',
31 'force obtaining a lock for the page (generally bad idea)',
32 'f'
34 $options->registerOption(
35 'user',
36 'work as this user. defaults to current CLI user',
37 'u',
38 'username'
40 $options->setHelp(
41 'Utility to help command line Dokuwiki page editing, allow ' .
42 'pages to be checked out for editing then committed after changes'
45 /* checkout command */
46 $options->registerCommand(
47 'checkout',
48 'Checks out a file from the repository, using the wiki id and obtaining ' .
49 'a lock for the page. ' . "\n" .
50 'If a working_file is specified, this is where the page is copied to. ' .
51 'Otherwise defaults to the same as the wiki page in the current ' .
52 'working directory.'
54 $options->registerArgument(
55 'wikipage',
56 'The wiki page to checkout',
57 true,
58 'checkout'
60 $options->registerArgument(
61 'workingfile',
62 'How to name the local checkout',
63 false,
64 'checkout'
67 /* commit command */
68 $options->registerCommand(
69 'commit',
70 'Checks in the working_file into the repository using the specified ' .
71 'wiki id, archiving the previous version.'
73 $options->registerArgument(
74 'workingfile',
75 'The local file to commit',
76 true,
77 'commit'
79 $options->registerArgument(
80 'wikipage',
81 'The wiki page to create or update',
82 true,
83 'commit'
85 $options->registerOption(
86 'message',
87 'Summary describing the change (required)',
88 'm',
89 'summary',
90 'commit'
92 $options->registerOption(
93 'trivial',
94 'minor change',
95 't',
96 false,
97 'commit'
100 /* lock command */
101 $options->registerCommand(
102 'lock',
103 'Obtains or updates a lock for a wiki page'
105 $options->registerArgument(
106 'wikipage',
107 'The wiki page to lock',
108 true,
109 'lock'
112 /* unlock command */
113 $options->registerCommand(
114 'unlock',
115 'Removes a lock for a wiki page.'
117 $options->registerArgument(
118 'wikipage',
119 'The wiki page to unlock',
120 true,
121 'unlock'
124 /* gmeta command */
125 $options->registerCommand(
126 'getmeta',
127 'Prints metadata value for a page to stdout.'
129 $options->registerArgument(
130 'wikipage',
131 'The wiki page to get the metadata for',
132 true,
133 'getmeta'
135 $options->registerArgument(
136 'key',
137 'The name of the metadata item to be retrieved.' . "\n" .
138 'If empty, an array of all the metadata items is returned.' . "\n" .
139 'For retrieving items that are stored in sub-arrays, separate the ' .
140 'keys of the different levels by spaces, in quotes, eg "date modified".',
141 false,
142 'getmeta'
147 * Your main program
149 * Arguments and options have been parsed when this is run
151 * @param Options $options
152 * @return void
154 protected function main(Options $options)
156 $this->force = $options->getOpt('force', false);
157 $this->username = $options->getOpt('user', $this->getUser());
159 $command = $options->getCmd();
160 $args = $options->getArgs();
161 switch ($command) {
162 case 'checkout':
163 $wiki_id = array_shift($args);
164 $localfile = array_shift($args);
165 $this->commandCheckout($wiki_id, $localfile);
166 break;
167 case 'commit':
168 $localfile = array_shift($args);
169 $wiki_id = array_shift($args);
170 $this->commandCommit(
171 $localfile,
172 $wiki_id,
173 $options->getOpt('message', ''),
174 $options->getOpt('trivial', false)
176 break;
177 case 'lock':
178 $wiki_id = array_shift($args);
179 $this->obtainLock($wiki_id);
180 $this->success("$wiki_id locked");
181 break;
182 case 'unlock':
183 $wiki_id = array_shift($args);
184 $this->clearLock($wiki_id);
185 $this->success("$wiki_id unlocked");
186 break;
187 case 'getmeta':
188 $wiki_id = array_shift($args);
189 $key = trim(array_shift($args));
190 $meta = p_get_metadata($wiki_id, $key, METADATA_RENDER_UNLIMITED);
191 echo trim(json_encode($meta, JSON_PRETTY_PRINT));
192 echo "\n";
193 break;
194 default:
195 echo $options->help();
200 * Check out a file
202 * @param string $wiki_id
203 * @param string $localfile
205 protected function commandCheckout($wiki_id, $localfile)
207 global $conf;
209 $wiki_id = cleanID($wiki_id);
210 $wiki_fn = wikiFN($wiki_id);
212 if (!file_exists($wiki_fn)) {
213 $this->fatal("$wiki_id does not yet exist");
216 if (empty($localfile)) {
217 $localfile = getcwd() . '/' . PhpString::basename($wiki_fn);
220 if (!file_exists(dirname($localfile))) {
221 $this->fatal("Directory " . dirname($localfile) . " does not exist");
224 if (stristr(realpath(dirname($localfile)), (string) realpath($conf['datadir'])) !== false) {
225 $this->fatal("Attempt to check out file into data directory - not allowed");
228 $this->obtainLock($wiki_id);
230 if (!copy($wiki_fn, $localfile)) {
231 $this->clearLock($wiki_id);
232 $this->fatal("Unable to copy $wiki_fn to $localfile");
235 $this->success("$wiki_id > $localfile");
239 * Save a file as a new page revision
241 * @param string $localfile
242 * @param string $wiki_id
243 * @param string $message
244 * @param bool $minor
246 protected function commandCommit($localfile, $wiki_id, $message, $minor)
248 $wiki_id = cleanID($wiki_id);
249 $message = trim($message);
251 if (!file_exists($localfile)) {
252 $this->fatal("$localfile does not exist");
255 if (!is_readable($localfile)) {
256 $this->fatal("Cannot read from $localfile");
259 if (!$message) {
260 $this->fatal("Summary message required");
263 $this->obtainLock($wiki_id);
265 saveWikiText($wiki_id, file_get_contents($localfile), $message, $minor);
267 $this->clearLock($wiki_id);
269 $this->success("$localfile > $wiki_id");
273 * Lock the given page or exit
275 * @param string $wiki_id
277 protected function obtainLock($wiki_id)
279 if ($this->force) $this->deleteLock($wiki_id);
281 $_SERVER['REMOTE_USER'] = $this->username;
283 if (checklock($wiki_id)) {
284 $this->error("Page $wiki_id is already locked by another user");
285 exit(1);
288 lock($wiki_id);
290 if (checklock($wiki_id)) {
291 $this->error("Unable to obtain lock for $wiki_id ");
292 var_dump(checklock($wiki_id));
293 exit(1);
298 * Clear the lock on the given page
300 * @param string $wiki_id
302 protected function clearLock($wiki_id)
304 if ($this->force) $this->deleteLock($wiki_id);
306 $_SERVER['REMOTE_USER'] = $this->username;
307 if (checklock($wiki_id)) {
308 $this->error("Page $wiki_id is locked by another user");
309 exit(1);
312 unlock($wiki_id);
314 if (file_exists(wikiLockFN($wiki_id))) {
315 $this->error("Unable to clear lock for $wiki_id");
316 exit(1);
321 * Forcefully remove a lock on the page given
323 * @param string $wiki_id
325 protected function deleteLock($wiki_id)
327 $wikiLockFN = wikiLockFN($wiki_id);
329 if (file_exists($wikiLockFN)) {
330 if (!unlink($wikiLockFN)) {
331 $this->error("Unable to delete $wikiLockFN");
332 exit(1);
338 * Get the current user's username from the environment
340 * @return string
342 protected function getUser()
344 $user = getenv('USER');
345 if (empty($user)) {
346 $user = getenv('USERNAME');
347 } else {
348 return $user;
350 if (empty($user)) {
351 $user = 'admin';
353 return $user;
357 // Main
358 $cli = new PageCLI();
359 $cli->run();