cf0056b1fa8dedc7e21ac07f153b697bcc1d335f
[atutor.git] / mods / wiki / plugins / db / flat_files.php
1 <?php
2
3 #  This is a replacement for the ewiki.php internal MySQL database access
4 #  interface; this one saves all WikiPages in so called "flat files", and
5 #  there are now two different formats you can choose from:
6 #    * rfc822-style (or say message/http like),
7 #      which leads to files you can edit with any available text editor
8 #    * in a compressed and faster 'binary' format,
9 #      which supports more functionality (hit counting)
10 #      enable with EWIKI_DB_FAST_FILES set to 1
11 #  As this plugin can read both, you are free to switch at any time.
12 #
13 #  To enable it, just include() this plugin __before__ the main/core
14 #  ewiki.php script using:
15 #
16 #       include("plugins/db/flat_files.php");
17 #
18 #  If you only will use the file database, you could go to the bottom of the
19 #  "ewiki.php" script and replace the 'ewiki_database_mysql' class with the
20 #  one defined herein. Then make also sure, that the initialization code knows
21 #  about it (there is a class name reference in $ewiki_plugins["database"]).
22 #
23 #  db_flat_files
24 #  -------------
25 #  The config option EWIKI_DBFILES_DIRECTORY must point to a directory
26 #  allowing write access for www-data (the user id, under which webservers
27 #  run usually), use 'chmod 757 dirname/' (from ftp or shell) to achieve this
28 #
29 #  db_fast_files
30 #  -------------
31 #  Some versions of PHP and zlib do not work correctly under Win32, so
32 #  you should disable it either in the php.ini, or via .htaccess:
33 #    php_option disable_functions "gzopen gzread gzwrite gzseek gzclose"
34 #  You need the plugins/db/fakezlib.php script for very old PHP versions.
35 #
36 #  db_fast_files` code was contributed_by("Carsten Senf <ewiki@csenf.de>");
37
38
39 #-- choose flat file format
40 define("EWIKI_DB_FAST_FILES", 0);
41 define("EWIKI_DBFF_ACCURATE", 0);
42
43
44 #-- plugin registration
45 $ewiki_plugins["database"][0] = "ewiki_database_files";
46
47
48 #-- backend
49 class ewiki_database_files {
50
51    function ewiki_database_files() {
52    }
53
54
55    function GET($id, $version=false) {
56
57       if (!$version && !($version = $this->LASTVER($id))) {
58          return;
59       }
60       #-- read file      
61       $dbfile = $this->FN("$id.$version");
62       if ($f = @gzopen($dbfile, "rb")) {
63          $dat = gzread($f, 1<<21);
64          gzclose($f);
65       }
66
67       #-- decode      
68       if ($dat && (substr($dat, 0, 2) == "a:")) {
69          $r = unserialize($dat);
70       }
71       if (empty($r)) {
72          $r = array();
73          $p = strpos($dat, "\012\015\012");
74          $p2 = strpos($dat, "\012\012");
75          if ((!$p2) || ($p) && ($p < $p2)) {
76             $p = $p + 3;
77          }
78          else {
79             $p = $p2 + 2;
80          }
81          $r["content"] = substr($dat, $p);
82          $dat = substr($dat, 0, $p);
83
84          foreach (explode("\012", $dat) as $h) {
85             if ($h = trim($h)) {
86                $r[trim(strtok($h, ":"))] = str_replace(EWIKI_DBFILES_NLR, "\n", trim(strtok("\000")));
87             }
88          }
89       }
90       return($r);
91    }
92
93
94    function WRITE($hash, $overwrite=0) {
95
96       #-- which file
97       $dbfile = $this->FN($hash["id"].".".$hash["version"]);
98       if (!$overwrite && file_exists($dbfile)) {
99          return(0);
100       }
101
102       #-- write
103       if (EWIKI_DB_FAST_FILES) {
104          $val = serialize($hash);
105          if (($f = gzopen($dbfile, "wb".EWIKI_DBFILES_GZLEVEL))) {
106             gzwrite($f, $val);
107             gzclose($f);
108          }
109          return(1);
110       }
111       else {
112          $headers = "";
113          foreach ($hash as $hn=>$hv) if ($hn != "content") {
114             $headers .= $hn . ": " . str_replace("\n", EWIKI_DBFILES_NLR, $hv) . "\015\012";
115          }
116          if ($f = fopen($dbfile, "wb")) {
117             flock($f, LOCK_EX);
118             fputs($f, $headers . "\015\012" . $hash["content"]);
119             flock($f, LOCK_UN);
120             fclose($f);
121             return(1);
122          }
123       }
124    }
125
126
127    function HIT($id) {
128       if (EWIKI_DB_FAST_FILES) {
129          $dbfile = $this->FN("$id.1");
130          if ($f = gzopen($dbfile, "rb")) {
131             $r = unserialize(gzread($ff, 1<<21));
132             gzclose($f);
133             if ($r) {
134                $r["hits"] += 1;
135                if ($f = gzopen($dbfile, "wb".EWIKI_DBFILES_GZLEVEL)) {
136                   gzwrite($fp, serialize($r));
137                   gzclose($fp);
138       }  }  }  }
139    }
140    
141    
142    function FIND($list) {
143       $existing = array_flip($this->ALLFILES());
144       $r = array();
145       foreach ($list as $id) {
146          $dbfile = $this->FN($id, 0);
147          $r[$id] = isset($existing[$dbfile]) ?1:0;
148          if (EWIKI_DBFF_ACCURATE && $r[$id] && strpos($id, "://")) {
149             $uu = $this->GET($id);
150             if ($uu["meta"]) {
151                $r[$id] = unserialize($uu["meta"]);
152                $r[$id]["flags"] = $uu["flags"];
153             } else {
154                $r[$id] = $uu["flags"];
155             } 
156          }
157       }
158       return($r);
159    }
160
161
162    function GETALL($fields, $mask=0, $filter=0) {
163       $r = new ewiki_dbquery_result($fields);
164       foreach ($this->ALLFILES() as $id) {
165          $r->entries[] = $id;
166       }
167       return($r);
168    }
169
170    
171    function SEARCH($field, $content, $ci="i", $regex=0, $mask=0, $filter=0) {
172       $r = new ewiki_dbquery_result(array($field));
173       $strsearch = $ci ? "stristr" : "strpos";
174       foreach ($this->ALLFILES() as $id) {
175          $row = $this->GET($id);
176          if ($mask && ($filter == $row["flags"] & $mask)) {
177             continue;
178          }
179          $match = 
180             !$regex && ($strsearch($row[$field], $content)!==false)
181             || $regex && preg_match("\007$content\007$ci", $row[$field]);
182          if ($match) {
183             $r->add($row);
184          }
185       }
186       return($r);
187    }
188    
189    
190    function DELETE($id, $version) {
191       $fn = $this->FN("$id.$version");
192       @unlink($fn);
193    }
194
195    function INIT() {
196       if (!is_writeable(EWIKI_DBFILES_DIRECTORY) || !is_dir(EWIKI_DBFILES_DIRECTORY)) {
197          mkdir(EWIKI_DBFILES_DIRECTORY)
198          or die("db_flat_files: »database« directory '".EWIKI_DBFILES_DIRECTORY."' is not writeable!\n");
199       }
200    }
201
202
203
204    #-- db plugin internal ---------------------------------------------- 
205
206    function FN($id, $prepend_path=1) {
207       $fn = EWIKI_DBFILES_ENCODE ? urlencode($id) : strtr($id, '/:', '\\:');
208       if ($prepend_path) {
209          $fn = EWIKI_DBFILES_DIRECTORY.DIRECTORY_SEPARATOR . $fn;
210       }
211       return($fn);
212    }
213
214
215    function ID($fn) {
216       $id = EWIKI_DBFILES_ENCODE ? urldecode($fn) : strtr($fn, '\\:', '/:');
217       return($id);
218    }
219
220     
221    function LASTVER($id) {
222       $find = $this->FN($id, 0);
223       $find_n = strlen($find);
224       $n = 0;
225       if ($find_n) {
226          $dh = opendir(EWIKI_DBFILES_DIRECTORY);
227          while ($fn = readdir($dh)) {
228             if ( (strpos($fn, $find) === 0) &&     //@FIXME: empty delimiter
229                  ($dot = strrpos($fn, ".")) && ($dot == $find_n) &&
230                  ($uu = substr($fn, ++$dot)) && ($uu > $n)  )
231             {
232                $n = $uu;
233             }
234       }  }
235       return($n);
236    }
237
238
239    function ALLFILES() {
240       $r = array();
241       $dh = opendir(EWIKI_DBFILES_DIRECTORY);
242       $n = 0;
243       while ($fn = readdir($dh)) {
244          if (is_file(EWIKI_DBFILES_DIRECTORY . "/" . $fn)) {
245             $id = $this->ID($fn);
246             if (($dot = strrpos($id, ".")) && (substr($id, $dot+1) >= 1)) {
247                $file = substr($id, 0, $dot);
248                $r[] = $file;
249             }
250             if ($n++ > 1000) {
251                $n = 0;
252                $r = array_unique($r);
253             }
254          }
255       }
256       closedir($dh);
257       $r = array_unique($r);
258       return($r);
259    }
260    
261
262 } // end of class ewiki_database_files
263
264 ?>