drastically reduce DB calls to build the menu
[diogenes.git] / include / diogenes.barrel.inc.php
CommitLineData
6855525e
JL
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
22require_once 'diogenes.page.inc.php';
23require_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 */
29class 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 // source page or pass it to a rendering plugin
217 if (is_object($render_plugin)) {
218 $content = $render_plugin->render($path);
219 } else {
220 $content = file_get_contents($path);
221 }
222
223 // apply plugin filtering
224 foreach ($active_plugins as $plugname => $plugobj) {
225 if (is_object($plugobj) && ($plugobj->type == "filter")) {
226 $content = $plugobj->filter($content);
227 }
228 }
229 $this->assign('page_content', $content);
230
231 parent::display('', $this->getTemplate($bpage->props['template']));
232 }
233
234
235 /** Return an RCS handle. */
236 function getRcs()
237 {
238 global $globals;
239 return new $globals->rcs($this,$this->alias,$_SESSION['session']->username);
240 }
241
242
243 /** Returns the master template for the current context.
244 *
245 * @param template
246 */
247 function getTemplate($template = '')
248 {
249 if ($template)
250 {
251 // we have a page-specific template, get its full path
252 $tpl = $this->templatePath($template);
253 } else if ($this->barrel->options->template) {
254 // we have default site template, get is full path
255 $tpl = $this->templatePath($this->barrel->options->template);
256 } else {
257 // fall back on the system-wide default template
258 $tpl = parent::getTemplate();
259 }
260 return $tpl;
261 }
262
263
264 /** Returns the available master templates. */
265 function getTemplates()
266 {
267 // the system-wide templates
268 $templates = parent::getTemplates();
269 $bbarrel =& $this->barrel;
270
271 // lookup templates in the template directory
272 if ($bbarrel->hasFlag('tpl') && $bbarrel->options->template_dir) {
273 $dir = $bbarrel->spool->spoolPath($bbarrel->options->template_dir);
274 $files = System::find($dir.' -maxdepth 1 -name *.tpl');
275 foreach ($files as $file)
276 $templates["barrel:".basename($file)] = "[barrel] ".basename($file);
277 }
278 return $templates;
279 }
280
281
282 /** Is the user an administrator for the current barrel ? */
283 function isAdmin() {
284 return isset($_SESSION['session']) && $_SESSION['session']->hasPerms('admin');
285 }
286
287
288 /** Build the page's "head" tag.
289 */
290 function makeHead() {
291 global $globals;
292 $bbarrel =& $this->barrel;
293
294 // site name
295 $this->assign('site', stripslashes($bbarrel->options->title));
296
297 // meta
298 array_push($this->head, '<meta name="description" content="'.stripslashes($bbarrel->options->description).'" />');
299 array_push($this->head, '<meta name="keywords" content="'.stripslashes($bbarrel->options->keywords).'" />');
300
301 // stylesheets
302 $this->sheets = array();
303 array_push($this->sheets, $this->url("common.css"));
304 if ($bbarrel->options->menu_style == 1 || $bbarrel->options->menu_style == 2)
305 array_push($this->sheets, $this->url("phplayersmenu/{$bbarrel->options->menu_theme}/style.css"));
306 array_push($this->sheets, $this->urlSite("", $globals->cssfile));
307
308 // add stylesheets to head
309 foreach ($this->sheets as $mysheet) {
310 array_push($this->head, '<link rel="stylesheet" href="'.$mysheet.'" type="text/css" />');
311 }
312 // favicon
313 if ($bbarrel->options->favicon)
314 array_push($this->head, '<link rel="icon" href="'.$this->urlSite("", $bbarrel->options->favicon).'" type="image/png" />');
315 }
316
317
318 /** Build the barrel's menu.
319 */
320 function makeMenu() {
321 global $globals;
322 $bbarrel =& $this->barrel;
323
324 // menu style & theme
325 $this->assign('menustyle', $bbarrel->options->menu_style);
326 $this->assign('menutheme', $bbarrel->options->menu_theme);
327
328 $PID = $this->curpage->props['PID'];
329
330 // build the Diogenes part of the menu
331 if (!$bbarrel->options->menu_hide_diogenes) {
332 array_push($this->menu,array(0,__("Home"),$this->urlSite(""), 1));
333 if ($this->isLogged()) {
334 array_push($this->menu, array(1,__("Logout"), "?dologout=1") );
335 array_push($this->menu, array(1,__("Preferences"), $this->urlSite("admin", "prefs")));
336 } else {
337 array_push($this->menu, array(1,__("Login"), "?doauth=1") );
338 }
339
340 if ($this->isAdmin()) {
341 array_push($this->menu, array(1, __("Administration"), $this->urlSite("admin")));
342 if ($PID)
343 array_push($this->menu, array(1, __("Page properties"), $this->urlSite("admin", "pages?dir=$PID")));
344 } elseif ($this->canedit && $PID) {
345 array_push($this->menu, array(0, __("Edit this page"), "", 1));
346 array_push($this->menu, array(1, __("Raw editor"), $this->urlSite("admin", "edit?dir=$PID&amp;file={$globals->htmlfile}")));
347 array_push($this->menu, array(1, __("HTML editor"), $this->urlSite("admin" , "compose?dir=$PID&amp;file={$globals->htmlfile}")));
348 }
349 }
350
351 // if this is an error page, we need to bail out here
352 if (!isset($this->table_menu))
353 return;
354
a92b0441
JL
355 // all menu entries from database
356 $mcache = $this->menuRead();
357
6855525e
JL
358 // try to figure out the current MID from the current PID
359 // and build filiation
360 $filiation = array();
a92b0441
JL
361 foreach ($mcache as $mid => $mentry)
362 {
363 if ($mentry['pid'] == $PID)
364 $filiation = $this->menuToRoot($mcache, $mid, $filiation);
365 }
6855525e
JL
366
367 // add the user-defined part of the menu
a92b0441 368 $this->menu = array_merge($this->menu,$this->menuRecurse($mcache,0,$filiation,0));
6855525e
JL
369 }
370
371
372 /** Return the filiation to get to the root element.
373 *
a92b0441 374 * @param mcache
6855525e
JL
375 * @param MID
376 * @param path
377 */
a92b0441 378 function menuToRoot($mcache, $MID, $path) {
6855525e
JL
379 /* add ourself to the path */
380 array_push($path,$MID);
381
382 if ($MID) {
383 /* recursion */
a92b0441 384 return $this->menuToRoot($mcache, $mcache[$MID]['parent'], $path);
6855525e
JL
385 } else {
386 /* termination */
387 return $path;
388 }
389 }
390
391
392 /** Recursively add menu entries
393 *
a92b0441 394 * @param mcache
6855525e
JL
395 * @param MIDpere
396 * @param filiation
397 * @param level
398 */
a92b0441 399 function menuRecurse($mcache, $MIDpere, $filiation, $level) {
6855525e
JL
400 // the produced output
401 $out = array();
402
a92b0441
JL
403 foreach ($mcache[$MIDpere]['children'] as $mid)
404 {
405 $mentry = $mcache[$mid];
6855525e 406// echo "pid : $pid, location : $location<br/>";
a92b0441
JL
407 $entry = htmlentities(stripslashes($mentry['title']), ENT_QUOTES);
408 if ($mentry['pid'])
409 {
410 $link = $this->urlSite($this->barrel->getLocation($mentry['pid']));
411 } else {
412 $link = $mentry['link'];
413 }
6855525e
JL
414 // decide whether this menu should be expanded
415 $expanded = ($this->barrel->options->menu_min_level == 0) ||
416 ($level+1 < $this->barrel->options->menu_min_level) ||
417 in_array($mid, $filiation);
a92b0441
JL
418 array_push($out, array($level, $entry, $link, $expanded));
419 $out = array_merge($out, $this->menuRecurse($mcache, $mid, $filiation, $level+1));
6855525e
JL
420 }
421
6855525e
JL
422 return $out;
423 }
424
425
a92b0441
JL
426 /** Read this barrel's menu entries from database.
427 */
428 function menuRead()
429 {
430 $menu = array();
431 $res = $this->dbh->query("select MID,MIDpere,title,link,PID from {$this->table_menu} order by ordre");
432 while (list($mid, $parent, $title, $link, $pid) = mysql_fetch_row($res))
433 {
434 $menu[$mid]['parent'] = $parent;
435 $menu[$mid]['title'] = $title;
436 $menu[$mid]['link'] = $link;
437 $menu[$mid]['title'] = $title;
438 $menu[$mid]['pid'] = $pid;
439 if (!is_array($menu[$mid]['children']))
440 $menu[$mid]['children'] = array();
441
442 // register this entry with its parent
443 if (!is_array($menu[$parent]['children']))
444 $menu[$parent]['children'] = array();
445 array_push($menu[$parent]['children'], $mid);
446 }
447 mysql_free_result($res);
448 return $menu;
449 }
450
451
6855525e
JL
452 /**
453 * Break down a PATH_INFO into site, page id and file
454 * Directories *must* be accessed with a final slash.
455 *
456 * @param path the path to parse
457 */
458 function parsePathInfo($path) {
459 if (empty($path) || !preg_match("/^\/([^\/]+)\/((.+)\/)?([^\/]*)$/",$path,$asplit))
460 return false;
461
462 $split['alias'] = $asplit[1];
463 $split['dir'] = isset($asplit[3]) ? $asplit[3] : "";
464 $split['file'] = isset($asplit[4]) ? $asplit[4] : "";
465 return $split;
466 }
467
468
469 /** Return the current URI.
470 */
471 function script_uri()
472 {
473 if ($this->barrel->vhost)
474 return preg_replace("/^(.*)\/site(\.php)?\/{$this->alias}\/(.*)/", "/\$3",$_SERVER['REQUEST_URI']);
475 else
476 return $_SERVER['REQUEST_URI'];
477 }
478
479
480 /** Returns the path to a given template. */
481 function templatePath($template)
482 {
483 global $globals;
484
485 $bits = split(":", $template);
486 switch ($bits[0]) {
487 case "global":
488 $path = $globals->template_dir."/". $bits[1];
489 break;
490 case "barrel":
491 $path = $this->barrel->spool->spoolPath($this->barrel->options->template_dir, $bits[1]);
492 break;
493 default:
494 $path = parent::templatePath($template);
495 }
496 return $path;
497 }
498
499
500 /** Returns the URL to one of the barrel's pages relative to
501 * the current location.
502 *
503 * @param dir
504 * @param file
505 */
506 function urlSite($dir, $file = '') {
507 global $page;
508 $tosite = strlen($this->pathinfo['dir']) ? str_repeat("../",1+substr_count($this->pathinfo['dir'],"/")) : '';
509 $url = $tosite . (strlen($dir) ? "$dir/" : "") . $file;
510 return strlen($url) ? $url : "./";
511 }
512
513}
514
515?>