* fixed - make the FileList plugin generate valid XHTML
[diogenes.git] / include / Barrel.php
1 <?php
2 /*
3 * Copyright (C) 2003-2004 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 require_once 'Barrel/Page.php';
22 require_once 'Barrel/Options.php';
23 require_once 'diogenes/diogenes.flagset.inc.php';
24
25
26 /** This class describes a Diogenes Barrel.
27 */
28 class Diogenes_Barrel
29 {
30 /** The barrel's alias. */
31 var $alias;
32
33 /** The database table holding the menus */
34 var $table_menu;
35
36 /** The database table holding the pages */
37 var $table_page;
38
39 /** The site's flags. */
40 var $flags;
41
42 /** The site's options. */
43 var $options;
44
45 /** Cache of the homepage ID */
46 var $homepage;
47
48 /** Handle to the spool */
49 var $spool;
50
51 /** If the barrel is running on a virtualhost. */
52 var $vhost;
53
54 /** Cache of tree */
55 var $treeCache;
56
57 /** File containing the tree cache. */
58 var $treeCacheFile;
59
60 /** Cache of the plugins */
61 var $pluginsCache;
62
63 /** File containing the plugin cache. */
64 var $pluginsCacheFile;
65
66
67 /** Construct a Diogenes Barrel.
68 *
69 * @param $alias
70 */
71 function Diogenes_Barrel($alias)
72 {
73 global $globals;
74 $webdav = '';
75
76 // Retrieve site-wide info from database
77 $res = $globals->db->query("select alias,vhost,flags from diogenes_site where alias='$alias'");
78 if (!list($this->alias,$this->vhost,$flags) = mysql_fetch_row($res)) {
79 return;
80 }
81 mysql_free_result($res);
82
83 $this->table_menu = "{$this->alias}_menu";
84 $this->table_page = "{$this->alias}_page";
85 $this->treeCacheFile = $globals->spoolroot."/diogenes_c/". $this->alias.".tree";
86 $this->pluginsCacheFile = $globals->plugins->cacheFile($this->alias);
87
88 $this->flags = new flagset($flags);
89 $this->options = new Diogenes_Barrel_Options($this->alias);
90 $this->spool = new DiogenesSpool($this,$this->alias);
91
92 $this->readTree();
93 }
94
95
96 /** Create a new Diogenes barrel. This creates the database, RCS and
97 * spool entries for the new barrel.
98 *
99 * @param $alias
100 * @param $caller
101 */
102 function create($alias, &$caller)
103 {
104 global $globals;
105
106 /* sanity check */
107 if (!preg_match("/^[a-zA-Z0-9_]+$/",$alias) or
108 in_array($alias, $globals->invalidaliases))
109 {
110 $caller->info("Invalid barrel name!");
111 return;
112 }
113
114 $res = $globals->db->query("select alias from diogenes_site where alias='$alias'");
115 if (mysql_num_rows($res) > 0) {
116 $caller->info("Entry '{$alias}' already exists in table 'diogenes_site'!");
117 return;
118 }
119
120 if (file_exists("{$globals->rcsroot}/$alias")) {
121 $caller->info("Directory '{$globals->rcsroot}/$alias' already exists!");
122 return;
123 }
124
125 if (!is_dir($globals->rcsroot) || !is_writable($globals->rcsroot)) {
126 $caller->info("Directory '{$globals->rcsroot}' is not writable!");
127 return;
128 }
129
130 /* log this event */
131 $caller->log("barrel_create","$alias:*");
132
133 /* create DB entry */
134 $globals->db->query("insert into diogenes_site set alias='$alias'");
135
136 $globals->db->query("CREATE TABLE {$alias}_menu ("
137 . "MID int(10) unsigned NOT NULL auto_increment,"
138 . "MIDpere int(10) unsigned NOT NULL,"
139 . "ordre int(10) unsigned NOT NULL,"
140 . "title tinytext NOT NULL,"
141 . "link text NOT NULL,"
142 . "PID int(10) unsigned NOT NULL,"
143 . "PRIMARY KEY (MID)"
144 . ") TYPE=MyISAM;");
145
146 $globals->db->query("CREATE TABLE {$alias}_page ("
147 . "PID int(10) unsigned NOT NULL auto_increment,"
148 . "parent INT( 10 ) UNSIGNED NOT NULL default '0',"
149 . "location tinytext NOT NULL,"
150 . "title tinytext NOT NULL,"
151 . "status tinyint(1) unsigned NOT NULL default '0',"
152 . "perms enum('public','auth','user','admin','forbidden') NOT NULL default 'public',"
153 . "wperms enum('public','auth','user','admin','forbidden') NOT NULL default 'admin',"
154 . "template varchar(255) NOT NULL,"
155 . "PRIMARY KEY (PID)"
156 . ") TYPE=MyISAM;");
157
158 /* set the barrel's title */
159 $opt = new Diogenes_Barrel_Options($alias);
160 $opt->updateOption('title',$alias);
161
162 /* create entry for the homepage */
163 $globals->db->query("insert into {$alias}_page set location='temp'");
164 $homepage = mysql_insert_id();
165 $globals->db->query("update {$alias}_page set location='',title='Home',perms='public' where PID='$homepage'");
166
167 /* create home page & copy CSS template */
168 $rcs = new $globals->rcs($caller,$alias,$_SESSION['session']->username,true);
169 $rcs->newdir("",$homepage);
170 $rcs->commit($homepage,$globals->htmlfile,"");
171 $rcs->commit($homepage,$globals->cssfile,
172 file_get_contents("{$globals->root}/{$globals->cssfile}") );
173 }
174
175
176 /** Destroy a Diogenes barrel. This removes the related database, RCS
177 * and spool entries.
178 *
179 * @param $alias
180 * @param $caller
181 */
182 function destroy($alias, &$caller) {
183 global $globals;
184
185 /** Sanity check */
186 if (!$alias) {
187 $caller->info("Empty alias supplied!");
188 return;
189 }
190
191 /* log this event */
192 $caller->log("barrel_delete","$alias:*");
193
194 system(escapeshellcmd("rm -rf ".escapeshellarg("{$globals->spoolroot}/$alias")));
195 system(escapeshellcmd("rm -rf ".escapeshellarg("{$globals->rcsroot}/$alias")));
196 system(escapeshellcmd("rm -f ".escapeshellarg("{$globals->spoolroot}/diogenes_c/$alias.tree")));
197 system(escapeshellcmd("rm -f ".escapeshellarg($globals->plugins->cacheFile($alias))));
198 $globals->db->query("drop table {$alias}_menu");
199 $globals->db->query("drop table {$alias}_page");
200 $globals->db->query("delete from diogenes_perm where alias='$alias'");
201 $globals->db->query("delete from diogenes_site where alias='$alias'");
202 $globals->db->query("delete from diogenes_option where barrel='$alias'");
203 $globals->db->query("delete from diogenes_plugin where barrel='$alias'");
204 }
205
206
207 /** Return the location corresponding to a given page ID
208 *
209 * @param $PID
210 */
211 function getLocation($PID)
212 {
213 return array_search($PID, $this->treeCache);
214 }
215
216
217
218 /** Return all the barrel's pages.
219 */
220 function getPages()
221 {
222 global $globals;
223 $bpages = array();
224
225 $res = $globals->db->query("select * from {$this->table_page}");
226 while ($props = mysql_fetch_assoc($res)) {
227 $bpages[$props['PID']] = new Diogenes_Barrel_Page($this, $props);
228 }
229 mysql_free_result($res);
230 return $bpages;
231 }
232
233
234 /** Return the page ID matching a given directory location
235 *
236 * @param $dir
237 */
238 function getPID($dir)
239 {
240 if (isset($this->treeCache[$dir]))
241 return $this->treeCache[$dir];
242 else
243 return;
244 }
245
246
247 /** List the plugins that are active in a given context
248 *
249 * @param $page
250 */
251 function getPlugins($page = 0)
252 {
253 $plugins = array();
254 foreach ($this->pluginsCache as $plug)
255 {
256 if ($plug['page'] == $page)
257 {
258 array_push($plugins, $plug);
259 }
260 }
261 return $plugins;
262 }
263
264
265 /** Check whether the barrel has a given flag
266 *
267 * @param $flag
268 */
269 function hasFlag($flag)
270 {
271 return $this->flags->hasFlag($flag);
272 }
273
274
275 /** Compile the directory tree
276 */
277 function compileTree()
278 {
279 global $globals;
280
281 if (!$fp = fopen($this->treeCacheFile, "w")) {
282 trigger_error("failed to open '{$this->treeCacheFile}' for writing", E_USER_ERROR);
283 return;
284 }
285
286 // load all the pages
287 $res = $globals->db->query("select * from {$this->table_page}");
288 $tpages = array();
289 while ($props = mysql_fetch_assoc($res))
290 {
291 $tpage = new Diogenes_Barrel_Page($this, $props);
292 $tpages[$props['PID']] = $tpage;
293 if (!strlen($props['location']))
294 {
295 $homepage = $props['PID'];
296 }
297 }
298
299 // recursively build the tree, starting at the homepage
300 $str = $this->compilePageTree($tpages, $homepage, '');
301 fputs($fp, $str);
302 fclose($fp);
303 }
304
305
306 /** Compile a single page
307 */
308 function compilePageTree(&$tpages, $PID, $ploc)
309 {
310 global $globals;
311
312 $cpage = $tpages[$PID];
313 $ploc = ($ploc ? "$ploc/" : "") . $cpage->props['location'];
314
315 // add this page
316 $out = "$ploc\t$PID\t".$cpage->props['parent']."\n";
317
318 // add children
319 $res = $globals->db->query("select PID from {$this->table_page} where parent='$PID'");
320 while (list($child) = mysql_fetch_row($res))
321 {
322 $out .= $this->compilePageTree($tpages, $child, $ploc);
323 }
324 mysql_free_result($res);
325 return $out;
326 }
327
328
329 /** Load all plugins for the specified page.
330 *
331 * @param $bpage
332 */
333 function loadPlugins(&$bpage)
334 {
335 global $globals;
336
337 $plugins = $this->getPlugins($bpage->props['PID']);
338
339 $loaded = array();
340 foreach ($plugins as $plugentry)
341 {
342 $loaded[$plugentry['plugin']] =& $globals->plugins->load($plugentry);
343 }
344 return $loaded;
345 }
346
347
348 /** Read the compiled plugin cache
349 */
350 function readPlugins()
351 {
352 global $globals;
353
354 $this->pluginsCache = $globals->plugins->readCache($this->pluginsCacheFile, $this->alias);
355 }
356
357
358 /** Read the compiled directory tree
359 */
360 function readTree()
361 {
362 global $globals;
363
364 // if the tree cache does not exits, try to init it
365 if (!file_exists($this->treeCacheFile)) {
366 $this->compileTree();
367 }
368
369 if (!$fp = fopen($this->treeCacheFile, "r")) {
370 trigger_error("failed to open '{$this->treeCacheFile}' for reading", E_USER_ERROR);
371 return;
372 }
373
374 $locations = array();
375 while ($line = fgets($fp))
376 {
377 $line = substr($line, 0, -1);
378 $bits = explode("\t", $line);
379 list($loc, $pid, $parent) = $bits;
380 $locations[$loc] = $pid;
381 }
382 fclose($fp);
383
384 $this->treeCache = $locations;
385 }
386
387 }