Subversion Repositories Applications.wikini

Rev

Rev 45 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
45 mathias 1
<?php
2
/*
3
$Id: wakka.php 864 2007-11-28 12:44:52Z nepote $
4
Copyright (c) 2002, Hendrik Mans <hendrik@mans.de>
5
Copyright 2003 Carlo Zottmann
6
Copyright 2002, 2003, 2005 David DELON
7
Copyright 2002, 2003, 2004, 2006 Charles N?POTE
8
Copyright 2002, 2003 Patrick PAUL
9
Copyright 2003 Eric DELORD
10
Copyright 2003 Eric FELDSTEIN
11
Copyright 2004-2006 Jean-Christophe ANDR?
12
Copyright 2005-2006 Didier LOISEAU
13
All rights reserved.
14
Redistribution and use in source and binary forms, with or without
15
modification, are permitted provided that the following conditions
16
are met:
17
1. Redistributions of source code must retain the above copyright
18
notice, this list of conditions and the following disclaimer.
19
2. Redistributions in binary form must reproduce the above copyright
20
notice, this list of conditions and the following disclaimer in the
21
documentation and/or other materials provided with the distribution.
22
3. The name of the author may not be used to endorse or promote products
23
derived from this software without specific prior written permission.
24
 
25
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
 */
36
 
37
/*
38
	Yes, most of the formatting used in this file is HORRIBLY BAD STYLE. However,
39
	most of the action happens outside of this file, and I really wanted the code
40
	to look as small as what it does. Basically. Oh, I just suck. :)
41
 */
42
 
43
 
44
 
45
// do not change this line, you fool. In fact, don't change anything! Ever!
46
define("WAKKA_VERSION", "0.1.1");
47
define("WIKINI_VERSION", "0.5.0");
48
require 'includes/constants.php';
49
include 'includes/urlutils.inc.php';
50
 
51
// start the compute time
52
list($g_usec, $g_sec) = explode(" ",microtime());
53
define ("t_start", (float)$g_usec + (float)$g_sec);
54
$t_SQL=0;
55
 
56
 
57
 
