Subversion Repositories Applications.papyrus

Rev

Rev 1087 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
831 florian 1
<?php
2
 
3
////////////////////////////////////////////////////////////////////////////////
4
//                                                                            //
5
//   Copyright (C) 2006  Phorum Development Team                              //
6
//   http://www.phorum.org                                                    //
7
//                                                                            //
8
//   This program is free software. You can redistribute it and/or modify     //
9
//   it under the terms of either the current Phorum License (viewable at     //
10
//   phorum.org) or the Phorum License that was distributed with this file    //
11
//                                                                            //
12
//   This program is distributed in the hope that it will be useful,          //
13
//   but WITHOUT ANY WARRANTY, without even the implied warranty of           //
14
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                     //
15
//                                                                            //
16
//   You should have received a copy of the Phorum License                    //
17
//   along with this program.                                                 //
18
////////////////////////////////////////////////////////////////////////////////
19
 
20
if(!defined("PHORUM")) return;
21
 
22
// For keeping track of include dependancies, which
23
// are used to let templates automatically rebuild
24
// in case an included subtemplate has been changed.
25
$include_level = 0;
26
$include_deps  = array();
27
 
28
function phorum_import_template($tplfile, $outfile)
29
{
30
    global $include_level, $include_deps;
31
    $include_level++;
32
 
33
    // Remember that we used this template.
34
    $include_deps[$tplfile] = $outfile;
35
 
36
    // In case we're handling 0 byte large files, we set $page
37
    // directly. Running fread($fp, 0) gives a PHP warning.
38
    if (filesize($tplfile)) {
39
        $fp=fopen($tplfile, "r");
40
        $page=fread($fp, filesize($tplfile));
41
        fclose($fp);
42
    } else {
43
        $page = '';
44
    }
45
 
46
    preg_match_all("/\{[\!\/A-Za-z].+?\}/s", $page, $matches);
47
 
48
    settype($oldloopvar, "string");
49
    settype($loopvar, "string");
50
    settype($olddatavar, "string");
51
    settype($datavar, "string");
52
    $loopvars = array();
53
 
54
    foreach($matches[0] as $match){
55
        unset($parts);
56
 
57
        $string=substr($match, 1, -1);
58
 
59
        $string = trim($string);
60
 
61
        // pre-parse pointer variables
62
        if(strstr($string, "->")){
63
            $string=str_replace("->", "']['", $string);
64
        }
65
 
66
        $parts=explode(" ", $string);
67
 
68
        switch(strtolower($parts[0])){
69
 
70
            // Comment
71
            case "!":
72
 
73
            $repl="<?php // ".implode(" ", $parts)." ?>";
74
            break;
75
 
76
 
77
            case "include":
78
 
79
            $repl = file_get_contents(phorum_get_template($parts[1],1));
80
            break;
81
 
82
            case "include_once":
83
 
84
            $repl="<?php include_once phorum_get_template('$parts[1]'); ?>";
85
            break;
86
 
87
            case "include_var": // include a file given by a variable
88
 
89
            $repl="<?php include_once phorum_get_template( \$PHORUM[\"DATA\"]['$parts[1]']); ?>";
90
            break;
91
 
92
            // A define is used to create vars for the engine to use.
93
            case "define":
94
 
95
            $repl="<?php \$PHORUM[\"TMP\"]['$parts[1]']='";
96
            array_shift($parts);
97
            array_shift($parts);
98
            foreach($parts as $part){
99
                $repl.=str_replace("'", "\\'", $part)." ";
100
            }
101
            $repl=trim($repl)."'; ?>";
102
            break;
103
 
104
 
105
            // A var is used to create vars for the template.
106
            case "var":
107
 
108
            $repl="<?php \$PHORUM[\"DATA\"]['$parts[1]']='";
109
            array_shift($parts);
110
            array_shift($parts);
111
            foreach($parts as $part){
112
                $repl.=str_replace("'", "\\'", $part)." ";
113
            }
114
            $repl=trim($repl)."'; ?>";
115
            break;
116
 
117
            // Run a Phorum hook. The first parameter is the name of the
118
            // hook. Other parameters will be passed on as arguments for
119
            // the hook function. On argument will be passed directly to
120
            // the hook. Multiple arguments will be passed in an array.
121
            case "hook":
122
 
123
            // Setup hook arguments.
124
            $hookargs = array();
125
            for($i = 2; !empty($parts[$i]); $i++) {
126
                // For supporting the following construct, where the
127
                // loopvar is passed to the hook in full:
128
                // {LOOP SOMELIST}
129
                //   {HOOK some_hook SOMELIST}
130
                // {/LOOP SOMELIST}
131
                if (isset($loopvars[$parts[$i]])) {
132
                    $hookargs[] = "\$PHORUM['TMP']['".addslashes($parts[$i])."']";
133
                } else {
134
                    $index = phorum_determine_index($loopvars, $parts[$i]);
135
                    $hookargs[] = "\$PHORUM['$index']['".addslashes($parts[$i])."']";
136
                }
137
            }
138
 
139
            // Build the replacement string.
140
            $repl = "<?php if(isset(\$PHORUM['hooks']['".addslashes($parts[1])."'])) phorum_hook('".addslashes($parts[1])."'";
141
            if (count($hookargs) == 1) {
142
                $repl .= "," . $hookargs[0];
143
            } elseif (count($hookargs) > 1) {
144
                $repl .= ",array(" . implode(",", $hookargs) . ")";
145
            }
146
            $repl .= ");?>";
147
            break;
148
 
149
            // starts a loop
150
            case "loop":
151
 
152
            $loopvars[$parts[1]]=true;
153
            $index=phorum_determine_index($loopvars, $parts[1]);
154
            $repl="<?php \$phorum_loopstack[] = isset(\$PHORUM['TMP']['$parts[1]']) ? \$PHORUM['TMP']['$parts[1]']:NULL; if(isset(\$PHORUM['$index']['$parts[1]']) && is_array(\$PHORUM['$index']['$parts[1]'])) foreach(\$PHORUM['$index']['$parts[1]'] as \$PHORUM['TMP']['$parts[1]']){ ?>";
155
            break;
156
 
157
 
158
            // ends a loop
159
            case "/loop":
160
 
161
            if (!isset($parts[1])) print "<h3>Template warning: Missing argument for /loop statement in file '" . htmlspecialchars($tplfile) . "'</h3>";
162
            $repl="<?php } if(isset(\$PHORUM['TMP']) && isset(\$PHORUM['TMP']['$parts[1]'])) unset(\$PHORUM['TMP']['$parts[1]']); \$phorum_loopstackitem=array_pop(\$phorum_loopstack); if (isset(\$phorum_loopstackitem)) \$PHORUM['TMP']['$parts[1]'] = \$phorum_loopstackitem;?>";
163
            unset($loopvars[$parts[1]]);
164
            break;
165
 
166
 
167
            // if and elseif are the same accept how the line starts
168
            case "if":
169
            case "elseif":
170
 
171
            // determine if or elseif
172
            $prefix = (strtolower($parts[0])=="if") ? "if" : "} elseif";
173
 
174
            // are we wanting == or !=
175
            if(strtolower($parts[1])=="not"){
176
                $operator="!=";
177
                $parts[1]=$parts[2];
178
                if(isset($parts[3])){
179
                    $parts[2]=$parts[3];
180
                    unset($parts[3]);
181
                } else {
182
                    unset($parts[2]);
183
                }
184
            } else {
185
                $operator="==";
186
            }
187
 
188
            $index=phorum_determine_index($loopvars, $parts[1]);
189
 
190
            // if there is no part 2, check that the value is set and not empty
191
            if(!isset($parts[2])){
192
                if($operator=="=="){
193
                    $repl="<?php $prefix(isset(\$PHORUM['$index']['$parts[1]']) && !empty(\$PHORUM['$index']['$parts[1]'])){ ?>";
194
                } else {
195
                    $repl="<?php $prefix(!isset(\$PHORUM['$index']['$parts[1]']) || empty(\$PHORUM['$index']['$parts[1]'])){ ?>";
196
                }
197
 
198
                // if it is numeric, a constant or a string, simply set it as is
199
            } elseif(is_numeric($parts[2]) || defined($parts[2]) || preg_match('!"[^"]*"!', $parts[2])) {
200
                $repl="<?php $prefix(isset(\$PHORUM['$index']['$parts[1]']) && \$PHORUM['$index']['$parts[1]']$operator$parts[2]){ ?>";
201
 
202
                // we must have a template var
203
            } else {
204
 
205
                $index_part2=phorum_determine_index($loopvars, $parts[2]);
206
 
207
                // this is a really complicated IF we are building.
208
 
209
                $repl="<?php $prefix(isset(\$PHORUM['$index']['$parts[1]']) && isset(\$PHORUM['$index_part2']['$parts[2]']) && \$PHORUM['$index']['$parts[1]']$operator\$PHORUM['$index_part2']['$parts[2]']) { ?>";
210
 
211
            }
212
 
213
            // reset $prefix
214
            $prefix="";
215
            break;
216
 
217
 
218
            // create an else
219
            case "else":
220
 
221
            $repl="<?php } else { ?>";
222
            break;
223
 
224
 
225
            // close an if
226
            case "/if":
227
 
228
            $repl="<?php } ?>";
229
            break;
230
 
231
            case "assign":
232
            if(defined($parts[2]) || is_numeric($parts[2])){
233
                $repl="<?php \$PHORUM[\"DATA\"]['$parts[1]']=$parts[2]; ?>";
234
            } else {
235
                $index=phorum_determine_index($loopvars, $parts[2]);
236
 
237
                $repl="<?php \$PHORUM[\"DATA\"]['$parts[1]']=\$PHORUM['$index']['$parts[2]']; ?>";
238
            }
239
            break;
240
 
241
 
242
            // this is just for echoing vars from DATA or TMP if it is a loopvar
243
            default:
244
 
245
            if(defined($parts[0])){
246
                $repl="<?php echo $parts[0]; ?>";
247
            } else {
248
 
249
                $index=phorum_determine_index($loopvars, $parts[0]);
250
 
251
                $repl="<?php echo \$PHORUM['$index']['$parts[0]']; ?>";
252
            }
253
        }
254
 
255
        $page=str_replace($match, $repl, $page);
256
    }
257
 
258
    $include_level--;
259
 
260
    // Did we finish processing our top level template? Then write out
261
    // the compiled template to the cache.
262
    //
263
    // For storing the compiled template, we use two files. The first one
264
    // has some code for checking if one of the dependant files has been
265
    // updated and for rebuilding the template if this is the case.
266
    // This one loads the second file, which is the template itself.
267
    //
268
    // This two-stage loading is needed to make sure that syntax
269
    // errors in a template file won't break the depancy checking process.
270
    // If both were in the same file, the complete file would not be run
271
    // at all and the user would have to clean out the template cache to
272
    // reload the template once it was fixed. This way user intervention
273
    // is never needed.
274
    if ($include_level == 0)
275
    {
276
        // Find the template name for the top level template.
277
        $pathparts = preg_split('[\\/]', $outfile);
278
        $fileparts = explode('-', preg_replace('/^.*\//', '', $pathparts[count($pathparts)-1]));
279
        $this_template = addslashes($fileparts[2]);
280
 
281
        // Determine first and second stage cache filenames.
282
        $stage1_file = $outfile;
283
        $fileparts[3] = "toplevel_stage2";
284
        unset($pathparts[count($pathparts)-1]);
285
        $stage2_file = implode('/', $pathparts) . '/' . implode('-', $fileparts);
286
 
287
        // Create code for automatic rebuilding of rendered templates
288
        // in case of changes. This is done by checking if one of the
289
        // templates in the dependancy list has been updated. If this
290
        // is the case, all dependant rendered subtemplates are deleted.
291
        // After that phorum_get_template() is called on the top level
292
        // template to rebuild all needed templates.
293
 
294
        $check_deps =
295
            "<?php\n" .
296
            '$mymtime = @filemtime("' . addslashes($stage1_file) . '");' . "\n" .
297
            "\$update_count = 0;\n" .
298
            "\$need_update = (\n";
299
        foreach ($include_deps as $tpl => $out) {
300
            $qtpl = addslashes($tpl);
301
            $check_deps .= "    @filemtime(\"$qtpl\") > \$mymtime ||\n";
302
        }
303
        $check_deps = substr($check_deps, 0, -4); // strip trailing " ||\n"
304
        $check_deps .=
305
        "\n" .
306
        ");\n" .
307
        "if (\$need_update) {\n";
308
        foreach ($include_deps as $tpl => $out) {
309
            $qout = addslashes($out);
310
            $check_deps .= "    @unlink(\"$qout\");\n";
311
        }
312
        $check_deps .=
313
        "    \$tplfile = phorum_get_template(\"$this_template\");\n" .
314
        "}\n" .
315
        "include(\"" . addslashes($stage2_file) . "\");\n" .
316
        "?>\n";
317
 
318
        // Reset dependancy list for the next phorum_import_template() call.
319
        $include_deps = array();
320
 
321
        // Write out data to the cache.
322
        phorum_write_templatefile($stage1_file, $check_deps);
323
        phorum_write_templatefile($stage2_file, $page, true);
324
    }
325
    else
326
    {
327
        // Write out subtemplate to the cache.
328
        phorum_write_templatefile($outfile, $page);
329
    }
330
 
331
 
332
}
333
 
