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

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