58
class Wiki
59
{
60
	var $dblink;
61
	var $page;
62
	var $tag;
63
	var $parameter = array();
64
	var $queryLog = array();
65
	var $interWiki = array();
66
	var $VERSION;
67
	var $CookiePath = '/';
68
	var $inclusions = array();
69
	/**
70
	 * an array containing all the actions that are implemented by an object
71
	 * @access private
72
	 */
73
	var $actionObjects;
74
 
75
	// LinkTrackink
76
	var $isTrackingLinks = false;
77
	var $linktable = array();
78
 
79
	var $pageCache = array();
80
	var $_groupsCache = array();
81
	var $_actionsAclsCache = array();
82
 
83
	// constructor
84
	function Wiki($config)
85
	{
86
		$this->config = $config;
87
		// some host do not allow mysql_pconnect
88
		$this->dblink = @mysql_connect (
89
			$this->config["mysql_host"],
90
			$this->config["mysql_user"],
91
			$this->config["mysql_password"]);
92
		if ($this->dblink)
93
		{
94
			if (!@mysql_select_db($this->config["mysql_database"], $this->dblink))
95
			{
96
				@mysql_close($this->dblink);
97
				$this->dblink = false;
98
			}
99
		}
100
		$this->VERSION = WAKKA_VERSION;
101
 
102
		// determine le chemin pour les cookies
103
		$a = parse_url($this->GetConfigValue('base_url'));
104
		$this->CookiePath = dirname($a['path']);
105
		// Fixe la gestion des cookie sous les OS utilisant le \ comme s?parteur de chemin
106
		$this->CookiePath = str_replace("\\","/",$this->CookiePath);
107
		// ajoute un '/' terminal sauf si on est ? la racine web
108
		if ($this->CookiePath != '/') $this->CookiePath .= '/';
109
	}
110
 
111
 
112
 
113
	// DATABASE
114
	function Query($query)
115
	{
116
		if($this->GetConfigValue("debug")) $start = $this->GetMicroTime();
117
		if (!$result = mysql_query($query, $this->dblink))
118
		{
119
			ob_end_clean();
120
			die("Query failed: ".$query." (".mysql_error().")");
121
		}
122
		if($this->GetConfigValue("debug"))
123
		{
124
			$time = $this->GetMicroTime() - $start;
125
			$this->queryLog[] = array(
126
				"query"		=> $query,
127
				"time"		=> $time);
128
		}
129
		return $result;
130
	}
131
	function LoadSingle($query) {
132
		if ($data = $this->LoadAll($query)) return $data[0];
133
		return null;
134
	}
135
	function LoadAll($query)
136
	{
137
		$data=array();
138
		if ($r = $this->Query($query))
139
		{
140
			while ($row = mysql_fetch_assoc($r)) $data[] = $row;
141
			mysql_free_result($r);
142
		}
143
		return $data;
144
	}
145
 
146
 
147
 
148
	// MISC
149
	function GetMicroTime()
150
	{
151
		list($usec, $sec) = explode(" ",microtime()); return ((float)$usec + (float)$sec);
152
	}
153
	function IncludeBuffered($filename, $notfoundText = "", $vars = "", $path = "")
154
	{
155
		if ($path) $dirs = explode(":", $path);
156
		else $dirs = array("");
157
 
158
		foreach($dirs as $dir)
159
		{
160
			if ($dir) $dir .= "/";
161
			$fullfilename = $dir.$filename;
162
			if (file_exists($fullfilename))
163
			{
164
				if (is_array($vars)) extract($vars);
165
 
166
				ob_start();
167
				include($fullfilename);
168
				$output = ob_get_contents();
169
				ob_end_clean();
170
				return $output;
171
			}
172
		}
173
		if ($notfoundText) return $notfoundText;
174
		else return false;
175
	}
176
 
177
 
178
 
179
	// VARIABLES
180
	function GetPageTag() { return $this->tag; }
181
	function GetPageTime() { return $this->page["time"]; }
182
	function GetMethod() { return $this->method; }
183
	function GetConfigValue($name) { return isset($this->config[$name]) ? trim($this->config[$name]) : ''; }
184
	function GetWakkaName() { return $this->GetConfigValue("wakka_name"); }
185
	function GetWakkaVersion() { return $this->VERSION; }
186
	function GetWikiNiVersion() { return WIKINI_VERSION; }
187
 
188
	/**
189
	 * Retrieves all the triples that match some criteria.
190
	 * This allows to search triples by their approximate resource or property names.
191
	 * The allowed operators are the sql LIKE and the sql =
192
	 * @param string $resource The resource of the triples
193
	 * @param string $property The property of the triple to retrieve or null
194
	 * @param string $res_op The operator of comparison between the effective resource and $resource (default: 'LIKE')
195
	 * @param string $prop_op The operator of comparison between the effective property and $property (default: '=')
196
	 * @return array The list of all the triples that match the asked criteria
197
	 */
198
	function GetMatchingTriples($resource, $property = null, $res_op = 'LIKE', $prop_op = '=')
199
	{
200
		static $operators = array('=', 'LIKE'); // we might want to add other operators later
201
		$res_op = strtoupper($res_op);
202
		if (!in_array($res_op, $operators)) $res_op = '=';
203
		$sql = 'SELECT * FROM ' . $this->GetConfigValue('table_prefix') . 'triples '
204
			. 'WHERE resource ' . $res_op . ' "' . addslashes($resource) . '"';
205
		if ($property !== null)
206
		{
207
			$prop_op = strtoupper($prop_op);
208
			if (!in_array($prop_op, $operators)) $prop_op = '=';
209
			$sql .= ' AND property ' . $prop_op . ' "' . addslashes($property) . '"';
210
		}
211
		return $this->LoadAll($sql);
212
	}
213
 
214
	/**
215
	 * Retrieves all the values for a given couple (resource, property)
216
	 * @param string $resource The resource of the triples
217
	 * @param string $property The property of the triple to retrieve
218
	 * @param string $re_prefix The prefix to add to $resource (defaults to THISWIKI_PREFIX)
219
	 * @param string $prop_prefix The prefix to add to $property (defaults to WIKINI_VOC_PREFIX)
220
	 * @return array An array of the retrieved values, in the form
221
	 * array(
222
	 * 	0 => array(id = 7 , 'value' => $value1),
223
	 * 	1 => array(id = 34, 'value' => $value2),
224
	 * 	...
225
	 * )
226
	 */
227
	function GetAllTriplesValues($resource, $property, $re_prefix = THISWIKI_PREFIX, $prop_prefix = WIKINI_VOC_PREFIX)
228
	{
229
		$sql = 'SELECT id, value FROM ' . $this->GetConfigValue('table_prefix') . 'triples '
230
			. 'WHERE resource = "' . addslashes($re_prefix . $resource) . '" '
231
			. 'AND property = "' . addslashes($prop_prefix . $property) . '" ';
232
		return $this->LoadAll($sql);
233
	}
234
 
235
	/**
236
	 * Retrieves a single value for a given couple (resource, property)
237
	 * @param string $resource The resource of the triples
238
	 * @param string $property The property of the triple to retrieve
239
	 * @param string $re_prefix The prefix to add to $resource (defaults to <tt>THISWIKI_PREFIX</tt>)
240
	 * @param string $prop_prefix The prefix to add to $property (defaults to <tt>WIKINI_VOC_PREFIX</tt>)
241
	 * @return string The value corresponding to ($resource, $property) or null if
242
	 * there is no such couple in the triples table.
243
	 */
244
	function GetTripleValue($resource, $property, $re_prefix = THISWIKI_PREFIX, $prop_prefix = WIKINI_VOC_PREFIX)
245
	{
246
		$res = $this->GetAllTriplesValues($resource, $property, $re_prefix, $prop_prefix);
247
		if ($res) return $res[0]['value'];
248
		return null;
249
	}
250
 
251
	/**
252
	 * Checks whether a triple exists or not
253
	 * @param string $resource The resource of the triple to find
254
	 * @param string $property The property of the triple to find
255
	 * @param string $value The value of the triple to find
256
	 * @param string $re_prefix The prefix to add to $resource (defaults to <tt>THISWIKI_PREFIX</tt>)
257
	 * @param string $prop_prefix The prefix to add to $property (defaults to <tt>WIKINI_VOC_PREFIX</tt>)
258
	 * @param int The id of the found triple or 0 if there is no such triple.
259
	 */
260
	function TripleExists($resource, $property, $value, $re_prefix = THISWIKI_PREFIX, $prop_prefix = WIKINI_VOC_PREFIX)
261
	{
262
		$sql = 'SELECT id FROM ' . $this->GetConfigValue('table_prefix') . 'triples '
263
			. 'WHERE resource = "' . addslashes($re_prefix . $resource) . '" '
264
			. 'AND property = "' . addslashes($prop_prefix . $property) . '" '
265
			. 'AND value = "' . addslashes($value) . '"';
266
		$res = $this->LoadSingle($sql);
267
		if (!$res) return 0;
268
		return $res['id'];
269
	}
270
 
271
	/**
272
	 * Inserts a new triple ($resource, $property, $value) in the triples' table
273
	 * @param string $resource The resource of the triple to insert
274
	 * @param string $property The property of the triple to insert
275
	 * @param string $value The value of the triple to insert
276
	 * @param string $re_prefix The prefix to add to $resource (defaults to <tt>THISWIKI_PREFIX</tt>)
277
	 * @param string $prop_prefix The prefix to add to $property (defaults to <tt>WIKINI_VOC_PREFIX</tt>)
278
	 * @return int An error code: 0 (success), 1 (failure) or 3 (already exists)
279
	 */
280
	function InsertTriple($resource, $property, $value, $re_prefix = THISWIKI_PREFIX, $prop_prefix = WIKINI_VOC_PREFIX)
281
	{
282
		if ($this->TripleExists($resource, $property, $value, $re_prefix, $prop_prefix))
283
		{
284
			return 3;
285
		}
286
		$sql = 'INSERT INTO ' . $this->GetConfigValue('table_prefix') . 'triples (resource, property, value)'
287
			. 'VALUES ("' . addslashes($re_prefix . $resource) . '", "'
288
				. addslashes($prop_prefix . $property) . '", "'
289
				. addslashes($value) . '")';
290
		return $this->Query($sql) ? 0 : 1;
291
	}
292
 
293
	/**
294
	 * Updates a triple ($resource, $property, $value) in the triples' table
295
	 * @param string $resource The resource of the triple to update
296
	 * @param string $property The property of the triple to update
297
	 * @param string $oldvalue The old value of the triple to update
298
	 * @param string $newvalue The new value of the triple to update
299
	 * @param string $re_prefix The prefix to add to $resource (defaults to <tt>THISWIKI_PREFIX</tt>)
300
	 * @param string $prop_prefix The prefix to add to $property (defaults to <tt>WIKINI_VOC_PREFIX</tt>)
301
	 * @return int An error code: 0 (succ?s), 1 (?chec),
302
	 * 		2 ($resource, $property, $oldvalue does not exist)
303
	 * 		or 3 ($resource, $property, $newvalue already exists)
304
	 */
305
	function UpdateTriple($resource, $property, $oldvalue, $newvalue, $re_prefix = THISWIKI_PREFIX, $prop_prefix = WIKINI_VOC_PREFIX)
306
	{
307
		$id = $this->TripleExists($resource, $property, $oldvalue, $re_prefix, $prop_prefix);
308
		if (!$id) return 2;
309
		if ($this->TripleExists($resource, $property, $newvalue, $re_prefix, $prop_prefix))
310
		{
311
			return 3;
312
		}
313
		$sql = 'UPDATE ' . $this->GetConfigValue('table_prefix') . 'triples '
314
			. 'SET value = "' . addslashes($newvalue) . '" '
315
			. 'WHERE id = ' . $id;
316
		return $this->Query($sql) ? 0 : 1;
317
	}
318
 
319
	/**
320
	 * Deletes a triple ($resource, $property, $value) from the triples' table
321
	 * @param string $resource The resource of the triple to delete
322
	 * @param string $property The property of the triple to delete
323
	 * @param string $value The value of the triple to delete. If set to <tt>null</tt>,
324
	 * deletes all the triples corresponding to ($resource, $property). (defaults to <tt>null</tt>)
325
	 * @param string $re_prefix The prefix to add to $resource (defaults to <tt>THISWIKI_PREFIX</tt>)
326
	 * @param string $prop_prefix The prefix to add to $property (defaults to <tt>WIKINI_VOC_PREFIX</tt>)
327
	 */
328
	function DeleteTriple($resource, $property, $value = null, $re_prefix = THISWIKI_PREFIX, $prop_prefix = WIKINI_VOC_PREFIX)
329
	{
330
		$sql = 'DELETE FROM ' . $this->GetConfigValue('table_prefix') . 'triples '
331
			. 'WHERE resource = "' . addslashes($re_prefix . $resource) . '" '
332
			. 'AND property = "' . addslashes($prop_prefix . $property) . '" ';
333
		if ($value !== null) $sql .= 'AND value = "' . addslashes($value) . '"';
334
		$this->Query($sql);
335
	}
336
 
337
	// inclusions
338
	/**
339
	 * Enr?gistre une nouvelle inclusion dans la pile d'inclusions.
340
	 *
341
	 * @param string $pageTag Le nom de la page qui va ?tre inclue
342
	 * @return int Le nombre d'?l?ments dans la pile
343
	 */
344
	function RegisterInclusion($pageTag)
345
	{
346
		return array_unshift($this->inclusions, strtolower(trim($pageTag)));
347
	}
348
	/**
349
	 * Retire le dernier ?l?ment de la pile d'inclusions.
350
	 *
351
	 * @return string Le nom de la page dont l'inclusion devrait se terminer.
352
	 * null s'il n'y a plus d'inclusion dans la pile.
353
	 */
354
	function UnregisterLastInclusion()
355
	{
356
		return array_shift($this->inclusions);
357
	}
358
	/**
359
	 * Renvoie le nom de la page en cours d'inclusion.
360
	 *
361
	 * @example // dans le cas d'une action comme l'ActionEcrivezMoi
362
	 * if($inc = $this->CurrentInclusion() && strtolower($this->GetPageTag()) != $inc)
363
	 * 	echo 'Cette action ne peut ?tre appel?e depuis une page inclue';
364
	 * @return string Le nom (tag) de la page (en minuscules)
365
	 * false si la pile est vide.
366
	 */
367
	function GetCurrentInclusion()
368
	{
369
		return isset($this->inclusions[0]) ? $this->inclusions[0]: false ;
370
	}
371
	/**
372
	 * V?rifie si on est ? l'int?rieur d'une inclusion par $pageTag (sans tenir compte de la casse)
373
	 *
374
	 * @param string $pageTag Le nom de la page ? v?rifier
375
	 * @return bool True si on est ? l'int?rieur d'une inclusion par $pageTag (false sinon)
376
	 */
377
	function IsIncludedBy($pageTag)
378
	{
379
		return in_array(strtolower($pageTag), $this->inclusions);
380
	}
381
	/**
382
	 *
383
	 * @return array La pile d'inclusions
384
	 * L'?l?ment 0 sera la derni?re inclusion, l'?l?ment 1 sera son parent et ainsi de suite.
385
	 */
386
	function GetAllInclusions()
387
	{
388
		return $this->inclusions;
389
	}
390
	/**
391
	 * Remplace la pile des inclusions par une nouvelle pile (par d?faut une pile vide)
392
	 * Permet de formatter une page sans tenir compte des inclusions pr?c?dentes.
393
	 *
394
	 * @param array $ La nouvelle pile d'inclusions.
395
	 * L'?l?ment 0 doit repr?senter la derni?re inclusion, l'?l?ment 1 son parent et ainsi de suite.
396
	 * @return array L'ancienne pile d'inclusions, avec les noms des pages en minuscules.
397
	 */
398
	function SetInclusions($pile = array())
399
	{
400
		$temp = $this->inclusions;
401
		$this->inclusions = $pile;
402
		return $temp;
403
	}
404
 
405
	// PAGES
406
	function LoadPage($tag, $time = "", $cache = 1)
407
	{
408
		// retrieve from cache
409
		if (!$time && $cache && (($cachedPage = $this->GetCachedPage($tag)) !== false))
410
		{
411
			$page = $cachedPage;
412
		}
413
		else // load page
414
		{
415
			$sql = "SELECT * FROM ".$this->config["table_prefix"]."pages"
416
				. " WHERE tag = '".mysql_real_escape_string($tag)."' AND "
417
				. ($time ? "time = '".mysql_real_escape_string($time)."'" : "latest = 'Y'") . " LIMIT 1";
418
			$page = $this->LoadSingle($sql);
419
			// cache result
420
			if (!$time) $this->CachePage($page, $tag);
421
		}
422
		return $page;
423
	}
424
	/**
425
	 * Retrieves the cached version of a page.
426
	 *
427
	 * Notice that this method null or false, use
428
	 * 	$this->GetCachedPage($tag) === false
429
	 * to check if a page is not in the cache.
430
	 * @return mixed The cached version of a page:
431
	 * 	- the page DB line if the page exists and is in cache
432
	 * 	- null if the cache knows that the page does not exists
433
	 * 	- false is the cache does not know the page
434
	 */
435
	function GetCachedPage($tag) {return (array_key_exists($tag, $this->pageCache) ? $this->pageCache[$tag] : false); }
436
	/**
437
	 * Caches a page's DB line.
438
	 *
439
	 * @param array $page The page (full) DB line or null if the page does not exists
440
	 * @param string $pageTag The tag of the page to cache. Defaults to $page['tag'] but is mendatory when $page === null
441
	 */
442
	function CachePage($page, $pageTag = null) {
443
		if ($pageTag === null)
444
		{
445
			$pageTag = $page["tag"];
446
		}
447
		$this->pageCache[$pageTag] = $page;
448
	}
449
	function SetPage($page) { $this->page = $page; if ($this->page["tag"]) $this->tag = $this->page["tag"]; }
450
	function LoadPageById($id) { return $this->LoadSingle("select * from ".$this->config["table_prefix"]."pages where id = '".mysql_real_escape_string($id)."' limit 1"); }
451
	function LoadRevisions($page) { return $this->LoadAll("select * from ".$this->config["table_prefix"]."pages where tag = '".mysql_real_escape_string($page)."' order by time desc"); }
452
	function LoadPagesLinkingTo($tag) { return $this->LoadAll("select from_tag as tag from ".$this->config["table_prefix"]."links where to_tag = '".mysql_real_escape_string($tag)."' order by tag"); }
453
	function LoadRecentlyChanged($limit=50)
454
	{
455
		$limit= (int) $limit;
456
		if ($pages = $this->LoadAll("select id, tag, time, user, owner from ".$this->config["table_prefix"]."pages where latest = 'Y' and comment_on = '' order by time desc limit $limit"))
457
		{
458
			foreach ($pages as $page)
459
			{
460
				$this->CachePage($page);
461
			}
462
			return $pages;
463
		}
464
	}
465
	function LoadAllPages() { return $this->LoadAll("select * from ".$this->config["table_prefix"]."pages where latest = 'Y' order by tag"); }
466
	function FullTextSearch($phrase) { return $this->LoadAll("select * from ".$this->config["table_prefix"]."pages where latest = 'Y' and match(tag, body) against('".mysql_real_escape_string($phrase)."')"); }
467
	function LoadWantedPages() {
468
		$p = $this->config["table_prefix"];
469
		$r = "SELECT ${p}links.to_tag AS tag, COUNT(${p}links.from_tag) AS count "
470
			. "FROM ${p}links LEFT JOIN ${p}pages ON ${p}links.to_tag = ${p}pages.tag "
471
			. "WHERE ${p}pages.tag IS NULL GROUP BY ${p}links.to_tag ORDER BY count DESC, tag ASC";
472
		return $this->LoadAll($r);
473
	}
474
	function LoadOrphanedPages() { return $this->LoadAll("select distinct tag from ".$this->config["table_prefix"]."pages as p left join ".$this->config["table_prefix"]."links as l on p.tag = l.to_tag where l.to_tag is NULL and p.comment_on = '' and p.latest = 'Y' order by tag"); }
475
	function IsOrphanedPage($tag) { return $this->LoadAll("select distinct tag from ".$this->config['table_prefix']."pages as p left join ".$this->config['table_prefix']."links as l on p.tag = l.to_tag where l.to_tag is NULL and p.latest = 'Y' and tag = '".mysql_real_escape_string($tag)."'"); }
476
	function DeleteOrphanedPage($tag) {
477
		$p = $this->config["table_prefix"];
478
		$this->Query("DELETE FROM ${p}pages WHERE tag='".mysql_real_escape_string($tag)."' OR comment_on='".mysql_real_escape_string($tag)."'");
479
		$this->Query("DELETE FROM ${p}links WHERE from_tag='".mysql_real_escape_string($tag)."' ");
480
		$this->Query("DELETE FROM ${p}acls WHERE page_tag='".mysql_real_escape_string($tag)."' ");
481
		$this->Query("DELETE FROM ${p}referrers WHERE page_tag='".mysql_real_escape_string($tag)."' ");
482
	}
483
 
484
	/**
485
	 * SavePage
486
	 * Sauvegarde un contenu dans une page donn?e
487
	 *
488
	 * @param string $body Contenu ? sauvegarder dans la page
489
	 * @param string $tag Nom de la page
490
	 * @param string $comment_on Indication si c'est un commentaire
491
	 * @param boolean $bypass_acls Indication si on bypasse les droits d'?criture
492
	 * @return int Code d'erreur : 0 (succ?s), 1 (l'utilisateur n'a pas les droits)
493
	 */
494
	function SavePage($tag, $body, $comment_on = "", $bypass_acls = false)
495
	{
496
		// get current user
497
		$user = $this->GetUserName();
498
 
499
		// check bypass of rights or write privilege
500
		$rights = $bypass_acls || ($comment_on ? $this->HasAccess("comment", $comment_on) : $this->HasAccess("write", $tag));
501
 
502
		if ($rights)
503
		{
504
			// is page new?
505
			if (!$oldPage = $this->LoadPage($tag))
506
			{
507
				// create default write acl. store empty write ACL for comments.
508
				$this->SaveAcl($tag, "write", ($comment_on ? $user : $this->GetConfigValue("default_write_acl")));
509
 
510
				// create default read acl
511
				$this->SaveAcl($tag, "read", $this->GetConfigValue("default_read_acl"));
512
 
513
				// create default comment acl.
514
				$this->SaveAcl($tag, "comment", ($comment_on ? "" : $this->GetConfigValue("default_comment_acl")));
515
 
516
				// current user is owner; if user is logged in! otherwise, no owner.
517
				if ($this->GetUser()) $owner = $user;
518
				else $owner = '';
519
			}
520
			else
521
			{
522
				// aha! page isn't new. keep owner!
523
				$owner = $oldPage["owner"];
524
 
525
				// ...and comment_on, eventualy?
526
				if ($comment_on == '') $comment_on = $oldPage['comment_on'];
527
			}
528
 
529
 
530
			// set all other revisions to old
531
			$this->Query("update ".$this->config["table_prefix"]."pages set latest = 'N' where tag = '".mysql_real_escape_string($tag)."'");
532
 
533
			// add new revision
534
			$this->Query("insert into ".$this->config["table_prefix"]."pages set ".
535
				"tag = '".mysql_real_escape_string($tag)."', ".
536
				($comment_on ? "comment_on = '".mysql_real_escape_string($comment_on)."', " : "").
537
				"time = now(), ".
538
				"owner = '".mysql_real_escape_string($owner)."', ".
539
				"user = '".mysql_real_escape_string($user)."', ".
540
				"latest = 'Y', ".
541
				"body = '".mysql_real_escape_string(chop($body))."'");
542
 
543
			unset($this->pageCache[$tag]);
544
			return 0;
545
		}
546
		else return 1;
547
	}
548
 
549
 
550
	/**
551
	 * AppendContentToPage
552
	 * Ajoute du contenu ? la fin d'une page
553
	 *
554
	 * @param string $content Contenu ? ajouter ? la page
555
	 * @param string $page Nom de la page
556
	 * @param boolean $bypass_acls Boul?en pour savoir s'il faut bypasser les ACLs
557
	 * @return int Code d'erreur : 0 (succ?s), 1 (pas de contenu sp?cifi?)
558
	 */
559
	function AppendContentToPage($content, $page, $bypass_acls = false)
560
	{
561
		// Si un contenu est sp?cifi?
562
		if (isset($content))
563
		{
564
			// -- D?termine quelle est la page :
565
			//    -- pass?e en param?tre (que se passe-t'il si elle n'existe pas ?)
566
			//    -- ou la page en cours par d?faut
567
			$page = isset($page) ? $page : $this->GetPageTag();
568
 
569
			// -- Chargement de la page
570
			$result = $this->LoadPage($page);
571
			$body = $result['body'];
572
			// -- Ajout du contenu ? la fin de la page
573
			$body .= $content;
574
 
575
			// -- Sauvegarde de la page
576
			// TODO : que se passe-t-il si la page est pleine ou si l'utilisateur n'a pas les droits ?
577
			$this->SavePage($page, $body, "", $bypass_acls);
578
 
579
			// now we render it internally so we can write the updated link table.
580
			$this->ClearLinkTable();
581
			$this->StartLinkTracking();
582
			$temp = $this->SetInclusions();
583
			$this->RegisterInclusion($this->GetPageTag()); // on simule totalement un affichage normal
584
			$this->Format($body);
585
			$this->SetInclusions($temp);
586
			if($user = $this->GetUser())
587
			{
588
				$this->TrackLinkTo($user['name']);
589
			}
590
			if($owner = $this->GetPageOwner())
591
			{
592
				$this->TrackLinkTo($owner);
593
			}
594
			$this->StopLinkTracking();
595
			$this->WriteLinkTable();
596
			$this->ClearLinkTable();/**/
597
 
598
			// Retourne 0 seulement si tout c'est bien pass?
599
			return 0;
600
		}
601
		else return 1;
602
	}
603
 
604
	/**
605
	 * LogAdministrativeAction($user, $content, $page = "")
606
	 *
607
	 * @param string $user Utilisateur
608
	 * @param string $content Contenu de l'enregistrement
609
	 * @param string $page Page de log
610
	 *
611
	 * @return int Code d'erreur : 0 (succ?s), 1 (pas de contenu sp?cifi?)
612
	 */
613
	function LogAdministrativeAction($user, $content, $page = "")
614
	{
615
		$order   = array("\r\n", "\n", "\r");
616
		$replace = '\\n';
617
		$content = str_replace($order, $replace, $content);
618
		$contentToAppend = "\n" . date("Y-m-d H:i:s") . " . . . . " . $user . " . . . . " . $content . "\n";
619
		$page = $page ? $page : "LogDesActionsAdministratives" . date("Ymd");
620
		return $this->AppendContentToPage($contentToAppend, $page, true);
621
	}
622
 
623
 
624
	/**
625
	 * Make the purge of page versions that are older than the last version older than 3 "pages_purge_time"
626
	 * This method permits to allways keep a version that is older than that period.
627
	 */
628
	function PurgePages() {
629
		if ($days = $this->GetConfigValue("pages_purge_time")) { // is purge active ?
630
			// let's search which pages versions we have to remove
631
			// this is necessary beacause even MySQL does not handel multi-tables deletes before version 4.0
632
			$wnPages = $this->GetConfigValue('table_prefix') . 'pages';
633
			$sql = 'SELECT DISTINCT a.id FROM ' . $wnPages . ' a,' . $wnPages . ' b WHERE a.latest = \'N\' AND a.time < date_sub(now(), INTERVAL \'' . addslashes($days) . '\' DAY) AND a.tag = b.tag AND a.time < b.time AND b.time < date_sub(now(), INTERVAL \'' . addslashes($days) . '\' DAY)';
634
			$ids = $this->LoadAll($sql);
635
 
636
			if (count($ids)) { // there are some versions to remove from DB
637
				// let's build one big request, that's better...
638
				$sql = 'DELETE FROM ' . $wnPages . ' WHERE id IN (';
639
				foreach($ids as $key => $line){
640
					$sql .= ($key ? ', ':'') . $line['id']; // NB.: id is an int, no need of quotes
641
				}
642
				$sql .= ')';
643
 
644
				// ... and send it !
645
				$this->Query($sql);
646
			}
647
		}
648
	}
649
 
650
 
651
 
652
	// COOKIES
653
	function SetSessionCookie($name, $value)
654
	{
655
		SetCookie($name, $value, 0, $this->CookiePath);
656
		$_COOKIE[$name] = $value;
657
	}
658
	function SetPersistentCookie($name, $value, $remember = 0)
659
	{
660
		SetCookie($name, $value, time() + ($remember ? 90*24*60*60 : 60 * 60), $this->CookiePath);
661
		$_COOKIE[$name] = $value;
662
	}
663
	function DeleteCookie($name)
664
	{
665
		SetCookie($name, "", 1, $this->CookiePath);
666
		$_COOKIE[$name] = "";
667
	}
668
	function GetCookie($name) { return $_COOKIE[$name]; }
669
 
670
 
671
 
672
	// HTTP/REQUEST/LINK RELATED
673
	function SetMessage($message) { $_SESSION["message"] = $message; }
674
	function GetMessage()
675
	{
676
		if (isset($_SESSION["message"])) $message = $_SESSION["message"];
677
		else $message = "";
678
		$_SESSION["message"] = "";
679
		return $message;
680
	}
681
	function Redirect($url)
682
	{
683
		header("Location: $url");
684
		exit;
685
	}
686
	// returns just PageName[/method].
687
	function MiniHref($method = "", $tag = "")
688
	{
689
		if (!$tag = trim($tag)) $tag = $this->tag;
690
		return $tag.($method ? "/".$method : "");
691
	}
692
	// returns the full url to a page/method.
693
	function Href($method = "", $tag = "", $params = "", $htmlspchars = true)
694
	{
695
		$href = $this->config["base_url"].$this->MiniHref($method, $tag);
696
		if ($params)
697
		{
698
			$href .= ($this->config["rewrite_mode"] ? "?" : ($htmlspchars ? "&amp;" : '&')).$params;
699
		}
700
		return $href;
701
	}
702
	function Link($tag, $method = "", $text = "", $track = 1)
703
	{
704
		$displayText = $text ? $text : $tag;
705
		// is this an interwiki link?
706
		if (preg_match('/^' . WN_INTERWIKI_CAPTURE . '$/', $tag, $matches))
707
		{
708
			if ($tagInterWiki = $this->GetInterWikiUrl($matches[1], $matches[2])) {
709
				return '<a href="'.htmlspecialchars($tagInterWiki).'">'
710
					.htmlspecialchars($displayText).' (interwiki)</a>';
711
			}
712
			else return '<a href="'.htmlspecialchars($tag).'">'
713
				.htmlspecialchars($displayText).' (interwiki inconnu)</a>';
714
		}
715
		// is this a full link? ie, does it contain non alpha-numeric characters?
716
		// Note : [:alnum:] is equivalent [0-9A-Za-z]
717
		//		  [^[:alnum:]] means : some caracters other than [0-9A-Za-z]
718
		// For example : "www.adress.com", "mailto:adress@domain.com", "http://www.adress.com"
719
		else if (preg_match("/[^[:alnum:]]/", $tag))
720
		{
721
			// check for various modifications to perform on $tag
722
			if (preg_match("/^[\w.-]+\@[\w.-]+$/", $tag))
723
			{ // email addresses
724
				$tag = 'mailto:'.$tag;
725
			}
726
			// Note : in Perl regexp, (?: ... ) is a non-catching cluster
727
			else if (preg_match('/^[[:alnum:]][[:alnum:].-]*(?:\/|$)/', $tag))
728
			{ // protocol-less URLs
729
				$tag = 'http://'.$tag;
730
			}
731
			// Finally, block script schemes (see RFC 3986 about
732
			// schemes) and allow relative link & protocol-full URLs
733
			else if (preg_match('/^[a-z0-9.+-]*script[a-z0-9.+-]*:/i', $tag)
734
				|| !(preg_match('/^\.?\.?\//', $tag)
735
					|| preg_match('/^[a-z0-9.+-]+:\/\//i', $tag)))
736
			{
737
				// If does't fit, we can't qualify $tag as an URL.
738
				// There is a high risk that $tag is just XSS (bad
739
				// javascript: code) or anything nasty. So we must not
740
				// produce any link at all.
741
				return htmlspecialchars($tag.($text ? ' '.$text : ''));
742
			}
743
			// Important: Here, we know that $tag is not something bad
744
			// and that we must produce a link with it
745
 
746
			// An inline image? (text!=tag and url ends by png,gif,jpeg)
747
			if ($text and preg_match("/\.(gif|jpeg|png|jpg)$/i",$tag))
748
			{
749
				return '<img src="'.htmlspecialchars($tag)
750
					.'" alt="'.htmlspecialchars($displayText).'"/>';
751
			}
752
			else
753
			{
754
				// Even if we know $tag is harmless, we MUST encode it
755
				// in HTML with htmlspecialchars() before echoing it.
756
				// This is not about being paranoiac. This is about
757
				// being compliant to the HTML standard.
758
				return '<a href="'.htmlspecialchars($tag).'">'
759
					.htmlspecialchars($displayText).'</a>';
760
			}
761
		}
762
		else
763
		{
764
			// it's a Wiki link!
765
			if (!empty($track)) $this->TrackLinkTo($tag);
766
			if ($this->LoadPage($tag))
767
				return '<a href="'.htmlspecialchars($this->href($method, $tag)).'">'
768
					.htmlspecialchars($displayText).'</a>';
769
			else
770
				return '<span class="missingpage">'.htmlspecialchars($displayText)
771
				.'</span><a href="'.htmlspecialchars($this->href("edit", $tag)).'">?</a>';
772
		}
773
	}
774
	function ComposeLinkToPage($tag, $method = "", $text = "", $track = 1) {
775
		if (!$text) $text = $tag;
776
		$text = htmlspecialchars($text);
777
		if ($track)
778
			$this->TrackLinkTo($tag);
779
		return '<a href="'.$this->href($method, $tag).'">'.$text.'</a>';
780
	}
781
	function IsWikiName($text) {
782
		return preg_match('/^' . WN_CAMEL_CASE . '$/', $text);
783
	}
784
 
785
	// LinkTracking management
786
	/**
787
	 * Tracks the link to a given page (only if the LinkTracking is activated)
788
	 * @param string $tag The tag (name) of the page to track a link to.
789
	 */
790
	function TrackLinkTo($tag) {
791
		if ($this->LinkTracking()) $this->linktable[] = $tag;
792
	}
793
	/**
794
	 * @return array The current link tracking table
795
	 */
796
	function GetLinkTable() { return $this->linktable; }
797
	/**
798
	 * Clears the link tracking table
799
	 */
800
	function ClearLinkTable() { $this->linktable = array(); }
801
	/**
802
	 * Starts the LinkTracking
803
	 * @return bool The previous state of the link tracking
804
	 */
805
	function StartLinkTracking() {
806
		return $this->LinkTracking(true);
807
	}
808
	/**
809
	 * Stops the LinkTracking
810
	 * @return bool The previous state of the link tracking
811
	 */
812
	function StopLinkTracking() {
813
		return $this->LinkTracking(false);
814
	}
815
	/**
816
	 * Sets and/or retrieve the state of the LinkTracking
817
	 * @param bool $newStatus The new status of the LinkTracking
818
	 * (defaults to <tt>null</tt> which lets it unchanged)
819
	 * @return bool The previous state of the link tracking
820
	 */
821
	function LinkTracking($newStatus = null)
822
	{
823
		$old = $this->isTrackingLinks;
824
		if ($newStatus !== null) $this->isTrackingLinks = $newStatus;
825
		return $old;
826
	}
827
	function WriteLinkTable() {
828
		// delete old link table
829
		$this->Query("delete from ".$this->config["table_prefix"]."links where from_tag = '".mysql_real_escape_string($this->GetPageTag())."'");
830
		if ($linktable = $this->GetLinkTable())
831
		{
832
			$from_tag = mysql_real_escape_string($this->GetPageTag());
833
			foreach ($linktable as $to_tag)
834
			{
835
				$lower_to_tag = strtolower($to_tag);
836
				if (!isset($written[$lower_to_tag]))
837
				{
838
					$this->Query("insert into ".$this->config["table_prefix"]."links set from_tag = '".$from_tag."', to_tag = '".mysql_real_escape_string($to_tag)."'");
839
					$written[$lower_to_tag] = 1;
840
				}
841
			}
842
		}
843
	}
844
 
845
	function Header() {
846
		$action = $this->GetConfigValue("header_action");
847
		if (($actionObj = &$this->GetActionObject($action)) && is_object($actionObj))
848
		{
849
			return $actionObj->GenerateHeader();
850
		}
851
		return $this->Action($action, 1);
852
	}
853
 
854
	function Footer() {
855
		$action = $this->GetConfigValue("footer_action");
856
		if (($actionObj = &$this->GetActionObject($action)) && is_object($actionObj))
857
		{
858
			return $actionObj->GenerateFooter();
859
		}
860
		return $this->Action($action, 1);
861
	}
862
 
863
	// FORMS
864
	function FormOpen($method = "", $tag = "", $formMethod = "post") {
865
		$result = "<form action=\"".$this->href($method, $tag)."\" method=\"".$formMethod."\">\n";
866
		if (!$this->config["rewrite_mode"]) $result .= "<input type=\"hidden\" name=\"wiki\" value=\"".$this->MiniHref($method, $tag)."\" />\n";
867
		return $result;
868
	}
869
	function FormClose() {
870
		return "</form>\n";
871
	}
872
 
873
 
874
 
875
	// INTERWIKI STUFF
876
	function ReadInterWikiConfig() {
877
		if ($lines = file("interwiki.conf"))
878
		{
879
			foreach ($lines as $line)
880
			{
881
				if ($line = trim($line))
882
				{
883
					list($wikiName, $wikiUrl) = explode(" ", trim($line));
884
					$this->AddInterWiki($wikiName, $wikiUrl);
885
				}
886
			}
887
		}
888
	}
889
	function AddInterWiki($name, $url) {
890
		$this->interWiki[strtolower($name)] = $url;
891
	}
892
	function GetInterWikiUrl($name, $tag)
893
	{
894
		if (isset($this->interWiki[strtolower($name)])) return $this->interWiki[strtolower($name)].$tag;
895
		else return FALSE;
896
	}
897
 
898
 
899
 
900
	// REFERRERS
901
	function LogReferrer($tag = "", $referrer = "") {
902
		// fill values
903
		if (!$tag = trim($tag)) $tag = $this->GetPageTag();
904
		if (!$referrer = trim($referrer) AND isset($_SERVER["HTTP_REFERER"])) $referrer = $_SERVER["HTTP_REFERER"];
905
 
906
		// check if it's coming from another site
907
		if ($referrer && !preg_match("/^".preg_quote($this->GetConfigValue("base_url"), "/")."/", $referrer))
908
		{
909
			// avoid XSS (with urls like "javascript:alert()" and co)
910
			// by forcing http/https prefix
911
			// NB.: this does NOT exempt to htmlspecialchars() the collected URIs !
912
			if (!preg_match('`^https?://`', $referrer)) return;
913
 
914
			$this->Query("insert into ".$this->config["table_prefix"]."referrers set ".
915
				"page_tag = '".mysql_real_escape_string($tag)."', ".
916
				"referrer = '".mysql_real_escape_string($referrer)."', ".
917
				"time = now()");
918
		}
919
	}
920
	function LoadReferrers($tag = "") {
921
		return $this->LoadAll("select referrer, count(referrer) as num from ".$this->config["table_prefix"]."referrers ".($tag = trim($tag) ? "where page_tag = '".mysql_real_escape_string($tag)."'" : "")." group by referrer order by num desc");
922
	}
923
	function PurgeReferrers() {
924
		if ($days = $this->GetConfigValue("referrers_purge_time")) {
925
			$this->Query("delete from ".$this->config["table_prefix"]."referrers where time < date_sub(now(), interval '".mysql_real_escape_string($days)."' day)");
926
		}
927
	}
928
 
929
 
930
 
931
	// PLUGINS
932
	/**
933
	 * Exacutes an "action" module and returns the generated output
934
	 * @param string $action The name of the action and its eventual parameters,
935
	 * as it appears in the page between "{{" and "}}"
936
	 * @param boolean $forceLinkTracking By default, the link tracking will be disabled
937
	 * during the call of an action. Set this value to <code>true</code> to allow it.
938
	 * @param array $vars An array of additionnal parameters to give to the action, in the form
939
	 * array( 'param' => 'value').
940
	 * This allows you to call Action() internally, setting $action to the name of the action
941
	 * you want to call and it's parameters in an array, wich is more efficient than
942
	 * the pattern-matching algorithm used to extract the parameters from $action.
943
	 * @return The output generated by the action.
944
	 */
945
	function Action($action, $forceLinkTracking = 0, $vars = array())
946
	{
947
		$cmd = trim($action);
948
		// extract $action and $vars_temp ("raw" attributes)
949
		if (!preg_match("/^([a-zA-Z-0-9]+)\/?(.*)$/", $cmd, $matches))
950
		{
951
			return '<i>Action invalide &quot;' . htmlspecialchars($cmd) . '&quot;</i>';
952
		}
953
		list(, $action, $vars_temp) = $matches;
954
		$vars[$vars_temp] = $vars_temp; // usefull for {{action/vars_temp}}
955
 
956
		// now that we have the action's name, we can check if the user satisfies the ACLs
957
		if (!$this->CheckModuleACL($action, 'action'))
958
		{
959
			return 'Erreur: vous n\'avez pas acc&egrave;s &agrave; l\'action ' . $action . '.';
960
		}
961
 
962
		// match all attributes (key and value)
963
		// prepare an array for extract() to work with (in $this->IncludeBuffered())
964
		if (preg_match_all("/([a-zA-Z0-9]*)=\"(.*)\"/U", $vars_temp, $matches))
965
		{
966
			for ($a = 0; $a < count($matches[1]); $a++)
967
			{
968
				$vars[$matches[1][$a]] = $matches[2][$a];
969
			}
970
		}
971
 
972
		if (!$forceLinkTracking) $this->StopLinkTracking();
973
		if ($actionObj = &$this->GetActionObject($action))
974
		{
975
			if (is_object($actionObj))
976
			{
977
				$result = $actionObj->PerformAction($vars, $cmd);
978
			}
979
			else // $actionObj is an error message
980
			{
981
				$result = $actionObj;
982
			}
983
		}
984
		else // $actionObj == null (not found, no error message)
985
		{
986
			$this->parameter = &$vars;
987
			$result = $this->IncludeBuffered(strtolower($action).".php", "<i>Action inconnue &quot;$action&quot;</i>", $vars, $this->config["action_path"]);
988
			unset($this->parameter);
989
		}
990
		$this->StartLinkTracking(); // shouldn't we restore the previous status ?
991
		return $result;
992
	}
993
 
994
	/**
995
	 * Finds the object corresponding to an action, if it exists.
996
	 * @param string $name The name of an action (should be alphanumeric)
997
	 * @return mixed
998
	 * 	- null if the corresponding file was not found or the corresponding class didn't exist after inclusion
999
	 *  - an error string if the corresponding file was found but an error append while loading it
1000
	 *  - the object corresponding to this action if no problem happend
1001
	 * To check the result, you should use is_object() on it.
1002
	 * You should always assign the result of this method by referrence
1003
	 * to avoid copying the object, which is the default beheviour in PHP4.
1004
	 * @example
1005
	 * $var = &$wiki->GetActionObject('actionname');
1006
	 * if (is_object($var))
1007
	 * {
1008
	 * 		// normal behaviour
1009
	 * }
1010
	 * elseif ($var) // $var is not an object but an error string
1011
	 * {
1012
	 * 		// threat error
1013
	 * }
1014
	 * else // action was not found
1015
	 * {
1016
	 * 		// rescue from inexising action or sth
1017
	 * }
1018
	 */
1019
	function &GetActionObject($name)
1020
	{
1021
		$name = strtolower($name);
1022
		$actionObj = null; // the generated object
1023
		if (!preg_match('/^[a-z0-9]+$/', $name)) // paranoiac
1024
		{
1025
			return $actionObj;
1026
		}
1027
 
1028
		// already tried to load this object ? (may be null)
1029
		if (isset($this->actionObjects[$name]))
1030
		{
1031
			return $this->actionObjects[$name];
1032
		}
1033
 
1034
		// object not loaded, try to load it
1035
		$filename = $name . '.class.php';
1036
		// load parent class for all action objects (only once)
1037
		require_once 'includes/action.class.php';
1038
		// include the action file, this should return an empty string
1039
		$result = $this->IncludeBuffered($filename, null, null, $this->GetConfigValue('action_path'));
1040
		if ($result) // the result was not an empty string, certainly an error message
1041
		{
1042
			$actionObj = $result;
1043
		}
1044
		elseif ($result !== false) // the result was empty but the file was found
1045
		{
1046
			$class = 'Action' . ucfirst($name);
1047
			if (class_exists($class))
1048
			{
1049
				$actionObj = new $class($this);
1050
				if (!is_a($actionObj, 'WikiniAction'))
1051
				{
1052
					die("Action invalide '$name': classe incorrecte");
1053
				}
1054
			}
1055
		}
1056
		$this->actionObjects[$name] = &$actionObj;
1057
		return $actionObj;
1058
	}
1059
 
1060
	/**
1061
	 * Retrieves the list of existing actions
1062
	 * @return array An unordered array of all the available actions.
1063
	 */
1064
	function GetActionsList()
1065
	{
1066
		$action_path = $this->GetConfigValue('action_path');
1067
		$list = array();
1068
		if ($dh = opendir($action_path))
1069
		{
1070
			while (($file = readdir($dh)) !== false)
1071
			{
1072
				if (preg_match('/^([a-zA-Z-0-9]+)(.class)?.php$/', $file, $matches))
1073
				{
1074
					$list[] = $matches[1];
1075
				}
1076
			}
1077
		}
1078
		return $list;
1079
	}
1080
 
1081
	/**
1082
	 * Retrieves the list of existing handlers
1083
	 * @return array An unordered array of all the available handlers.
1084
	 */
1085
	function GetHandlersList()
1086
	{
1087
		$handler_path = $this->GetConfigValue('handler_path') . '/page/';
1088
		$list = array();
1089
		if ($dh = opendir($handler_path))
1090
		{
1091
			while (($file = readdir($dh)) !== false)
1092
			{
1093
				if (preg_match('/^([a-zA-Z-0-9]+)(.class)?.php$/', $file, $matches))
1094
				{
1095
					$list[] = $matches[1];
1096
				}
1097
			}
1098
		}
1099
		return $list;
1100
	}
1101
 
1102
	function Method($method) {
1103
		if (!$handler = $this->page["handler"]) $handler = "page";
1104
		$methodLocation = $handler."/".$method.".php";
1105
		return $this->IncludeBuffered($methodLocation, "<i>M&eacute;thode inconnue \"$methodLocation\"</i>", "", $this->config["handler_path"]);
1106
	}
1107
	function Format($text, $formatter = "wakka") {
1108
		return $this->IncludeBuffered("formatters/".$formatter.".php", "<i>Impossible de trouver le formateur \"$formatter\"</i>", compact("text"));
1109
	}
1110
 
1111
 
1112
 
1113
	// USERS
1114
	function LoadUser($name, $password = 0) { return $this->LoadSingle("select * from ".$this->config["table_prefix"]."users where name = '".mysql_real_escape_string($name)."' ".($password === 0 ? "" : "and password = '".mysql_real_escape_string($password)."'")." limit 1"); }
1115
	function LoadUsers() { return $this->LoadAll("select * from ".$this->config["table_prefix"]."users order by name"); }
1116
	function GetUserName() { if ($user = $this->GetUser()) $name = $user["name"]; else if (!$name = gethostbyaddr($_SERVER["REMOTE_ADDR"])) $name = $_SERVER["REMOTE_ADDR"]; return $name; }
1117
	function GetUser() { return (isset($_SESSION["user"]) ? $_SESSION["user"] : '');}
1118
	function SetUser($user, $remember=0) { $_SESSION["user"] = $user; $this->SetPersistentCookie("name", $user["name"], $remember); $this->SetPersistentCookie("password", $user["password"], $remember); $this->SetPersistentCookie("remember", $remember, $remember); }
1119
	function LogoutUser() { $_SESSION["user"] = ""; $this->DeleteCookie("name"); $this->DeleteCookie("password"); }
1120
	function UserWantsComments() { if (!$user = $this->GetUser()) return false; return ($user["show_comments"] == "Y"); }
1121
	function GetParameter($parameter, $default = '') { return (isset($this->parameter[$parameter]) ? $this->parameter[$parameter] : $default); }
1122
 
1123
 
1124
	// COMMENTS
1125
	/**
1126
	 * Charge les commentaires relatifs ? une page.
1127
	 *
1128
	 * @param string $tag Nom de la page. Ex : "PagePrincipale"
1129
	 * @return array Tableau contenant tous les commentaires et leurs
1130
	 * propri?t?s correspondantes.
1131
	 */
1132
	function LoadComments($tag)
1133
	{
1134
		return $this->LoadAll(
1135
			"select * " .
1136
			"from ".$this->config["table_prefix"]."pages " .
1137
			"where comment_on = '".mysql_real_escape_string($tag)."' " .
1138
			"and latest = 'Y' " .
1139
			"order by substring(tag, 8) + 0");
1140
	}
1141
	/**
1142
	 * Charge les derniers commentaires de toutes les pages.
1143
	 *
1144
	 * @param int $limit Nombre de commentaires charg?s.
1145
	 *                   0 par d?faut (ie tous les commentaires).
1146
	 * @return array Tableau contenant chaque commentaire et ses
1147
	 *               propri?t?s associ?es.
1148
	 * @todo Ajouter le param?tre $start pour permettre une pagination
1149
	 *       des commentaires : ->LoadRecentComments(10, 10)
1150
	 */
1151
	function LoadRecentComments($limit = 0)
1152
	{
1153
		// The part of the query which limit the number of comments
1154
		if(is_numeric($limit) && $limit > 0) $lim = " limit ".$limit;
1155
		else $lim = "";
1156
 
1157
		// Query
1158
		return $this->LoadAll(
1159
			"select * " .
1160
			"from " . $this->config["table_prefix"] . "pages " .
1161
			"where comment_on != '' " .
1162
			"and latest = 'Y' " .
1163
			"order by time desc " .
1164
			$lim);
1165
	}
1166
	function LoadRecentlyCommented($limit = 50)
1167
	{
1168
		$pages = array();
1169
 
1170
		// NOTE: this is really stupid. Maybe my SQL-Fu is too weak, but apparently there is no easier way to simply select
1171
		//       all comment pages sorted by their first revision's (!) time. ugh!
1172
 
1173
		// load ids of the first revisions of latest comments. err, huh?
1174
		if ($ids = $this->LoadAll("select min(id) as id from ".$this->config["table_prefix"]."pages where comment_on != '' group by tag order by id desc"))
1175
		{
1176
			// load complete comments
1177
			$num=0;
1178
			foreach ($ids as $id)
1179
			{
1180
				$comment = $this->LoadSingle("select * from ".$this->config["table_prefix"]."pages where id = '".$id["id"]."' limit 1");
1181
				if (!isset($comments[$comment["comment_on"]]) && $num < $limit)
1182
				{
1183
					$comments[$comment["comment_on"]] = $comment;
1184
					$num++;
1185
				}
1186
			}
1187
 
1188
			// now load pages
1189
			if ($comments)
1190
			{
1191
				// now using these ids, load the actual pages
1192
				foreach ($comments as $comment)
1193
				{
1194
					$page = $this->LoadPage($comment["comment_on"]);
1195
					$page["comment_user"] = $comment["user"];
1196
					$page["comment_time"] = $comment["time"];
1197
					$page["comment_tag"] = $comment["tag"];
1198
					$pages[] = $page;
1199
				}
1200
			}
1201
		}
1202
		// load tags of pages
1203
		//return $this->LoadAll("select comment_on as tag, max(time) as time, tag as comment_tag, user from ".$this->config["table_prefix"]."pages where comment_on != '' group by comment_on order by time desc");
1204
		return $pages;
1205
	}
1206
 
1207
 
1208
 
1209
	// ACCESS CONTROL
1210
	// returns true if logged in user is owner of current page, or page specified in $tag
1211
	function UserIsOwner($tag = "") {
1212
		// check if user is logged in
1213
		if (!$this->GetUser()) return false;
1214
 
1215
		// set default tag
1216
		if (!$tag = trim($tag)) $tag = $this->GetPageTag();
1217
 
1218
		// check if user is owner
1219
		if ($this->GetPageOwner($tag) == $this->GetUserName()) return true;
1220
	}
1221
 
1222
	/**
1223
	 * @param string $group The name of a group
1224
	 * @return string the ACL associated with the group $gname
1225
	 * @see UserIsInGroup to check if a user belongs to some group
1226
	 */
1227
	function GetGroupACL($group)
1228
	{
1229
		if (array_key_exists($group, $this->_groupsCache))
1230
		{
1231
			return $this->_groupsCache[$group];
1232
		}
1233
		return $this->_groupsCache[$group] = $this->GetTripleValue($group, WIKINI_VOC_ACLS, GROUP_PREFIX);
1234
	}
1235
 
1236
	/**
1237
	 * Checks if a new group acl is not defined recursively
1238
	 * (this method expects that groups that are already defined are not themselves defined recursively...)
1239
	 * @param string $gname The name of the group
1240
	 * @param string $acl The new acl for that group
1241
	 * @return boolean True iff the new acl defines the group recursively
1242
	 */
1243
	function MakesGroupRecursive($gname, $acl, $origin = null, $checked = array())
1244
	{
1245
		$gname = strtolower($gname);
1246
		if ($origin === null)
1247
		{
1248
			$origin = $gname;
1249
		}
1250
		elseif ($gname === $origin)
1251
		{
1252
			return true;
1253
		}
1254
		foreach (explode("\n", $acl) as $line)
1255
		{
1256
			if (!$line) continue;
1257
			if ($line[0] == '!')
1258
			{
1259
				$line = substr($line, 1);
1260
			}
1261
			if (!$line) continue;
1262
			if ($line[0] == '@')
1263
			{
1264
				$line = substr($line, 1);
1265
				if (!in_array($line, $checked))
1266
				{
1267
					if ($this->MakesGroupRecursive($line, $this->GetGroupACL($line), $origin, $checked))
1268
					{
1269
						return true;
1270
					}
1271
				}
1272
			}
1273
		}
1274
		$checked[] = $gname;
1275
		return false;
1276
	}
1277
 
1278
	/**
1279
	 * Sets a new ACL to a given group
1280
	 * @param string $gname The name of a group
1281
	 * @param string $acl The new ACL to associate with the group $gname
1282
	 * @return int 0 if successful, a triple error code or a specific error code:
1283
	 * 	1000 if the new value would define the group recursively
1284
	 * 	1001 if $gname is not named with alphanumeric chars
1285
	 * @see GetGroupACL
1286
	 */
1287
	function SetGroupACL($gname, $acl)
1288
	{
1289
		if (preg_match('/[^A-Za-z0-9]/', $gname))
1290
		{
1291
			return 1001;
1292
		}
1293
		$old = $this->GetGroupACL($gname);
1294
		if ($this->MakesGroupRecursive($gname, $acl))
1295
		{
1296
			return 1000;
1297
		}
1298
		$this->_groupsCache[$gname] = $acl;
1299
		if ($old === null)
1300
		{
1301
			return $this->InsertTriple($gname, WIKINI_VOC_ACLS, $acl, GROUP_PREFIX);
1302
		}
1303
		elseif ($old === $acl)
1304
		{
1305
			return 0; // nothing has changed
1306
		}
1307
		else
1308
		{
1309
			return $this->UpdateTriple($gname, WIKINI_VOC_ACLS, $old, $acl, GROUP_PREFIX);
1310
		}
1311
	}
1312
	/**
1313
	 * @return array The list of all group names
1314
	 */
1315
	function GetGroupsList()
1316
	{
1317
		$res = $this->GetMatchingTriples(GROUP_PREFIX . '%', WIKINI_VOC_ACLS_URI);
1318
		$prefix_len = strlen(GROUP_PREFIX);
1319
		$list = array();
1320
		foreach ($res as $line)
1321
		{
1322
			$list[] = substr($line['resource'], $prefix_len);
1323
		}
1324
		return $list;
1325
	}
1326
 
1327
	/**
1328
	 * @param string $group The name of a group
1329
	 * @return boolean true iff the user is in the given $group
1330
	 */
1331
	function UserIsInGroup($group, $user = null, $admincheck = true)
1332
	{
1333
		return $this->CheckACL($this->GetGroupACL($group), $user, $admincheck);
1334
	}
1335
 
1336
	/**
1337
	 * Checks if a given user is andministrator
1338
	 * @param string $user The name of the user (defaults to the current user if not given)
1339
	 * @return boolean true iff the user is an administrator
1340
	 */
1341
	function UserIsAdmin($user = null)
1342
	{
1343
		return $this->UserIsInGroup(ADMIN_GROUP, $user, false);
1344
	}
1345
 
1346
	function GetPageOwner($tag = "", $time = "") { if (!$tag = trim($tag)) $tag = $this->GetPageTag(); if ($page = $this->LoadPage($tag, $time)) return $page["owner"]; }
1347
	function SetPageOwner($tag, $user) {
1348
		// check if user exists
1349
		if (!$this->LoadUser($user)) return;
1350
 
1351
		// updated latest revision with new owner
1352
		$this->Query("update ".$this->config["table_prefix"]."pages set owner = '".mysql_real_escape_string($user)."' where tag = '".mysql_real_escape_string($tag)."' and latest = 'Y' limit 1");
1353
	}
1354
	function LoadAcl($tag, $privilege, $useDefaults = 1) {
1355
		if ((!$acl = $this->LoadSingle("select * from ".$this->config["table_prefix"]."acls where page_tag = '".mysql_real_escape_string($tag)."' and privilege = '".mysql_real_escape_string($privilege)."' limit 1")) && $useDefaults)
1356
		{
1357
			$acl = array("page_tag" => $tag, "privilege" => $privilege, "list" => $this->GetConfigValue("default_".$privilege."_acl"));
1358
		}
1359
		return $acl;
1360
	}
1361
	function SaveAcl($tag, $privilege, $list) {
1362
		if ($this->LoadAcl($tag, $privilege, 0)) $this->Query("update ".$this->config["table_prefix"]."acls set list = '".mysql_real_escape_string(trim(str_replace("\r", "", $list)))."' where page_tag = '".mysql_real_escape_string($tag)."' and privilege = '".mysql_real_escape_string($privilege)."' limit 1");
1363
		else $this->Query("insert into ".$this->config["table_prefix"]."acls set list = '".mysql_real_escape_string(trim(str_replace("\r", "", $list)))."', page_tag = '".mysql_real_escape_string($tag)."', privilege = '".mysql_real_escape_string($privilege)."'");
1364
	}
1365
	// returns true if $user (defaults to current user) has access to $privilege on $page_tag (defaults to current page)
1366
	function HasAccess($privilege, $tag = "", $user = "") {
1367
		// set defaults
1368
		if (!$tag = trim($tag)) $tag = $this->GetPageTag();
1369
		if (!$user)
1370
		{
1371
			// if current user is owner, return true. owner can do anything!
1372
			if ($this->UserIsOwner($tag)) return true;
1373
			$user = $this->GetUserName();
1374
		}
1375
 
1376
		// TODO: we might want to check if a given $user (other than the current user)
1377
		// has access to a given page. If the $user is the owner of that page,
1378
		// this method might give a wrong result (because we can't check that)
1379
 
1380
		// load acl
1381
		$acl = $this->LoadAcl($tag, $privilege);
1382
 
1383
		// fine fine... now go through acl
1384
		return $this->CheckACL($acl["list"], $user);
1385
	}
1386
 
1387
	/**
1388
	 * Checks if some $user satisfies the given $acl
1389
	 * @param string $acl The acl to check, in the same format than for pages ACL's
1390
	 * @param string $user The name of the user that must satisfy the ACL. By default
1391
	 * the current remote user.
1392
	 * @return bool True if the $user satisfies the $acl, false otherwise
1393
	 */
1394
	function CheckACL($acl, $user = null, $admincheck = true)
1395
	{
1396
		if (!$user)
1397
		{
1398
			$user = $this->GetUserName();
1399
		}
1400
 
1401
		if ($admincheck && $this->UserIsAdmin($user))
1402
		{
1403
			return true;
1404
		}
1405
 
1406
 
1407
		foreach (explode("\n", $acl) as $line)
1408
		{
1409
			$line = trim($line);
1410
 
1411
			// check for inversion character "!"
1412
			if (preg_match("/^[!](.*)$/", $line, $matches))
1413
			{
1414
				$negate = 1;
1415
				$line = $matches[1];
1416
			}
1417
			else
1418
			{
1419
				$negate = 0;
1420
			}
1421
 
1422
			// if there's still anything left... lines with just a "!" don't count!
1423
			if ($line)
1424
			{
1425
				switch ($line[0])
1426
				{
1427
				case "#": // comments
1428
					break;
1429
				case "*": // everyone
1430
					return !$negate;
1431
				case "+": // registered users
1432
					if (!$this->LoadUser($user))
1433
					{
1434
						return $negate;
1435
					}
1436
					else
1437
					{
1438
						return !$negate;
1439
					}
1440
				case '@': // groups
1441
					$gname = substr($line, 1);
1442
					// paranoiac: avoid line = '@'
1443
					if ($gname && $this->UserIsInGroup($gname, $user, false /* we have allready checked if user was an admin */))
1444
					{
1445
						return !$negate;
1446
					}
1447
					break;
1448
				default: // simple user entry
1449
					if ($line == $user)
1450
					{
1451
						return !$negate;
1452
					}
1453
				}
1454
			}
1455
		}
1456
 
1457
		// tough luck.
1458
		return false;
1459
	}
1460
 
1461
	/**
1462
	 * Loads the module ACL for a certain module
1463
	 * @param string $module The name of the module
1464
	 * @param string $module_type The type of module: 'action' or 'handler'
1465
	 * @return mixed The ACL for the given module or <tt>null</tt> if no such
1466
	 * ACL was found (which should probably be interpreted as '*').
1467
	 */
1468
	function GetModuleACL($module, $module_type)
1469
	{
1470
		$module = strtolower($module);
1471
		switch ($module_type)
1472
		{
1473
		case 'action':
1474
			if (array_key_exists($module, $this->_actionsAclsCache))
1475
			{
1476
				$acl = $this->_actionsAclsCache[$module];
1477
				break;
1478
			}
1479
			$this->_actionsAclsCache[$module] = $acl = $this->GetTripleValue($module, WIKINI_VOC_ACLS, WIKINI_VOC_ACTIONS_PREFIX);
1480
			if ($acl === null)
1481
			{
1482
				$action = &$this->GetActionObject($module);
1483
				if (is_object($action))
1484
				{
1485
					return $this->_actionsAclsCache[$module] = $action->GetDefaultACL();
1486
				}
1487
			}
1488
			break;
1489
		case 'handler':
1490
			$acl = $this->GetTripleValue($module, WIKINI_VOC_ACLS, WIKINI_VOC_HANDLERS_PREFIX);
1491
			break;
1492
		default:
1493
			return null; // TODO error msg ?
1494
		}
1495
		return $acl === null ? '*' : $acl;
1496
	}
1497
 
1498
	/**
1499
	 * Sets the $acl for a given $module
1500
	 * @param string $module The name of the module
1501
	 * @param string $module_type The type of module ('action' or 'handler')
1502
	 * @param string $acl The new ACL for that module
1503
	 * @return 0 on success, > 0 on error (see InsertTriple and UpdateTriple)
1504
	 */
1505
	function SetModuleACL($module, $module_type, $acl)
1506
	{
1507
		$module = strtolower($module);
1508
		$voc_prefix = $module_type == 'action' ? WIKINI_VOC_ACTIONS_PREFIX : WIKINI_VOC_HANDLERS_PREFIX;
1509
		$old = $this->GetTripleValue($module, WIKINI_VOC_ACLS, $voc_prefix);
1510
		if ($module_type == 'action')
1511
		{
1512
			$this->_actionsAclsCache[$module] = $acl;
1513
		}
1514
		if ($old === null)
1515
		{
1516
			return $this->InsertTriple($module, WIKINI_VOC_ACLS, $acl, $voc_prefix);
1517
		}
1518
		elseif ($old === $acl)
1519
		{
1520
			return 0; // nothing has changed
1521
		}
1522
		else
1523
		{
1524
			return $this->UpdateTriple($module, WIKINI_VOC_ACLS, $old, $acl, $voc_prefix);
1525
		}
1526
	}
1527
 
1528
	/**
1529
	 * Checks if a $user satisfies the ACL to access a certain $module
1530
	 * @param string $module The name of the module to access
1531
	 * @param string $module_type The type of the module ('action' or 'handler')
1532
	 * @param string $user The name of the user. By default
1533
	 * the current remote user.
1534
	 * @return bool True if the $user has access to the given $module, false otherwise.
1535
	 */
1536
	function CheckModuleACL($module, $module_type, $user = null)
1537
	{
1538
		$acl = $this->GetModuleACL($module, $module_type);
1539
		if ($acl === null) return true; // undefined ACL means everybody has access
1540
		return $this->CheckACL($acl, $user);
1541
	}
1542
 
1543
 
1544
	// MAINTENANCE
1545
	function Maintenance() {
1546
		// purge referrers
1547
		$this->PurgeReferrers();
1548
		// purge old page revisions
1549
		$this->PurgePages();
1550
	}
1551
 
1552
 
1553
 
1554
	// THE BIG EVIL NASTY ONE!
1555
	function Run($tag, $method = "") {
1556
		if(!($this->GetMicroTime()%9)) $this->Maintenance();
1557
 
1558
		$this->ReadInterWikiConfig();
1559
 
1560
		// do our stuff!
1561
		if (!$this->method = trim($method)) $this->method = "show";
1562
		if (!$this->tag = trim($tag)) $this->Redirect($this->href("", $this->config["root_page"]));
1563
		if ((!$this->GetUser() && isset($_COOKIE["name"])) && ($user = $this->LoadUser($_COOKIE["name"], $_COOKIE["password"]))) $this->SetUser($user, $_COOKIE["remember"]);
1564
		$this->SetPage($this->LoadPage($tag, (isset($_REQUEST["time"]) ? $_REQUEST["time"] :'')));
1565
		$this->LogReferrer();
1566
 
1567
		//correction pour un support plus facile de nouveaux handlers
1568
		if ($this->CheckModuleACL($this->method, 'handler'))
1569
		{
1570
			echo $this->Method($this->method);
1571
		}
1572
		else
1573
		{
1574
			echo 'Vous ne pouvez pas acc&eacute;der &agrave; cette page par le handler sp&eacute;cifi&eacute;.';
1575
		}
1576
 
1577
		// action redirect: aucune redirection n'a eu lieu, effacer la liste des redirections pr?c?dentes
1578
		if(!empty($_SESSION['redirects'])) session_unregister('redirects');
1579
	}
1580
}
1581
 
