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

Last change on this file since 98 was 98, checked in by scdev, 18 years ago

B - pEdit formatting ticket #1

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