more work on the plugins system
[diogenes.git] / include / diogenes.barrel.inc.php
1 <?php
2 /*
3 * Copyright (C) 2003-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21
22 require_once 'diogenes.page.inc.php';
23 require_once 'Barrel.php';
24
25 /** This class is used to display a page of a Diogenes barrel,
26 * that is an RCS-managed website with a virtual directory
27 * structure.
28 */
29 class DiogenesBarrel extends DiogenesPage
30 {
31 // barrel definition info
32
33 /** The database table holding the menus */
34 var $table_menu;
35
36 /** The barrel's alias. */
37 var $alias;
38
39 /** The Diogenes_Barrel representing the barrel */
40 var $barrel;
41
42 // context info
43 /** A Diogenes_Barrel_Page representing the current page */
44 var $curpage;
45
46 /** Information about the current location */
47 var $pathinfo;
48
49 /** Can the current user edit this page? */
50 var $canedit = false;
51
52
53 /** Constructs a Smarty-derived object to display contents within a barrel.
54 *
55 * @param $override_pathinfo
56 */
57 function DiogenesBarrel($override_pathinfo = null)
58 {
59 global $globals;
60
61 // call parent constructor
62 $this->DiogenesPage();
63
64 // break down PATH_INFO into site and location components
65 $mypathinfo = $override_pathinfo ? $override_pathinfo : $_SERVER['PATH_INFO'];
66 $this->pathinfo = $this->parsePathInfo($mypathinfo);
67 if (!$this->pathinfo)
68 $this->kill404("Invalid location specified!");
69
70 // Retrieve site-wide info from database
71 $this->barrel = new Diogenes_Barrel($this->pathinfo['alias']);
72 if (!$this->barrel->alias)
73 $this->kill404("Unknown barrel requested : {$this->pathinfo['alias']}");
74
75 // Legacy
76 $this->alias = $this->barrel->alias;
77 $this->table_menu = $this->barrel->table_menu;
78
79 // Build page head
80 $this->makeHead();
81
82 // Check the requested page exists
83 $tdir = $this->pathinfo['dir'];
84 if ($tdir != 'admin')
85 {
86 if (!$this->pathinfo['PID'] = $this->barrel->getPID($tdir))
87 {
88 $this->kill404("Unknown location specified '$tdir'!");
89 }
90 }
91 }
92
93 /** Check the user has the right permissions.
94 * Read the user's permissions for the current site from database.
95 *
96 * @param level the required permissions level
97 */
98 function checkPerms($level) {
99 global $globals;
100
101 if ($level != "public")
102 $_SESSION['session']->doAuth($this);
103
104 $_SESSION['session']->setBarrelPerms($this->alias);
105
106 if (!$_SESSION['session']->hasPerms($level))
107 $this->kill(__("You are not authorized to view this page!"), 403);
108 }
109
110
111 /** Read the contents for the current location.
112 */
113 function doContent()
114 {
115 global $globals;
116
117 // Retrieve information specific to the current page
118
119 // enable directory index
120 $file = $this->pathinfo['file'] ? $this->pathinfo['file'] : $globals->htmlfile;
121
122 // read from Db
123 if (!$bpage = Diogenes_Barrel_Page::fromDb($this->barrel, $this->pathinfo['PID']))
124 {
125 $this->kill404("Directory not found : '{$this->pathinfo['dir']}' ({$this->pathinfo['PID']}) !");
126 }
127 $this->curpage =& $bpage;
128
129 // check the permissions for the current location
130 if (!$this->pathinfo['file'] || $bpage->props['perms'] != 'public' || isset($_REQUEST['rev'])) {
131 $this->startSession();
132
133 // handle login/logout requests
134 if (isset($_REQUEST['dologout']))
135 $this->doLogout();
136 if (isset($_REQUEST['doauth']))
137 $this->checkPerms('auth');
138
139 $this->checkPerms($bpage->props['perms']);
140
141 // can we edit this page?
142 $this->canedit = $_SESSION['session']->hasPerms($bpage->props['wperms']);
143 }
144
145 // now we can display the page
146 // check the location is valid
147 if (!$this->barrel->spool->checkPath($bpage->props['PID'],$file,false))
148 $this->kill404("Malformed location!");
149
150 // check that the page is 'live'
151 switch ($bpage->props['status']) {
152 case 0:
153 break;
154 case 1:
155 $this->assign('page_content', "<p>".__("This page is currently under construction.")."<p>");
156 $this->display('');
157 exit;
158 default:
159 $this->assign('page_content', "<p>".__("This page is currently unavailable.")."<p>");
160 $this->display('');
161 exit;
162 }
163
164 // if necessary, do a checkout
165 if (isset($_REQUEST['rev'])) {
166 $rcs = $this->getRcs();
167 $path = $rcs->checkout($bpage->props['PID'],$file,$_REQUEST['rev'],System::mktemp("-d"));
168 } else {
169 $path = $this->barrel->spool->spoolPath($bpage->props['PID'],$file);
170 }
171
172 if (!is_file($path))
173 $this->kill404("File not found : $path!");
174
175 if (!$this->pathinfo['file']) {
176 // this is a page, display it within header/footer framework
177 $this->doPage($path, $bpage);
178 } else {
179 // otherwise, we send back the raw file
180 $type = get_mime_type($path);
181 if (is_mime_multipart($type)) {
182 $boundary = get_mime_boundary($path);
183 if ($boundary) $type = "$type; boundary=\"$boundary\"";
184 }
185 header("Content-Type:$type");
186 header("Content-Length:".filesize($path));
187 header("Last-modified:".gmdate("D, d M Y H:i:s T", filemtime($path)));
188 readfile($path);
189 }
190
191 }
192
193
194 /** Display a page within the header/footer framework.
195 *
196 * @param path the path of the file
197 * @param bpage a Diogenes_Barrel_Page representing the current page
198 */
199 function doPage($path, $bpage)
200 {
201 global $globals;
202
203 $this->assign('page',stripslashes($bpage->props['title']));
204
205 // load plugins
206 $this->barrel->readPlugins();
207 $active_plugins = $this->barrel->loadPlugins($bpage);
208
209 // search for rendering pluging
210 $render_plugin = '';
211 foreach ($active_plugins as $plugname => $plugobj) {
212 if (is_object($plugobj) && ($plugobj->type == "render")) {
213 $render_plugin = $plugobj;
214 }
215 }
216
217 // source page or pass it to a rendering plugin
218 if (is_object($render_plugin)) {
219 $content = $render_plugin->render($path);
220 } else {
221 $content = file_get_contents($path);
222 }
223
224 // apply plugin filtering
225 foreach ($active_plugins as $plugname => $plugobj) {
226 if (is_object($plugobj) && ($plugobj->type == "filter")) {
227 $content = $plugobj->filter($content);
228 }
229 }
230 $this->assign('page_content', $content);
231
232 parent::display('', $this->getTemplate($bpage->props['template']));
233 }
234
235
236 /** Return an RCS handle. */
237 function getRcs()
238 {
239 global $globals;
240 return new $globals->rcs($this,$this->alias,$_SESSION['session']->username);
241 }
242
243
244 /** Returns the master template for the current context.
245 *
246 * @param template
247 */
248 function getTemplate($template = '')
249 {
250 if ($template)
251 {
252 // we have a page-specific template, get its full path
253 $tpl = $this->templatePath($template);
254 } else if ($this->barrel->options->template) {
255 // we have default site template, get is full path
256 $tpl = $this->templatePath($this->barrel->options->template);
257 } else {
258 // fall back on the system-wide default template
259 $tpl = parent::getTemplate();
260 }
261 return $tpl;
262 }
263
264
265 /** Returns the available master templates. */
266 function getTemplates()
267 {
268 // the system-wide templates
269 $templates = parent::getTemplates();
270 $bbarrel =& $this->barrel;
271
272 // lookup templates in the template directory
273 if ($bbarrel->hasFlag('tpl') && $bbarrel->options->template_dir) {
274 $dir = $bbarrel->spool->spoolPath($bbarrel->options->template_dir);
275 $files = System::find($dir.' -maxdepth 1 -name *.tpl');
276 foreach ($files as $file)
277 $templates["barrel:".basename($file)] = "[barrel] ".basename($file);
278 }
279 return $templates;
280 }
281
282
283 /** Is the user an administrator for the current barrel ? */
284 function isAdmin() {
285 return isset($_SESSION['session']) && $_SESSION['session']->hasPerms('admin');
286 }
287
288
289 /** Build the page's "head" tag.
290 */
291 function makeHead() {
292 global $globals;
293 $bbarrel =& $this->barrel;
294
295 // site name
296 $this->assign('site', stripslashes($bbarrel->options->title));
297
298 // meta
299 array_push($this->head, '<meta name="description" content="'.stripslashes($bbarrel->options->description).'" />');
300 array_push($this->head, '<meta name="keywords" content="'.stripslashes($bbarrel->options->keywords).'" />');
301
302 // stylesheets
303 $this->sheets = array();
304 array_push($this->sheets, $this->url("common.css"));
305 if ($bbarrel->options->menu_style == 1 || $bbarrel->options->menu_style == 2)
306 array_push($this->sheets, $this->url("phplayersmenu/{$bbarrel->options->menu_theme}/style.css"));
307 array_push($this->sheets, $this->urlSite("", $globals->cssfile));
308
309 // add stylesheets to head
310 foreach ($this->sheets as $mysheet) {
311 array_push($this->head, '<link rel="stylesheet" href="'.$mysheet.'" type="text/css" />');
312 }
313 // favicon
314 if ($bbarrel->options->favicon)
315 array_push($this->head, '<link rel="icon" href="'.$this->urlSite("", $bbarrel->options->favicon).'" type="image/png" />');
316 }
317
318
319 /** Build the barrel's menu.
320 */
321 function makeMenu() {
322 global $globals;
323 $bbarrel =& $this->barrel;
324
325 // menu style & theme
326 $this->assign('menustyle', $bbarrel->options->menu_style);
327 $this->assign('menutheme', $bbarrel->options->menu_theme);
328
329 $PID = $this->curpage->props['PID'];
330
331 // build the Diogenes part of the menu
332 if (!$bbarrel->options->menu_hide_diogenes) {
333 array_push($this->menu,array(0,__("Home"),$this->urlSite(""), 1));
334 if ($this->isLogged()) {
335 array_push($this->menu, array(1,__("Logout"), "?dologout=1") );
336 array_push($this->menu, array(1,__("Preferences"), $this->urlSite("admin", "prefs")));
337 } else {
338 array_push($this->menu, array(1,__("Login"), "?doauth=1") );
339 }
340
341 if ($this->isAdmin()) {
342 array_push($this->menu, array(1, __("Administration"), $this->urlSite("admin")));
343 if ($PID)
344 array_push($this->menu, array(1, __("Page properties"), $this->urlSite("admin", "pages?dir=$PID")));
345 } elseif ($this->canedit && $PID) {
346 array_push($this->menu, array(0, __("Edit this page"), "", 1));
347 array_push($this->menu, array(1, __("Raw editor"), $this->urlSite("admin", "edit?dir=$PID&amp;file={$globals->htmlfile}")));
348 array_push($this->menu, array(1, __("HTML editor"), $this->urlSite("admin" , "compose?dir=$PID&amp;file={$globals->htmlfile}")));
349 }
350 }
351
352 // if this is an error page, we need to bail out here
353 if (!isset($this->table_menu))
354 return;
355
356 // try to figure out the current MID from the current PID
357 // and build filiation
358 $filiation = array();
359 $res = $this->dbh->query("select MID from {$this->table_menu} where PID='$PID'");
360 while (list($MID) = mysql_fetch_row($res))
361 $filiation = $this->menuToRoot($MID, $filiation);
362 mysql_free_result($res);
363
364 // add the user-defined part of the menu
365 $this->menu = array_merge($this->menu,$this->menuRecurse(0,$filiation,0));
366 }
367
368
369 /** Return the filiation to get to the root element.
370 *
371 * @param MID
372 * @param path
373 */
374 function menuToRoot($MID, $path) {
375 /* add ourself to the path */
376 array_push($path,$MID);
377
378 if ($MID) {
379 /* recursion */
380 $res = $this->dbh->query("select MIDpere from {$this->table_menu} where MID=$MID");
381 list($MIDpere) = mysql_fetch_row($res);
382 mysql_free_result($res);
383
384 return $this->menuToRoot($MIDpere, $path);
385 } else {
386 /* termination */
387 return $path;
388 }
389 }
390
391
392 /** Recursively add menu entries
393 *
394 * @param MIDpere
395 * @param filiation
396 * @param level
397 */
398 function menuRecurse($MIDpere, $filiation, $level) {
399 // the produced output
400 $out = array();
401
402 $res = $this->dbh->query("select m.MID,m.title,m.link,m.PID ".
403 "from {$this->table_menu} as m ".
404 "where MIDpere=$MIDpere order by ordre");
405
406 while(list($mid,$title,$link,$pid) = mysql_fetch_row($res)) {
407 $location = $this->barrel->getLocation($pid);
408 // echo "pid : $pid, location : $location<br/>";
409 $title = stripslashes($title);
410 $entry = htmlentities(stripslashes($title), ENT_QUOTES);
411 $link = $pid ? $this->urlSite($location) : $link;
412 // decide whether this menu should be expanded
413 $expanded = ($this->barrel->options->menu_min_level == 0) ||
414 ($level+1 < $this->barrel->options->menu_min_level) ||
415 in_array($mid, $filiation);
416 array_push($out, array($level, $entry, $link, $expanded));
417 $out = array_merge($out, $this->menuRecurse($mid, $filiation, $level+1));
418 }
419
420 // free MySQL result and return output
421 mysql_free_result($res);
422 return $out;
423 }
424
425
426 /**
427 * Break down a PATH_INFO into site, page id and file
428 * Directories *must* be accessed with a final slash.
429 *
430 * @param path the path to parse
431 */
432 function parsePathInfo($path) {
433 if (empty($path) || !preg_match("/^\/([^\/]+)\/((.+)\/)?([^\/]*)$/",$path,$asplit))
434 return false;
435
436 $split['alias'] = $asplit[1];
437 $split['dir'] = isset($asplit[3]) ? $asplit[3] : "";
438 $split['file'] = isset($asplit[4]) ? $asplit[4] : "";
439 return $split;
440 }
441
442
443 /** Return the current URI.
444 */
445 function script_uri()
446 {
447 if ($this->barrel->vhost)
448 return preg_replace("/^(.*)\/site(\.php)?\/{$this->alias}\/(.*)/", "/\$3",$_SERVER['REQUEST_URI']);
449 else
450 return $_SERVER['REQUEST_URI'];
451 }
452
453
454 /** Returns the path to a given template. */
455 function templatePath($template)
456 {
457 global $globals;
458
459 $bits = split(":", $template);
460 switch ($bits[0]) {
461 case "global":
462 $path = $globals->template_dir."/". $bits[1];
463 break;
464 case "barrel":
465 $path = $this->barrel->spool->spoolPath($this->barrel->options->template_dir, $bits[1]);
466 break;
467 default:
468 $path = parent::templatePath($template);
469 }
470 return $path;
471 }
472
473
474 /** Returns the URL to one of the barrel's pages relative to
475 * the current location.
476 *
477 * @param dir
478 * @param file
479 */
480 function urlSite($dir, $file = '') {
481 global $page;
482 $tosite = strlen($this->pathinfo['dir']) ? str_repeat("../",1+substr_count($this->pathinfo['dir'],"/")) : '';
483 $url = $tosite . (strlen($dir) ? "$dir/" : "") . $file;
484 return strlen($url) ? $url : "./";
485 }
486
487 }
488
489 ?>