1582
 
1583
 
1584
// stupid version check
1585
if (!isset($_REQUEST)) die('$_REQUEST[] not found. Wakka requires PHP 4.1.0 or higher!');
1586
 
1587
// workaround for the amazingly annoying magic quotes.
1588
function magicQuotesSuck(&$a)
1589
{
1590
	if (is_array($a))
1591
	{
1592
		foreach ($a as $k => $v)
1593
		{
1594
			if (is_array($v))
1595
				magicQuotesSuck($a[$k]);
1596
			else
1597
				$a[$k] = stripslashes($v);
1598
		}
1599
	}
1600
}
1601
 
1602
if (get_magic_quotes_runtime())
1603
{
1604
    // Deactivate
1605
    set_magic_quotes_runtime(false);
1606
}
1607
 
1608
if (get_magic_quotes_gpc())
1609
{
1610
	magicQuotesSuck($_POST);
1611
	magicQuotesSuck($_GET);
1612
	magicQuotesSuck($_COOKIE);
1613
}
1614
 
1615
 
1616
// default configuration values
1617
$wakkaConfig= array();
1618
$_rewrite_mode = detectRewriteMode();
1619
$wakkaDefaultConfig = array(
1620
	'wakka_version'		=> '',
1621
	'wikini_version'	=> '',
1622
	'debug'				=> 'no',
1623
	"mysql_host"		=> "localhost",
1624
	"mysql_database"	=> "wikini",
1625
	"mysql_user"		=> "wikini",
1626
	"mysql_password"	=> '',
1627
	"table_prefix"		=> "wikini_",
1628
	"root_page"			=> "PagePrincipale",
1629
	"wakka_name"		=> "MonSiteWikiNi",
1630
	"base_url"			=> computeBaseURL($_rewrite_mode),
1631
	"rewrite_mode"		=> $_rewrite_mode,
1632
	'meta_keywords'		=> '',
1633
	'meta_description'	=> '',
1634
	"action_path"		=> "actions",
1635
	"handler_path"		=> "handlers",
1636
	"header_action"		=> "header",
1637
	"footer_action"		=> "footer",
1638
	"navigation_links"		=> "DerniersChangements :: DerniersCommentaires :: ParametresUtilisateur",
1639
	"referrers_purge_time"	=> 24,
1640
	"pages_purge_time"	=> 90,
1641
	"default_write_acl"	=> "*",
1642
	"default_read_acl"	=> "*",
1643
	"default_comment_acl"	=> "*",
1644
	"preview_before_save"	=> 0,
1645
	'allow_raw_html'	=> false);
