Fixes and improvements to the Wiki engine.
[platal.git] / classes / plwikipage.php
CommitLineData
ccb397e5
FB
1<?php
2/***************************************************************************
3 * Copyright (C) 2003-2008 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
21
22/** This namespace groups all the methods relative to the integration
23 * of PmWiki into plat/al.
24 *
25 * pagename: A page name is NameSpace.Page and must be acceed trough
26 * NameSpace/Page.
27 */
28class PlWikiPage
29{
30 static private $permLevels = array('public' => 'Public',
31 'logged' => 'Connecté',
32 'mdp' => 'Authentifié',
33 'admin' => 'Admin');
34 static private $defaulPerms = array('logged', 'admin');
35
36 public $name;
37 public $path;
38
ec537891 39 private $content = null;
ccb397e5
FB
40 private $perms = null;
41
42 /** Build a new page from a PmWiki page name (ie NameSpace.Page).
43 */
44 public function __construct($n)
45 {
46 if (!is_utf8($n)) {
47 $n = utf8_encode($n);
48 }
49 $this->name = $n;
50 $this->path = str_replace('.', '/', $n);
51 }
52
53 /** Return the filename.
54 */
55 public function filename()
56 {
57 return self::workDir() . '/' . $this->name;
58 }
59
60 /** Return the filename for the cache file.
61 */
62 public function cacheFilename()
63 {
64 return self::workDir() . '/cache_' . $this->name . '.tpl';
65 }
66
ec537891 67 /** Fetch the content of the wiki page.
ccb397e5 68 */
ec537891 69 private function fetchContent()
ccb397e5 70 {
ec537891 71 if (!is_null($this->content)) {
ccb397e5
FB
72 return;
73 }
74 $file = $this->filename();
ec537891 75 if (!is_file($file)) {
ccb397e5
FB
76 return;
77 }
78 $lines = explode("\n", file_get_contents($file));
ec537891 79 $this->content = array();
ccb397e5
FB
80 foreach ($lines as $line) {
81 @list($k, $v) = explode('=', $line, 2);
ec537891
FB
82 $this->content[$k] = $v;
83 }
84 }
85
86 /** Save the content of the wiki page based on the
87 * fetched content.
88 */
89 private function saveContent()
90 {
91 if (is_null($this->content)) {
92 return false;
93 }
94 $lines = array();
95 foreach ($this->content as $k => $v) {
96 $lines[] = "$k=$v";
97 }
98 return file_put_contents($this->filename(), implode("\n", $lines)) !== false;
99 }
100
101 /** Get a field from the wiki content.
102 */
103 public function getField($name)
104 {
105 $this->fetchContent();
106 return @$this->content[$name];
107 }
108
109 /** Fetch the permissions.
110 */
111 private function fetchPerms()
112 {
113 if (!is_null($this->perms)) {
114 return;
115 }
116 $this->fetchContent();
117 if (isset($this->content['platal_perms'])) {
118 $this->perms = explode(':', $this->content['platal_perms']);
119 } else {
120 $this->perms = self::$defaulPerms;
ccb397e5 121 }
ccb397e5
FB
122 }
123
124 /** Return read perms.
125 */
126 public function readPerms()
127 {
128 if (is_null($this->perms)) {
129 $this->fetchPerms();
130 }
131 return $this->perms[0];
132 }
133
134 /** Check if the user can read the page.
135 */
136 public function canRead()
137 {
138 return self::havePerms($this->readPerms());
139 }
140
141 /** Return write perms.
142 */
143 public function writePerms()
144 {
145 if (is_null($this->perms)) {
146 $this->fetchPerms();
147 }
148 return $this->perms[1];
149 }
150
151 /** Check if the user can write the page.
152 */
153 public function canWrite()
154 {
155 return self::havePerms($this->writePerms());
156 }
157
158 /** Set the permission level for the page.
159 */
160 public function setPerms($read, $write)
161 {
ec537891
FB
162 $this->fetchContent();
163 if (is_null($this->content)) {
ccb397e5
FB
164 return false;
165 }
ec537891 166 $this->content['platal_perms'] = $read . ':' . $write;
ccb397e5 167 $this->perms = array($read, $write);
ec537891 168 return $this->saveContent();
ccb397e5
FB
169 }
170
171
172 /** Check permission for RSS feed.
173 */
174 public function prepareFeed()
175 {
176 if ($this->canRead()) {
177 return;
178 }
179 $uid = Platal::session()->tokenAuth(Get::v('user'), Get::v('hash'));
180 if ($this->canRead()) {
181 return;
182 }
183 exit;
184 }
185
186 /** Apply the read permissions for the current page.
187 */
188 public function applyReadPerms()
189 {
190 if ($this->canRead()) {
191 return;
192 }
193 $this->applyPerms($this->readPerms());
194 }
195
196 /** Apply the write permissions for the current page.
197 */
198 public function applyWritePerms()
199 {
200 if ($this->canWrite()) {
201 return;
202 }
203 $this->applyPerms($this->writePerms());
204 }
205
206 /** Build the cache for the page.
207 */
208 public function buildCache()
209 {
210 global $globals;
211 if (is_file($this->cacheFilename())) {
212 return;
213 }
214 system('wget --no-check-certificate ' . escapeshellarg($globals->baseurl . '/' . $this->path) . ' -O /dev/null');
215 }
216
217 /** Remove the page.
218 */
219 public function delete()
220 {
221 $file = $this->filename();
222 $cache = $this->cacheFilename();
223 if (is_file($cache)) {
224 unlink($cache);
225 }
226 if (!is_file($file)) {
227 return false;
228 }
229 unlink($file);
230 return true;
231 }
232
233 /** Return the wiki storage dir.
234 */
235 public static function workDir()
236 {
237 global $globals;
238 return $globals->spoolroot . '/spool/wiki.d';
239 }
240
241 /** Clear wiki cache.
242 */
243 public static function clearCache()
244 {
245 system('rm -f ' . self::workDir() . '/cache_*');
246 }
247
248
249 /** Search links in the a page
250 */
251 private static function findLinks($line, $groupname)
252 {
253 $links = array();
254 if (preg_match_all('@\[\[([^~][^\]\|\?#]*)((\?|#)[^\]\|]+)?(\\|[^\]]+)?\]\]@', $line, $matches, PREG_OFFSET_CAPTURE)) {
255 foreach ($matches[1] as $j => $link) if (!preg_match('@http://@', $link[0])) {
256 $mylink = str_replace('/','.',trim($link[0]));
257 $sup = trim(substr($matches[2][$j][0],1));
258 $alt = trim(substr($matches[4][$j][0],1));
259 $newlink = str_replace(' ','',ucwords($mylink));
260 if (strpos($newlink,'.') === false) {
261 $newlink = $groupname.'.'.$newlink;
262 }
263 if (!$alt && $mylink != $newlink) {
264 $alt = trim($link[0]);
265 }
266 $links[] = array(
267 'pos' => $matches[0][$j][1],
268 'size' => strlen($matches[0][$j][0]),
269 'href' => $newlink,
270 'sup' => $sup,
271 'alt' => $alt,
272 'group' => substr($mylink, 0, strpos($mylink, '.')));
273 }
274 }
275 return $links;
276 }
277
278 public function rename($newname, $changeLinks = true)
279 {
280 $newpage = new PlWikiPage(str_replace('/', '.', $newname));
ec537891
FB
281 $cache = $this->cacheFilename();
282 if (is_file($cache)) {
283 unlink($cache);
284 }
ccb397e5
FB
285
286 list($groupname, ) = explode('.', $this->name);
287 list($newgroupname, ) = explode('.', $newpage->name);
288
289 $file = $this->filename();
290 $newfile = $newpage->filename();
291 if (!is_file($file)) {
292 // old page doesn't exist
293 return false;
294 }
295 if (!rename($file, $newfile)) {
296 // impossible to renama page
297 return false;
298 }
299
300 if (!$changeLinks) {
301 $this->name = $newpage->name;
302 $this->path = $newpage->path;
303 return true;
304 }
305
306 $changedLinks = 0;
307 // change name inside this folder and ingroup links if changing group
308 $lines = explode("\n", file_get_contents($newfile));
309 $changed = false;
310 foreach ($lines as $i => $line) {
311 list($k, $v) = explode('=', $line, 2);
312 if ($k == 'name' && $v == $pagename_dots) {
313 $lines[$i] = 'name=' . $newpage->name;
314 $changed = true;
315 } else if ($groupname != $newgroupname) {
316 $links = self::findLinks($line, $groupname);
317 $newline = '';
318 $last = 0;
319 foreach ($links as $link) {
320 if ($link['group'] == $groupname) {
321 $newline .= substr($line, $last, $link['pos']);
322 $newline .= '[['.$link['href'].$link['sup'].($link['alt']?(' |'.$link['alt']):'').']]';
323 $last = $link['pos']+$link['size'];
324 $changedLinks++;
325 }
326 }
327 if ($last != 0) {
328 $newline .= substr($line, $last);
329 $lines[$i] = $newline;
330 $changed = true;
331 }
332 }
333 }
334 file_put_contents($newfile, join("\n", $lines));
335
336 // change wiki links in all wiki pages
337 $endname = substr($pagename_dots, strpos($this->name, '.') + 1);
338 $pages = array();
339 exec('grep ' . $endname . ' ' . self::workDir() . '/* -sc', $pages);
340 foreach($pages as $line) {
341 if (preg_match('%/([^/:]+):([0-9]+)$%', $line, $vals) && $vals[2] > 0) {
342 $inpage = $vals[1];
343 $lines = explode("\n", file_get_contents(self::workDir().'/'.$inpage));
344 $changed = false;
345 // find all wiki links in page and change if linked to this page
346 foreach ($lines as $i => $line) {
347 $links = self::findLinks($line, substr($inpage, 0, strpos($inpage, '.')));
348 $newline = '';
349 $last = 0;
350 foreach ($links as $link) {
351 if ($link['href'] == $pagename_dots) {
352 $newline .= substr($line, $last, $link['pos']);
353 $newline .= '[[' . $newname_dots . $link['sup'] . ($link['alt'] ? (' |' . $link['alt']) : '') . ']]';
354 $last = $link['pos'] + $link['size'];
355 $changedLinks++;
356 }
357 }
358 if ($last != 0) {
359 $newline .= substr($line, $last);
360 $lines[$i] = $newline;
361 $changed = true;
362 }
363 }
364 if ($changed) {
365 file_put_contents(self::workDir() . '/' . $inpage, join("\n", $lines));
366 }
367 }
368 }
369 if ($changedLinks > 0) {
370 return $changedLinks;
371 }
372 $this->name = $newpage->name;
373 $this->path = $newpage->path;
374 return true;
375 }
376
377 /** Return the authentication level translation table.
378 */
379 public static function permOptions()
380 {
381 return array('public' => 'Public',
382 'logged' => 'Connecté',
383 'mdp' => 'Authentifié',
384 'admin' => 'Admin');
385 }
386
387 /** Check permissions.
388 */
389 public static function havePerms($perm)
390 {
391 switch ($perm) {
392 case 'public':
393 return true;
394 case 'logged':
395 case 'mdp':
396 return S::logged();
397 case 'admin':
398 return S::has_perms();
399 default:
400 return false;
401 }
402 }
403
404 /** Apply permissions.
405 */
406 public static function applyPerms($perm)
407 {
408 switch ($perm) {
409 case 'public':
410 return;
411 case 'logged':
412 Platal::session()->start(AUTH_PUBLIC + 1);
413 return;
414 default:
415 Platal::session()->start(Platal::session()->sureLevel());
416 return;
417 }
418 }
419
420 /** Return the current page.
421 */
422 public static function currentPage()
423 {
424 if (!Get::v('n')) {
425 return null;
426 }
427
428 $words = explode('/', trim(Get::v('n'), '/'));
429 if (count($words) == 2) {
430 return new PlWikiPage(join('.', $words));
431 }
432
433 // We are on NameSpace.Page, redirect to NameSpace/Page
434 array_unshift($words, $words[0]);
435 $b = array_pop($words);
436 $a = array_pop($words);
437
438 pl_redirect($a . '/' . $b);
439 }
ec537891
FB
440
441 /** List wiki pages.
442 */
443 public static function listPages()
444 {
445 $wiki_pages = array();
446 $dir = PlWikiPage::workDir();
447 if (is_dir($dir)) {
448 if ($dh = opendir($dir)) {
449 while (($file = readdir($dh)) !== false) {
450 if ($file{0} >= 'A' && $file{0} <= 'Z') {
451 $wp = new PlWikiPage($file);
452 $wiki_pages[$file] = array('read' => $wp->readPerms(),
453 'edit' => $wp->writePerms(),
454 'cached' => is_file($wp->cacheFilename()));
455 }
456 }
457 closedir($dh);
458 }
459 }
460 return $wiki_pages;
461 }
ccb397e5
FB
462}
463
464// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
465?>