Fix check.
[platal.git] / classes / plwikipage.php
1 <?php
2 /***************************************************************************
3 * Copyright (C) 2003-2010 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 */
28 class 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
39 private $content = null;
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
67 /** Remove the cache for the current page.
68 */
69 public function removePageCache()
70 {
71 @unlink($this->cacheFilename());
72 global $globals;
73 $page = glob($globals->spoolroot . '/spool/templates_c/*cache_' . $this->name . '.tpl.php');
74 if (count($page) > 0) {
75 @unlink($page[0]);
76 }
77 }
78
79 /** Fetch the content of the wiki page.
80 */
81 private function fetchContent()
82 {
83 if (!is_null($this->content)) {
84 return;
85 }
86 $file = $this->filename();
87 if (!is_file($file)) {
88 return;
89 }
90 $lines = explode("\n", file_get_contents($file));
91 $this->content = array();
92 foreach ($lines as $line) {
93 @list($k, $v) = explode('=', $line, 2);
94 $this->content[$k] = $v;
95 }
96 }
97
98 /** Save the content of the wiki page based on the
99 * fetched content.
100 */
101 private function saveContent()
102 {
103 if (is_null($this->content)) {
104 return false;
105 }
106 $lines = array();
107 foreach ($this->content as $k => $v) {
108 $lines[] = "$k=$v";
109 }
110 return file_put_contents($this->filename(), implode("\n", $lines)) !== false;
111 }
112
113 /** Get a field from the wiki content.
114 */
115 public function getField($name)
116 {
117 $this->fetchContent();
118 return @$this->content[$name];
119 }
120
121 /** Fetch the permissions.
122 */
123 private function fetchPerms()
124 {
125 if (!is_null($this->perms)) {
126 return;
127 }
128 $this->fetchContent();
129 if (isset($this->content['platal_perms'])) {
130 $this->perms = explode(':', $this->content['platal_perms']);
131 } else {
132 $this->perms = self::$defaulPerms;
133 }
134 }
135
136 /** Return read perms.
137 */
138 public function readPerms()
139 {
140 if (is_null($this->perms)) {
141 $this->fetchPerms();
142 }
143 return $this->perms[0];
144 }
145
146 /** Check if the user can read the page.
147 */
148 public function canRead()
149 {
150 return self::havePerms($this->readPerms());
151 }
152
153 /** Return write perms.
154 */
155 public function writePerms()
156 {
157 if (is_null($this->perms)) {
158 $this->fetchPerms();
159 }
160 return $this->perms[1];
161 }
162
163 /** Check if the user can write the page.
164 */
165 public function canWrite()
166 {
167 return self::havePerms($this->writePerms());
168 }
169
170 /** Set the permission level for the page.
171 */
172 public function setPerms($read, $write)
173 {
174 $this->fetchContent();
175 if (is_null($this->content)) {
176 return false;
177 }
178 $this->content['platal_perms'] = $read . ':' . $write;
179 $this->perms = array($read, $write);
180 return $this->saveContent();
181 }
182
183
184 /** Check permission for RSS feed.
185 */
186 public function prepareFeed()
187 {
188 if ($this->canRead()) {
189 return;
190 }
191 $uid = Platal::session()->tokenAuth(Get::v('user'), Get::v('hash'));
192 if ($this->canRead()) {
193 return;
194 }
195 exit;
196 }
197
198 /** Apply the read permissions for the current page.
199 */
200 public function applyReadPerms()
201 {
202 if ($this->canRead()) {
203 return;
204 }
205 $this->applyPerms($this->readPerms());
206 }
207
208 /** Apply the write permissions for the current page.
209 */
210 public function applyWritePerms()
211 {
212 if ($this->canWrite()) {
213 return;
214 }
215 $this->applyPerms($this->writePerms());
216 }
217
218 /** Build the cache for the page.
219 */
220 public function buildCache()
221 {
222 global $globals;
223 if (is_file($this->cacheFilename())) {
224 return;
225 }
226 system('wget --no-check-certificate ' . escapeshellarg($globals->baseurl . '/' . $this->path) . ' -O /dev/null');
227 }
228
229 /** Remove the page.
230 */
231 public function delete()
232 {
233 $file = $this->filename();
234 $cache = $this->cacheFilename();
235 if (is_file($cache)) {
236 unlink($cache);
237 }
238 if (!is_file($file)) {
239 return false;
240 }
241 unlink($file);
242 return true;
243 }
244
245 /** Return the wiki storage dir.
246 */
247 public static function workDir()
248 {
249 global $globals;
250 return $globals->spoolroot . '/spool/wiki.d';
251 }
252
253 /** Clear wiki cache.
254 */
255 public static function clearCache()
256 {
257 system('rm -f ' . self::workDir() . '/cache_*');
258 }
259
260
261 /** Search links in the a page
262 */
263 private static function findLinks($line, $groupname)
264 {
265 $links = array();
266 if (preg_match_all('@\[\[([^~][^\]\|\?#]*)((\?|#)[^\]\|]+)?(\\|[^\]]+)?\]\]@', $line, $matches, PREG_OFFSET_CAPTURE)) {
267 foreach ($matches[1] as $j => $link) if (!preg_match('@http://@', $link[0])) {
268 $mylink = str_replace('/','.',trim($link[0]));
269 $sup = trim(substr($matches[2][$j][0],1));
270 $alt = trim(substr($matches[4][$j][0],1));
271 $newlink = str_replace(' ','',ucwords($mylink));
272 if (strpos($newlink,'.') === false) {
273 $newlink = $groupname.'.'.$newlink;
274 }
275 if (!$alt && $mylink != $newlink) {
276 $alt = trim($link[0]);
277 }
278 $links[] = array(
279 'pos' => $matches[0][$j][1],
280 'size' => strlen($matches[0][$j][0]),
281 'href' => $newlink,
282 'sup' => $sup,
283 'alt' => $alt,
284 'group' => substr($mylink, 0, strpos($mylink, '.')));
285 }
286 }
287 return $links;
288 }
289
290 public function rename($newname, $changeLinks = true)
291 {
292 $newpage = new PlWikiPage(str_replace('/', '.', $newname));
293 $cache = $this->cacheFilename();
294 if (is_file($cache)) {
295 unlink($cache);
296 }
297
298 list($groupname, ) = explode('.', $this->name);
299 list($newgroupname, ) = explode('.', $newpage->name);
300
301 $file = $this->filename();
302 $newfile = $newpage->filename();
303 if (!is_file($file)) {
304 // old page doesn't exist
305 return false;
306 }
307 if (!rename($file, $newfile)) {
308 // impossible to renama page
309 return false;
310 }
311
312 if (!$changeLinks) {
313 $this->name = $newpage->name;
314 $this->path = $newpage->path;
315 return true;
316 }
317
318 $changedLinks = 0;
319 // change name inside this folder and ingroup links if changing group
320 $lines = explode("\n", file_get_contents($newfile));
321 $changed = false;
322 foreach ($lines as $i => $line) {
323 list($k, $v) = explode('=', $line, 2);
324 if ($k == 'name' && $v == $pagename_dots) {
325 $lines[$i] = 'name=' . $newpage->name;
326 $changed = true;
327 } else if ($groupname != $newgroupname) {
328 $links = self::findLinks($line, $groupname);
329 $newline = '';
330 $last = 0;
331 foreach ($links as $link) {
332 if ($link['group'] == $groupname) {
333 $newline .= substr($line, $last, $link['pos']);
334 $newline .= '[['.$link['href'].$link['sup'].($link['alt']?(' |'.$link['alt']):'').']]';
335 $last = $link['pos']+$link['size'];
336 $changedLinks++;
337 }
338 }
339 if ($last != 0) {
340 $newline .= substr($line, $last);
341 $lines[$i] = $newline;
342 $changed = true;
343 }
344 }
345 }
346 file_put_contents($newfile, join("\n", $lines));
347
348 // change wiki links in all wiki pages
349 $endname = substr($pagename_dots, strpos($this->name, '.') + 1);
350 $pages = array();
351 exec('grep ' . $endname . ' ' . self::workDir() . '/* -sc', $pages);
352 foreach($pages as $line) {
353 if (preg_match('%/([^/:]+):([0-9]+)$%', $line, $vals) && $vals[2] > 0) {
354 $inpage = $vals[1];
355 $lines = explode("\n", file_get_contents(self::workDir().'/'.$inpage));
356 $changed = false;
357 // find all wiki links in page and change if linked to this page
358 foreach ($lines as $i => $line) {
359 $links = self::findLinks($line, substr($inpage, 0, strpos($inpage, '.')));
360 $newline = '';
361 $last = 0;
362 foreach ($links as $link) {
363 if ($link['href'] == $pagename_dots) {
364 $newline .= substr($line, $last, $link['pos']);
365 $newline .= '[[' . $newname_dots . $link['sup'] . ($link['alt'] ? (' |' . $link['alt']) : '') . ']]';
366 $last = $link['pos'] + $link['size'];
367 $changedLinks++;
368 }
369 }
370 if ($last != 0) {
371 $newline .= substr($line, $last);
372 $lines[$i] = $newline;
373 $changed = true;
374 }
375 }
376 if ($changed) {
377 file_put_contents(self::workDir() . '/' . $inpage, join("\n", $lines));
378 }
379 }
380 }
381 if ($changedLinks > 0) {
382 return $changedLinks;
383 }
384 $this->name = $newpage->name;
385 $this->path = $newpage->path;
386 return true;
387 }
388
389 /** Return the authentication level translation table.
390 */
391 public static function permOptions()
392 {
393 return array('public' => 'Public',
394 'logged' => 'Connecté',
395 'mdp' => 'Authentifié',
396 'admin' => 'Admin');
397 }
398
399 /** Check permissions.
400 */
401 public static function havePerms($perm)
402 {
403 switch ($perm) {
404 case 'public':
405 return true;
406 case 'logged':
407 return S::logged();
408 case 'mdp':
409 return S::identified();
410 case 'admin':
411 return S::admin();
412 default:
413 return false;
414 }
415 }
416
417 /** Apply permissions.
418 */
419 public static function applyPerms($perm)
420 {
421 switch ($perm) {
422 case 'public':
423 return;
424 case 'logged':
425 $ok = Platal::session()->start(AUTH_PUBLIC + 1);
426 break;
427 default:
428 $ok = Platal::session()->start(Platal::session()->sureLevel());
429 break;
430 }
431 if (!$ok) {
432 global $platal;
433 $page =& Platal::page();
434 $platal->force_login($page);
435 } else if ($perm == 'admin' && !S::admin()) {
436 global $platal;
437 $platal->error403();
438 }
439 }
440
441 /** Return the current page.
442 */
443 public static function currentPage()
444 {
445 if (!Get::v('n')) {
446 return null;
447 }
448
449 $words = explode('/', trim(Get::v('n'), '/'));
450 if (count($words) == 2) {
451 return new PlWikiPage(join('.', $words));
452 }
453
454 // We are on NameSpace.Page, redirect to NameSpace/Page
455 array_unshift($words, $words[0]);
456 $b = array_pop($words);
457 $a = array_pop($words);
458
459 pl_redirect($a . '/' . $b);
460 }
461
462 /** List wiki pages.
463 */
464 public static function listPages()
465 {
466 $wiki_pages = array();
467 $dir = PlWikiPage::workDir();
468 if (is_dir($dir)) {
469 if ($dh = opendir($dir)) {
470 while (($file = readdir($dh)) !== false) {
471 if ($file{0} >= 'A' && $file{0} <= 'Z') {
472 $wp = new PlWikiPage($file);
473 $wiki_pages[$file] = array('read' => $wp->readPerms(),
474 'edit' => $wp->writePerms(),
475 'cached' => is_file($wp->cacheFilename()));
476 }
477 }
478 closedir($dh);
479 }
480 }
481 return $wiki_pages;
482 }
483 }
484
485 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
486 ?>