1646
unset($_rewrite_mode);
1647
 
1648
// load config
1649
if (!$configfile = GetEnv("WAKKA_CONFIG")) $configfile = "wakka.config.php";
1650
if (file_exists($configfile)) include($configfile);
1651
$wakkaConfigLocation = $configfile;
1652
$wakkaConfig = array_merge($wakkaDefaultConfig, $wakkaConfig);
1653
 
1654
// check for locking
1655
if (file_exists("locked")) {
1656
	// read password from lockfile
1657
	$lines = file("locked");
1658
	$lockpw = trim($lines[0]);
1659
 
1660
	// is authentification given?
1661
	if (isset($_SERVER["PHP_AUTH_USER"])) {
1662
		if (!(($_SERVER["PHP_AUTH_USER"] == "admin") && ($_SERVER["PHP_AUTH_PW"] == $lockpw))) {
1663
			$ask = 1;
1664
		}
1665
	} else {
1666
		$ask = 1;
1667
	}
1668
 
1669
	if ($ask) {
1670
		header("WWW-Authenticate: Basic realm=\"".$wakkaConfig["wakka_name"]." Install/Upgrade Interface\"");
1671
		header("HTTP/1.0 401 Unauthorized");
1672
		echo "Ce site est en cours de mise &agrave; jour. Veuillez essayer plus tard." ;
1673
		exit;
1674
	}
1675
}
1676
 
