source: trunk/lib/PEdit.inc.php @ 301

Last change on this file since 301 was 301, checked in by quinn, 16 years ago

Added minor functinoality to Upload. Fixed (?) XML serialier config in PEdit.

File size: 26.5 KB
Line 
1<?php
2/**
3 * PEdit.inc.php
4 * code by strangecode :: www.strangecode.com :: this document contains copyrighted information
5 *
6 * PEdit provides a mechanism to store text in php variables
7 * which will be printed to the client browser under normal
8 * circumstances, but an authenticated user can 'edit' the document--
9 * data stored in vars will be shown in html form elements to be editied
10 * and saved. Posted data is stored in XML format in a specified data dir.
11 * A copy of the previous version is saved with the unix
12 * timestamp as part of the filename. This allows reverting to previous versions.
13 *
14 * To use, include this file, initialize variables,
15 * and call printing/editing functions where you want data and forms to
16 * show up.
17 *
18 * @author  Quinn Comendant <quinn@strangecode.com>
19 * @concept Beau Smith <beau@beausmith.com>
20 * @version 2.0
21 *
22 * Example of use:
23 
24 // Initialize PEdit object.
25 require_once 'codebase/lib/PEdit.inc.php';
26 $pedit = new PEdit(array(
27     'data_dir' => COMMON_BASE . '/html/_pedit_data',
28     'authorized' => true,
29 ));
30 
31 // Setup content data types.
32 $pedit->set('title');
33 $pedit->set('content', array('type' => 'textarea'));
34 
35 // After setting all parameters and data, load the data.
36 $pedit->start();
37 
38 // Print content.
39 echo $pedit->get('title');
40 echo $pedit->get('content');
41 
42 // Print additional PEdit functionality.
43 $pedit->formBegin();
44 $pedit->printAllForms();
45 $pedit->printVersions();
46 $pedit->formEnd();
47
48 */
49class PEdit {
50
51    // PEdit object parameters.
52    var $_params = array(
53        'data_dir' => '',
54        'character_set' => 'utf-8',
55        'versions_min_qty' => 20,
56        'versions_min_days' => 10,
57    );
58
59    var $_data = array(); // Array to store loaded data.
60    var $_data_file = ''; // Full file path to the pedit data file.
61    var $_authorized = false; // User is authenticated to see extended functions.
62    var $_data_loaded = false;
63    var $op = '';
64
65    /**
66     * Constructs a new PEdit object. Initializes what file is being operated with
67     * (PHP_SELF) and what that operation is. The two
68     * operations that actually modify data (save, restore) are treated differently
69     * than view operations (versions, view, default). They die redirect so you see
70     * the page you just modified.
71     *
72     * @access public
73     * @param optional array $params  A hash containing connection parameters.
74     */
75    function PEdit($params)
76    {
77        $this->setParam($params);
78       
79        if ($this->getParam('authorized') === true) {
80            $this->_authorized = true;
81        }
82       
83        // Setup PEAR XML libraries.
84        require_once 'XML/Serializer.php';
85        $this->xml_serializer =& new XML_Serializer(array(
86            XML_SERIALIZER_OPTION_INDENT => '',
87            XML_SERIALIZER_OPTION_LINEBREAKS => '',
88            XML_SERIALIZER_OPTION_RETURN_RESULT => true,
89            XML_SERIALIZER_OPTION_TYPEHINTS => true,
90        ));
91        require_once 'XML/Unserializer.php';
92        $this->xml_unserializer =& new XML_Unserializer(array(
93            XML_UNSERIALIZER_OPTION_COMPLEXTYPE => 'array',
94        ));
95    }
96   
97    /**
98     * Set (or overwrite existing) parameters by passing an array of new parameters.
99     *
100     * @access public
101     * @param  array    $params     Array of parameters (key => val pairs).
102     */
103    function setParam($params)
104    {
105        $app =& App::getInstance();
106
107        if (isset($params) && is_array($params)) {
108            // Merge new parameters with old overriding only those passed.
109            $this->_params = array_merge($this->_params, $params);
110        } else {
111            $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_WARNING, __FILE__, __LINE__);
112        }
113    }
114
115    /**
116     * Return the value of a parameter, if it exists.
117     *
118     * @access public
119     * @param string $param        Which parameter to return.
120     * @return mixed               Configured parameter value.
121     */
122    function getParam($param)
123    {
124        $app =& App::getInstance();
125   
126        if (isset($this->_params[$param])) {
127            return $this->_params[$param];
128        } else {
129            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
130            return null;
131        }
132    }
133   
134    /*
135    * Load the pedit data and run automatic functions.
136    *
137    * @access   public
138    * @author   Quinn Comendant <quinn@strangecode.com>
139    * @since    12 Apr 2006 12:43:47
140    */
141    function start($initialize_data_file=false)
142    {
143        $app =& App::getInstance();
144
145        if (!is_dir($this->getParam('data_dir'))) {
146            trigger_error(sprintf('PEdit data directory not found: %s', $this->getParam('data_dir')), E_USER_WARNING);
147        }
148       
149        // The location of the data file. (i.e.: "COMMON_DIR/html/_pedit_data/news/index.xml")
150        $this->_data_file = sprintf('%s%s.xml', $this->getParam('data_dir'), $_SERVER['PHP_SELF']);
151
152        // op is used throughout the script to determine state.
153        $this->op = getFormData('op');
154
155        // Automatic functions based on state.
156        switch ($this->op) {
157        case 'Save' :
158            if ($this->_writeData()) {
159                $app->dieURL($_SERVER['PHP_SELF']);
160            }
161            break;
162        case 'Restore' :
163            if ($this->_restoreVersion(getFormData('version'))) {
164                $app->dieURL($_SERVER['PHP_SELF']);
165            }
166            break;
167        case 'View' :
168            $this->_data_file = sprintf('%s%s__%s.xml', $this->getParam('data_dir'), $_SERVER['PHP_SELF'], getFormData('version'));
169            $app->raiseMsg(sprintf(_("This is <em><strong>only a preview</strong></em> of version %s."), getFormData('version')), MSG_NOTICE, __FILE__, __LINE__);
170            break;
171        }
172       
173        // Load data.
174        $this->_loadDataFile();
175
176        if ($initialize_data_file === true) {
177            $this->_createVersion();
178            $this->_initializeDataFile();
179        }
180    }
181
182    /**
183     * Stores a variable in the pedit data array with the content name, and type of form.
184     *
185     * @access public
186     *
187     * @param string    $content         The variable containing the text to store.
188     * @param array     $options         Additional options to store with this data.
189     */
190    function set($name, $options=array())
191    {
192        $app =& App::getInstance();
193
194        $name = preg_replace('/\s/', '_', $name);
195        if (!isset($this->_data[$name])) {
196            $this->_data[$name] = array_merge(array('content' => ''), $options);
197        } else {
198            $app->logMsg(sprintf('Duplicate set data: %s', $name), LOG_NOTICE, __FILE__, __LINE__);
199        }
200    }
201
202    /**
203     * Returns the contents of a data variable. The variable must first be 'set'.
204     *
205     * @access public
206     * @param string $name   The name of the variable to return.
207     * @return string        The trimmed content of the named data.
208     */
209    function get($name)
210    {
211        $name = preg_replace('/\s/', '_', $name);
212        if ($this->op != 'Edit' && $this->op != 'Versions' && isset($this->_data[$name]['content'])) {
213            return $this->_data[$name]['content'];
214        } else {
215            return '';
216        }
217    }
218
219    /**
220     * Prints the beginning <form> HTML tag, as well as hidden input forms.
221     *
222     * @return bool  False if unauthorized or current page is a version.
223     */
224    function formBegin()
225    {
226        $app =& App::getInstance();
227
228        if (!$this->_authorized || empty($this->_data)) {
229            return false;
230        }
231        ?>       
232        <form action="<?php echo oTxt($_SERVER['PHP_SELF']); ?>" method="post" id="sc-pedit-form">
233        <input type="hidden" name="filename" value="<?php echo oTxt($_SERVER['PHP_SELF']); ?>" />
234        <input type="hidden" name="file_hash" value="<?php echo $this->_fileHash(); ?>" />
235        <?php
236        $app->printHiddenSession();
237        switch ($this->op) {
238        case 'Edit' :
239            ?>
240            <div class="sc-pedit-buttons">
241                <input type="submit" name="op" value="<?php echo _("Save"); ?>" title="<?php echo preg_replace('/^(\w)/i', '($1)', _("Save"))?>" accesskey="<?php echo mb_substr(_("Save"), 0, 1) ?>" />
242                <input type="submit" name="op" value="<?php echo _("Cancel"); ?>" title="<?php echo preg_replace('/^(\w)/i', '($1)', _("Cancel"))?>" accesskey="<?php echo mb_substr(_("Cancel"), 0, 1) ?>" />
243            </div>
244            <?php
245            break;
246        case 'View' :
247            ?>
248            <input type="hidden" name="version" value="<?php echo getFormData('version'); ?>" />
249            <?php
250            break;
251        }
252    }
253
254    /**
255     * Loops through the PEdit data array and prints all the HTML forms corresponding
256     * to all pedit variables, in the order in which they were 'set'.
257     *
258     * @access public
259     */
260    function printAllForms()
261    {
262        if ($this->_authorized && $this->op == 'Edit' && is_array($this->_data) && $this->_data_loaded) {
263            foreach ($this->_data as $name=>$d) {
264                $this->printForm($name);
265            }
266        }
267    }
268
269    /**
270     * Prints the HTML forms corresponding to pedit variables. Each variable
271     * must first be 'set'.
272     *
273     * @access public
274     * @param string $name      The name of the variable.
275     * @param string $type      Type of form to print. Currently only 'text' and 'textarea' supported.
276     */
277    function printForm($name, $type='text')
278    {
279        if ($this->_authorized && $this->op == 'Edit' && $this->_data_loaded) {       
280            ?>
281            <div class="sc-pedit-item">
282            <?php
283            $type = (isset($this->_data[$name]['type'])) ? $this->_data[$name]['type'] : $type;
284            // Print edit form.
285            switch ($type) {
286            case 'text' :
287            default :
288                ?>
289                <label><?php echo ucfirst(str_replace('_', ' ', $name)); ?></label>
290                <input type="text" name="_pedit_data[<?php echo $name; ?>]" id="sc-pedit-field-<?php echo $name; ?>" value="<?php echo oTxt($this->_data[$name]['content']); ?>" class="sc-full" />
291                <?php
292                break;
293            case 'textarea' :
294                ?>
295                <label><?php echo ucfirst(str_replace('_', ' ', $name)); ?></label>
296                <textarea name="_pedit_data[<?php echo $name; ?>]" id="sc-pedit-field-<?php echo $name; ?>" rows="" cols="" class="sc-full sc-tall"><?php echo oTxt($this->_data[$name]['content']); ?></textarea>
297                <?php
298                break;
299            }
300            ?>
301            </div>
302            <?php
303        }
304    }
305
306    /**
307     * Prints the endig </form> HTML tag, as well as buttons used during
308     * different operations.
309     *
310     * @return bool  False if unauthorized or current page is a version.
311     */
312    function formEnd()
313    {
314        if (!$this->_authorized || empty($this->_data)) {
315            // Don't show form elements for versioned documents.
316            return false;
317        }
318        switch ($this->op) {
319        case 'Edit' :
320            ?>
321            <div class="sc-pedit-buttons">
322                <input type="submit" name="op" value="<?php echo _("Save"); ?>" title="<?php echo preg_replace('/^(\w)/i', '($1)', _("Save"))?>" accesskey="<?php echo mb_substr(_("Save"), 0, 1) ?>" />
323                <input type="submit" name="op" value="<?php echo _("Cancel"); ?>" title="<?php echo preg_replace('/^(\w)/i', '($1)', _("Cancel"))?>" accesskey="<?php echo mb_substr(_("Cancel"), 0, 1) ?>" />
324            </div>
325            </form>
326            <?php
327            break;
328        case 'Versions' :
329            ?>
330            <div class="sc-pedit-buttons">
331                <input type="submit" name="op" value="<?php echo _("Cancel"); ?>" title="<?php echo preg_replace('/^(\w)/i', '($1)', _("Cancel"))?>" accesskey="<?php echo mb_substr(_("Cancel"), 0, 1) ?>" />
332            </div>
333            </form>
334            <?php
335            break;
336        case 'View' :
337            ?>
338            <div class="sc-pedit-buttons">
339                <input type="submit" name="op" value="<?php echo _("Restore"); ?>" title="<?php echo preg_replace('/^(\w)/i', '($1)', _("Restore"))?>" accesskey="<?php echo mb_substr(_("Restore"), 0, 1) ?>" />
340                <input type="submit" name="op" value="<?php echo _("Versions"); ?>" title="<?php echo preg_replace('/^(\w)/i', '($1)', _("Versions"))?>" accesskey="<?php echo mb_substr(_("Versions"), 0, 1) ?>" />
341                <input type="submit" name="op" value="<?php echo _("Cancel"); ?>" title="<?php echo preg_replace('/^(\w)/i', '($1)', _("Cancel"))?>" accesskey="<?php echo mb_substr(_("Cancel"), 0, 1) ?>" />
342            </div>
343            </form>
344            <?php
345            break;
346        default :
347            ?>
348            <div class="sc-pedit-buttons">
349                <input type="submit" name="op" value="<?php echo _("Edit"); ?>" title="<?php echo preg_replace('/^(\w)/i', '($1)', _("Edit"))?>" accesskey="<?php echo mb_substr(_("Edit"), 0, 1) ?>" />
350                <input type="submit" name="op" value="<?php echo _("Versions"); ?>" title="<?php echo preg_replace('/^(\w)/i', '($1)', _("Versions"))?>" accesskey="<?php echo mb_substr(_("Versions"), 0, 1) ?>" />
351            </div>
352            </form>
353            <?php
354        }
355    }
356
357    /**
358     * Prints an HTML list of versions of current file, with the filesize
359     * and links to view and restore the file.
360     *
361     * @access public
362     */
363    function printVersions()
364    {
365        $app =& App::getInstance();
366
367        if ($this->_authorized && $this->op == 'Versions') {
368            // Print versions and commands to view/restore.
369            $version_files = $this->_getVersions();
370            ?><h1><?php printf(_("%s saved versions of %s"), sizeof($version_files), basename($_SERVER['PHP_SELF'])); ?></h1><?php
371            if (is_array($version_files) && !empty($version_files)) {
372                ?>
373                <table id="sc-pedit-versions-table">
374                <tr>
375                    <th><?php echo _("Date"); ?></th>
376                    <th><?php echo _("Time"); ?></th>
377                    <th><?php echo _("File size"); ?></th>
378                    <th><?php echo _("Version options"); ?></th>
379                </tr>
380                <?php
381                foreach ($version_files as $v) {
382                    ?>
383                    <tr>
384                        <td><?php echo date($app->getParam('date_format'), $v['unixtime']); ?></td>
385                        <td><?php echo date($app->getParam('time_format'), $v['unixtime']); ?></td>
386                        <td><?php echo humanFileSize($v['filesize']); ?></td>
387                        <td class="sc-nowrap"><a href="<?php echo $app->oHREF($_SERVER['PHP_SELF'] . '?op=View&version=' . $v['unixtime'] . '&file_hash=' . $this->_fileHash()); ?>"><?php echo _("View"); ?></a> <?php echo _("or"); ?> <a href="<?php echo $app->oHREF($_SERVER['PHP_SELF'] . '?op=Restore&version=' . $v['unixtime'] . '&file_hash=' . $this->_fileHash()); ?>"><?php echo _("Restore"); ?></a></td>
388                    </tr>
389                    <?php
390                }
391                ?>
392                </table>
393                <div class="sc-help"><?php printf(_("When there are more than %s versions, those over %s days old are deleted."), $this->getParam('versions_min_qty'), $this->getParam('versions_min_days')); ?></div>
394                <?php
395            }
396        }
397    }
398
399    /*
400    * Returns a secreat hash for the current file.
401    *
402    * @access   public
403    * @author   Quinn Comendant <quinn@strangecode.com>
404    * @since    12 Apr 2006 10:52:35
405    */
406    function _fileHash()
407    {
408        $app =& App::getInstance();
409
410        return md5($app->getParam('signing_key') . $_SERVER['PHP_SELF']);
411    }
412
413    /*
414    * Load the XML data file into $this->_data.
415    *
416    * @access   public
417    * @return   bool    false on error
418    * @author   Quinn Comendant <quinn@strangecode.com>
419    * @since    11 Apr 2006 20:36:26
420    */
421    function _loadDataFile()
422    {
423        $app =& App::getInstance();
424
425        if (!file_exists($this->_data_file)) {
426            if (!$this->_initializeDataFile()) {
427                $app->logMsg(sprintf('Initializing content file failed: %s', $this->_data_file), LOG_WARNING, __FILE__, __LINE__);
428                return false;
429            }
430        }
431        $xml_file_contents = file_get_contents($this->_data_file);
432        $status = $this->xml_unserializer->unserialize($xml_file_contents, false);   
433        if (PEAR::isError($status)) {
434            $app->logMsg(sprintf('XML_Unserialize error: %s', $status->getMessage()), LOG_WARNING, __FILE__, __LINE__);
435            return false;
436        }
437        $xml_file_data = $this->xml_unserializer->getUnserializedData();
438
439        // Only load data specified with set(), even though there may be more in the xml file.
440        foreach ($this->_data as $name => $initial_data) {
441            if (isset($xml_file_data[$name])) {
442                $this->_data[$name] = array_merge($initial_data, $xml_file_data[$name]);
443            } else {
444                $this->_data[$name] = $initial_data;
445            }
446        }
447
448        $this->_data_loaded = true;
449        return true;
450    }
451   
452    /*
453    * Start a new data file.
454    *
455    * @access   public
456    * @return   The success value of both xml_serializer->serialize() and _filePutContents()
457    * @author   Quinn Comendant <quinn@strangecode.com>
458    * @since    11 Apr 2006 20:53:42
459    */
460    function _initializeDataFile()
461    {
462        $app =& App::getInstance();
463
464        $app->logMsg(sprintf('Initializing data file: %s', $this->_data_file), LOG_INFO, __FILE__, __LINE__);
465        $xml_file_contents = $this->xml_serializer->serialize($this->_data);
466        return $this->_filePutContents($this->_data_file, $xml_file_contents);
467    }
468
469    /**
470     * Saves the POSTed data by overwriting the pedit variables in the
471     * current file.
472     *
473     * @access private
474     * @return bool  False if unauthorized or on failure. True on success.
475     */
476    function _writeData()
477    {
478        $app =& App::getInstance();
479
480        if (!$this->_authorized) {
481            return false;
482        }
483        if ($this->_fileHash() != getFormData('file_hash')) {
484            // Posted data is NOT for this file!
485            $app->logMsg(sprintf('File_hash does not match current file.', null), LOG_WARNING, __FILE__, __LINE__);
486            return false;
487        }
488
489        // Scrub incoming data. Escape tags?
490        $new_data = getFormData('_pedit_data');
491
492        if (is_array($new_data) && !empty($new_data)) {
493            // Make certain a version is created.
494            $this->_deleteOldVersions();
495            if (!$this->_createVersion()) {
496                $app->logMsg(sprintf('Failed creating new version of file.', null), LOG_NOTICE, __FILE__, __LINE__);
497                return false;
498            }
499           
500            // Collect posted data that is already specified in _data (by set()).
501            foreach ($new_data as $name => $content) {
502                if (isset($this->_data[$name])) {
503                    $this->_data[$name]['content'] = $content;
504                }
505            }
506           
507            if (is_array($this->_data) && !empty($this->_data)) {
508                $xml_file_contents = $this->xml_serializer->serialize($this->_data);
509                return $this->_filePutContents($this->_data_file, $xml_file_contents);
510            }
511        }
512    }
513   
514    /*
515    * Writes content to the specified file.
516    *
517    * @access   public
518    * @param    string  $filename   Path to file.
519    * @param    string  $content    Data to write into file.
520    * @return   bool                Success or failure.
521    * @author   Quinn Comendant <quinn@strangecode.com>
522    * @since    11 Apr 2006 22:48:30
523    */
524    function _filePutContents($filename, $content)
525    {
526        $app =& App::getInstance();
527
528        // Ensure requested filename is within the pedit data dir.
529        if (mb_strpos($filename, $this->getParam('data_dir')) === false) {
530            $app->logMsg(sprintf('Failed writing file outside pedit _data_dir: %s', $filename), LOG_ERR, __FILE__, __LINE__);
531            return false;
532        }
533
534        // Recursively create directories.
535        $subdirs = preg_split('!/!', str_replace($this->getParam('data_dir'), '', dirname($filename)), -1, PREG_SPLIT_NO_EMPTY);
536        // Start with the pedit _data_dir base.
537        $curr_path = $this->getParam('data_dir');
538        while (!empty($subdirs)) {
539            $curr_path .= '/' . array_shift($subdirs);
540            if (!is_dir($curr_path)) {
541                if (!mkdir($curr_path)) {
542                    $app->logMsg(sprintf('Failed mkdir: %s', $curr_path), LOG_ERR, __FILE__, __LINE__);
543                    return false;
544                }
545            }
546        }
547
548        // Open file for writing and truncate to zero length.
549        if ($fp = fopen($filename, 'w')) {
550            if (flock($fp, LOCK_EX)) {
551                fwrite($fp, $content, mb_strlen($content));
552                flock($fp, LOCK_UN);
553            } else {
554                $app->logMsg(sprintf('Could not lock file for writing: %s', $filename), LOG_ERR, __FILE__, __LINE__);
555                return false;
556            }
557            fclose($fp);
558            // Success!
559            $app->logMsg(sprintf('Wrote to file: %s', $filename), LOG_DEBUG, __FILE__, __LINE__);
560            return true;
561        } else {
562            $app->logMsg(sprintf('Could not open file for writing: %s', $filename), LOG_ERR, __FILE__, __LINE__);
563            return false;
564        }
565    }
566
567    /**
568     * Makes a copy of the current file with the unix timestamp appended to the
569     * filename.
570     *
571     * @access private
572     * @return bool  False on failure. True on success.
573     */
574    function _createVersion()
575    {
576        $app =& App::getInstance();
577
578        if (!$this->_authorized) {
579            return false;
580        }
581        if ($this->_fileHash() != getFormData('file_hash')) {
582            // Posted data is NOT for this file!
583            $app->logMsg(sprintf('File_hash does not match current file.', null), LOG_ERR, __FILE__, __LINE__);
584            return false;
585        }
586
587        // Ensure current data file exists.
588        if (!file_exists($this->_data_file)) {
589            $app->logMsg(sprintf('Data file does not yet exist: %s', $this->_data_file), LOG_NOTICE, __FILE__, __LINE__);
590            return false;
591        }
592
593        // Do the actual copy. File naming scheme must be consistent!
594        // filename.php.xml becomes filename.php__1124124128.xml
595        $version_file = sprintf('%s__%s.xml', preg_replace('/\.xml$/', '', $this->_data_file), time());
596        if (!copy($this->_data_file, $version_file)) {
597            $app->logMsg(sprintf('Failed copying new version: %s -> %s', $this->_data_file, $version_file), LOG_ERR, __FILE__, __LINE__);
598            return false;
599        }
600
601        return true;
602    }
603   
604    /*
605    * Delete all versions older than versions_min_days if there are more than versions_min_qty or 100.
606    *
607    * @access   public
608    * @return bool  False on failure. True on success.
609    * @author   Quinn Comendant <quinn@strangecode.com>
610    * @since    12 Apr 2006 11:08:11
611    */
612    function _deleteOldVersions()
613    {
614        $app =& App::getInstance();
615
616        $version_files = $this->_getVersions();
617        if (is_array($version_files) && sizeof($version_files) > $this->getParam('versions_min_qty')) {
618            // Pop oldest ones off bottom of array.
619            $oldest = array_pop($version_files);
620            // Loop while minimum X qty && minimum X days worth but never more than 100 qty.
621            while ((sizeof($version_files) > $this->getParam('versions_min_qty')
622            && $oldest['unixtime'] < mktime(date('H'), date('i'), date('s'), date('m'), date('d') - $this->getParam('versions_min_days'), date('Y')))
623            || sizeof($version_files) > 100) {
624                $del_file = dirname($this->_data_file) . '/' . $oldest['filename'];
625                if (!unlink($del_file)) {
626                    $app->logMsg(sprintf('Failed deleting version: %s', $del_file), LOG_ERR, __FILE__, __LINE__);
627                }
628                $oldest = array_pop($version_files);
629            }
630        }
631    }
632
633    /**
634     * Returns an array of all archived versions of the current file,
635     * sorted with newest versions at the top of the array.
636     *
637     * @access private
638     * @return array  Array of versions.
639     */
640    function _getVersions()
641    {
642        $version_files = array();
643        $dir_handle = opendir(dirname($this->_data_file));
644        $curr_file_preg_pattern = sprintf('/^%s__(\d+).xml$/', preg_quote(basename($_SERVER['PHP_SELF'])));
645        while ($dir_handle && ($version_file = readdir($dir_handle)) !== false) {
646            if (!preg_match('/^\./', $version_file) && !is_dir($version_file) && preg_match($curr_file_preg_pattern, $version_file, $time)) {
647                $version_files[] = array(
648                    'filename' => $version_file,
649                    'unixtime' => $time[1],
650                    'filesize' => filesize(dirname($this->_data_file) . '/' . $version_file)
651                );
652            }
653        }
654
655        if (is_array($version_files) && !empty($version_files)) {
656            array_multisort($version_files, SORT_DESC);
657            return $version_files;
658        } else {
659            return array();
660        }
661    }
662
663    /**
664     * Makes a version backup of the current file, then copies the specified
665     * archived version over the current file.
666     *
667     * @access private
668     * @param string $version    Unix timestamp of archived version to restore.
669     * @return bool  False on failure. True on success.
670     */
671    function _restoreVersion($version)
672    {
673        $app =& App::getInstance();
674
675        if (!$this->_authorized) {
676            return false;
677        }
678       
679        // The file to restore.
680        $version_file = sprintf('%s__%s.xml', preg_replace('/\.xml$/', '', $this->_data_file), $version);
681       
682        // Ensure specified version exists.
683        if (!file_exists($version_file)) {
684            $app->logMsg(sprintf('Cannot restore non-existant file: %s', $version_file), LOG_NOTICE, __FILE__, __LINE__);
685            return false;
686        }
687
688        // Make certain a version is created.
689        if (!$this->_createVersion()) {
690            $app->logMsg(sprintf('Failed creating new version of file.', null), LOG_ERR, __FILE__, __LINE__);
691            return false;
692        }
693
694        // Do the actual copy.
695        if (!copy($version_file, $this->_data_file)) {
696            $app->logMsg(sprintf('Failed copying old version: %s -> %s', $version_file, $this->_data_file), LOG_ERR, __FILE__, __LINE__);
697            return false;
698        }
699
700        // Success!
701        $app->raiseMsg(sprintf(_("Page has been restored to version %s."), $version), MSG_SUCCESS, __FILE__, __LINE__);
702        return true;
703    }
704
705} // End class.
706
707?>
Note: See TracBrowser for help on using the repository browser.