334
function phorum_write_templatefile($filename, $content, $is_toplevel = false)
335
{
336
    if($fp=fopen($filename, "w")) {
337
        fputs($fp, "<?php if(!defined(\"PHORUM\")) return; ?>\n");
338
        if ($is_toplevel) {
339
            fputs($fp, "<?php \$phorum_loopstack = array() ?>\n");
340
        }
341
        fputs($fp, $content);
342
        if (! fclose($fp)) {
343
            die("Error on closing $filename. Is your disk full?");
344
        }
345
        // Some very unusual thing might happen. On Windows2000 we have seen
346
        // that the webserver can write a message to the cache directory,
347
        // but that it cannot read it afterwards. Probably due to
348
        // specific NTFS file permission settings. So here we have to make
349
        // sure that we can open the file that we just wrote.
350
        $checkfp = fopen($filename, "r");
351
        if (! $checkfp) {
352
            die("Failed to write a usable compiled template to $filename. " .
353
                "The file was was created successfully, but it could not " .
354
                "be read by the webserver afterwards. This is probably " .
355
                "caused by the file permissions on your cache directory.");
356
        }
357
        fclose($checkfp);
358
    } else {
359
        die("Failed to write a compiled template to $filename. This is " .
360
            "probably caused by the file permissions on your cache " .
361
            "directory.");
362
    }
363
}
364
 
365
function phorum_determine_index($loopvars, $varname)
366
{
367
    if(isset($loopvars) && count($loopvars)){
368
        while(strstr($varname, "]")){
369
            $varname=substr($varname, 0, strrpos($varname, "]")-1);
370
            if(isset($loopvars[$varname])){
371
                return "TMP";
372
                break;
373
            }
374
        }
375
    }
376
 
377
    return "DATA";
378
}
379
 
380
?>