1677
 
1678
// compare versions, start installer if necessary
1679
if ($wakkaConfig["wakka_version"] && (!$wakkaConfig["wikini_version"])) { $wakkaConfig["wikini_version"]=$wakkaConfig["wakka_version"]; }
1680
if (($wakkaConfig["wakka_version"] != WAKKA_VERSION) || ($wakkaConfig["wikini_version"] != WIKINI_VERSION)) {
1681
	// start installer
1682
	if (!isset($_REQUEST["installAction"]) OR !$installAction = trim($_REQUEST["installAction"])) $installAction = "default";
1683
	include("setup/header.php");
1684
	if (file_exists("setup/".$installAction.".php")) include("setup/".$installAction.".php"); else echo "<i>Invalid action</i>" ;
1685
	include("setup/footer.php");
1686
	exit;
1687
}
1688
 
1689
// Check if the server is configured to automatically compress the output
1690
if (!ini_get('zlib.output_compression') && !ini_get('zlib.output_handler'))
1691
{
1692
	// Check if we can use ob_gzhandler (requires the zlib extension)
1693
	if (function_exists('ob_gzhandler'))
1694
	{
1695
		// let ob_gzhandler do the dirty job
1696
		// NB.: this must be done BEFORE session_start() when session.use_trans_sid is on
1697
		ob_start('ob_gzhandler');
1698
	}
1699
	// else lets do the dirty job by ourselves...
1700
	elseif (!empty($_SERVER['HTTP_ACCEPT_ENCODING']) && strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') && function_exists('gzencode'))
1701
	{
1702
		ob_start ('gzencode');
1703
		// Tell the browser the content is compressed with gzip
1704
		header ("Content-Encoding: gzip");
1705
	}
1706
}
1707
 
1708
// configuration du cookie de session
1709
// determine le chemin pour les cookies
1710
$a = parse_url($wakkaConfig['base_url']);
1711
$CookiePath = dirname($a['path']);
1712
// Fixe la gestion des cookie sous les OS utilisant le \ comme s?parteur de chemin
1713
$CookiePath = str_replace("\\","/",$CookiePath);
1714
// ajoute un '/' terminal sauf si on est ? la racine web
1715
if ($CookiePath != '/') $CookiePath .= '/';
1716
$a = session_get_cookie_params();
1717
session_set_cookie_params($a['lifetime'],$CookiePath);
1718
unset($a);
1719
unset($CookiePath);
1720
 
1721
// start session
1722
session_start();
1723
 
1724
// create wiki object
1725
$wiki = new Wiki($wakkaConfig);
1726
// check for database access
1727
if (!$wiki->dblink)
1728
{
1729
	echo	"<p>Pour des raisons ind&eacute;pendantes de notre volont&eacute;, ".
1730
		"le contenu de ce Wiki est temporairement inaccessible. Veuillez ".
1731
		"r&eacute;essayer ult&eacute;rieurement, merci de votre ".
1732
		"compr&eacute;hension.</p>";
1733
	// Log error (useful to find the buggy server in a load balancing platform)
1734
	trigger_error("WikiNi : DB connection failed");
1735
	exit;
1736
}
1737
 
1738
 
1739
// go!
1740
if (!isset($method)) $method='';
1741
 
1742
 
1743
// Security (quick hack)  : Check method syntax
1744
if (!(preg_match('#^[A-Za-z0-9_]*$#',$method))) {
1745
	$method='';
1746
}
1747
include('tools/prepend.php');//$wiki->Run($page, $method);
1748
?>