Changeset 92
- Timestamp:
- Apr 13, 2006 12:00:40 AM (18 years ago)
- Location:
- trunk/lib
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/lib/CSS.inc.php
r91 r92 14 14 var $_css_files = array('default' => array()); 15 15 16 // C ache style sheets?16 // CSS object parameters. 17 17 var $_params = array( 18 18 'cache_css' => true, -
trunk/lib/Hierarchy.inc.php
r91 r92 677 677 * @return array Details of from the node table of all nodes below the 678 678 * specified node: (type, id, title, indent level, selected status) 679 *680 * FIX ME: $include_curr doesn't work because the current node will break with an infinite loop.681 679 */ 682 680 function &getNodeList($preselected=null, $child_type=null, $child_id=null, $type_constraint=null, $include_curr=false, $order='', $_indent=0, $_return_flag=true) … … 709 707 for ($i=0; $i<$num_children; $i++) { 710 708 711 $row = $my_children[$i];712 709 // Preventing circular references. 713 if ($ row['child_type'] == $child_type && $row['child_id'] == $child_id) {714 App::logMsg(sprintf(_("Circular reference detected: %s has itself as a parent."), $this->toStringID($ row['child_type'], $row['child_id'])), LOG_ERR, __FILE__, __LINE__);710 if ($my_children[$i]['child_type'] == $child_type && $my_children[$i]['child_id'] == $child_id && !($_return_flag && $include_curr)) { 711 App::logMsg(sprintf(_("Circular reference detected: %s has itself as a parent."), $this->toStringID($my_children[$i]['child_type'], $my_children[$i]['child_id'])), LOG_ERR, __FILE__, __LINE__); 715 712 continue; 716 713 } 717 $row['indent'] = $_indent; 718 if (in_array($this->toStringID($row['child_type'], $row['child_id']), $preselected)) { 719 $row['selected'] = true; 714 715 $my_children[$i]['indent'] = $_indent; 716 if (in_array($this->toStringID($my_children[$i]['child_type'], $my_children[$i]['child_id']), $preselected)) { 717 $my_children[$i]['selected'] = true; 720 718 } 721 $output[] = $row; 722 unset($row); 723 724 // This is so we test if each node is a string only once. We store the result in the is_a_leaf array statically. 719 $output[] = $my_children[$i]; 720 721 // Test if each node is a string only once. Store the result in the is_a_leaf array statically. 725 722 if (!isset($is_a_leaf[$this->toStringID($my_children[$i]['child_type'], $my_children[$i]['child_id'])])) { 726 723 $is_a_leaf[$this->toStringID($my_children[$i]['child_type'], $my_children[$i]['child_id'])] = $this->isLeaf($my_children[$i]['child_type'], $my_children[$i]['child_id']); -
trunk/lib/PEdit.inc.php
r91 r92 5 5 * circumstances, but an authenticated user can 'edit' the document-- 6 6 * data stored in vars will be shown in html form elements to be editied 7 * and saved. On save, a mass search and replace is complated to inssert the new8 * data into the old file.A copy of the previous version is saved with the unix7 * 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 9 * timestamp as part of the filename. This allows reverting to previous versions. 10 10 * … … 12 12 * and call printing/editing functions where you want data and forms to 13 13 * show up. Below is an example of use: 14 *15 * // Initialize.16 *require_once 'codebase/lib/PEdit.inc.php';17 * $p = new PEdit($auth->hasClearance('pedit'));18 *19 * $title = <<<P_E_D_I_T_title20 * Using Burritos to Improve Student Learnin'21 * P_E_D_I_T_title;22 * $p->set($title, 'title', 'textbox');23 *24 * // Begin content. Include a header or something right here.25 * $p->printContent('title');26 *27 * // Prints beginning form tags and special hidden forms. (Only happens if page is NOT a archived version.)28 * $p->formBegin();29 *30 * // Print editing form elements. (Only happens if op == Edit.)31 * $p->printForm('title');32 *33 * // Print versions list. (Only happens if op == Versions.)34 * $p->printVersions();35 *36 * // Prints ending form tags and command buttons.(Only happens if page is NOT a archived version.)37 * $p->formEnd();38 * 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 39 * @author Quinn Comendant <quinn@strangecode.com> 40 40 * @concept Beau Smith <beau@beausmith.com> 41 * @version 1.141 * @version 2.0 42 42 */ 43 class PEdit 44 { 45 var $_data = array(); // Array to store editable data. 46 var $_filename = ''; // Full file path to current file. 47 var $_authorized = false; // User is authenticated to see extended functions. 48 var $versions_min_qty = 20; // Keep at least this many versions of each file. 49 var $versions_min_days = 10;// Keep ALL versions within this many days, even if MORE than versions_min_qty. 50 51 // Tags that are not stripped from the POSTed data. 52 var $allowed_tags = '<p><h1><h2><h3><h4><h5><h6><div><br><hr><a><img><i><em><b><strong><small><blockquote><ul><ol><li><dl><dt><dd><map><area><table><tr><td>'; 53 54 55 /** 56 * Constructs a new PEdit object. Initializes what file is being operated on 57 * (SCRIPT_FILENAME) and what that operation is. The two 43 class 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 58 62 * operations that actually modify data (save, restore) are treated differently 59 * than view operations (versions, '' -default). They die redirect so you see63 * than view operations (versions, view, default). They die redirect so you see 60 64 * the page you just modified. 61 65 * 62 66 * @access public 63 *64 67 * @param optional array $params A hash containing connection parameters. 65 68 */ 66 function PEdit($authorized=false) 67 { 68 if ($authorized === true) { 69 function PEdit($params) 70 { 71 $this->setParam($params); 72 73 if ($this->getParam('authorized') === true) { 69 74 $this->_authorized = true; 70 75 } 71 72 $this->_filename = $_SERVER['SCRIPT_FILENAME']; 73 if (empty($this->_filename)) { 74 App::logMsg(sprintf('PEdit error: server variable SCRIPT_FILENAME must be defined.', null), LOG_WARNING, __FILE__, __LINE__); 75 die; 76 } 77 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. 78 139 $this->op = getFormData('op'); 79 140 141 // Automatic functions based on state. 80 142 switch ($this->op) { 81 143 case 'Save' : … … 85 147 break; 86 148 case 'Restore' : 87 if ($this->_restoreVersion(getFormData(' with_file'))) {149 if ($this->_restoreVersion(getFormData('version'))) { 88 150 App::dieURL($_SERVER['PHP_SELF']); 89 151 } 90 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(); 91 165 } 92 166 } … … 97 171 * @access public 98 172 * 99 * @param string $content The variable containing the text to store. 100 * @param string $name The name of the variable. 101 * @param string $type The type of form element to use. 102 * @param optional int $form_size The size of the form element. 103 */ 104 function set($content, $name, $type, $form_size=null) 105 { 106 $this->_data[$name] = array( 107 'type' => $type, 108 'content' => $content, 109 'form_size' => $form_size 110 ); 111 } 112 113 /** 114 * Stores a checkbox variable in the pedit data array with the content name, and type of form. 115 * 116 * @access public 117 * 118 * @param string $content The variable containing the text to store. 119 * @param string $name The name of the variable. 120 * @param string $corresponding_text The text that corresponds to this checkbox. 121 */ 122 function setCheckbox($content, $name, $corresponding_text) 123 { 124 if (isset($content) && isset($name) && isset($corresponding_text)) { 125 $this->_data[$name] = array( 126 'type' => 'checkbox', 127 'content' => $content, 128 'corresponding_text' => $corresponding_text 129 ); 130 } 131 } 132 133 /** 134 * Tests if we are should display page contents. 135 * 136 * @access public 137 * 138 * @return bool true if we are displaying page normally, false if editing page, or viewing versions. 139 */ 140 function displayMode() 141 { 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); 142 196 if ($this->op != 'Edit' && $this->op != 'Versions' && isset($this->_data[$name]['content'])) { 143 return true; 144 } 145 } 146 147 148 /** 149 * Prints an HTML list of versions of current file, with the filesize 150 * and links to view and restore the file. 151 * 152 * @access public 153 */ 154 function printVersions() 155 { 156 if ($this->_authorized && $this->op == 'Versions') { 157 // Print versions and commands to view/restore. 158 $versions = $this->_getVersions(); 159 ?><h1><?php printf(_("%s saved versions of %s"), sizeof($versions), basename($this->_filename)); ?></h1><?php 160 if (is_array($versions) && !empty($versions)) { 161 ?><table border="0" cellspacing="0" cellpadding="4"><?php 162 foreach ($versions as $v) { 163 ?> 164 <tr> 165 <td nowrap="nowrap"><p><?php echo date('r', $v['unixtime']); ?></p></td> 166 <td nowrap="nowrap"><p> <?php printf(_("%s bytes"), $v['filesize']); ?></p></td> 167 <td nowrap="nowrap"><p> [<a href="<?php echo App::oHREF(dirname($_SERVER['PHP_SELF']) . (preg_match('!/$!', dirname($_SERVER['PHP_SELF'])) ? '' : '/') . $v['filename']); ?>" target="_blank"><?php echo _("view"); ?></a>]</p></td> 168 <td nowrap="nowrap"><p> [<a href="<?php echo App::oHREF($_SERVER['PHP_SELF'] . '?op=Restore&with_file=' . $v['filename'] . '&file_hash=' . md5('frog_guts' . $this->_filename)); ?>"><?php echo _("restore"); ?></a>]</p></td> 169 </tr> 170 <?php 171 } 172 ?></table><?php 173 ?><div class="help"><?php printf(_("When there are more than %s versions, those over %s days old are deleted."), $this->versions_min_qty, $this->versions_min_days); ?></div><?php 174 } 175 } 176 } 177 178 /** 179 * Returns the contents of a data variable. The variable must first be 'set'. 180 * 181 * @access public 182 * 183 * @param string $name The name of the variable to return. 184 * 185 * @return string The trimmed content of the named data. 186 */ 187 function getContent($name, $preserve_html=true) 188 { 189 if ($this->op != 'Edit' && $this->op != 'Versions' && isset($this->_data[$name]['content'])) { 190 // Print content. 191 switch ($this->_data[$name]['type']) { 192 case 'checkbox' : 193 return 'off' == $this->_data[$name]['content'] ? '' : oTxt($this->_data[$name]['corresponding_text'], $preserve_html); 194 default : 195 return trim(oTxt($this->_data[$name]['content'], $preserve_html)); 196 } 197 } 198 } 199 200 /** 201 * Prints the contents of a data variable. The variable must first be 'set'. 202 * 203 * @access public 204 * 205 * @param string $name The name of the variable to print. 206 */ 207 function printContent($name, $preserve_html=true) 208 { 209 echo $this->getContent($name, $preserve_html); 210 } 211 212 /** 213 * Prints the HTML forms corresponding to pedit variables. Each variable 214 * must first be 'set'. 215 * 216 * @access public 217 * 218 * @param string $name The name of the variable. 219 */ 220 function printForm($name) 221 { 222 if ($this->_authorized && $this->op == 'Edit' && isset($this->_data[$name]['type'])) { 223 // Print edit form. 224 switch ($this->_data[$name]['type']) { 225 case 'textbox' : 226 $rows = is_numeric($this->_data[$name]['form_size']) ? $this->_data[$name]['form_size'] : 50; 227 echo '<input class="monospaced" style="width: 100%;" type="text" name="data[' . $name . ']" value="' . oTxt($this->_data[$name]['content']) . '" size="' . $rows . '" /><br />'; 228 break; 229 case 'textarea' : 230 $rows = is_numeric($this->_data[$name]['form_size']) ? $this->_data[$name]['form_size'] : 20; 231 echo '<textarea class="monospaced" style="width: 100%;" rows="' . $rows . '" cols="60" name="data[' . $name . ']">' . oTxt($this->_data[$name]['content']) . '</textarea><br />'; 232 break; 233 case 'checkbox' : 234 $checked = ('off' == $this->_data[$name]['content']) ? '' : 'checked="checked" '; 235 // Note hidden form below. If the checkbox is not checked, the hidden variable will send "off" as the variable, 236 // otherwise if the form is not posted for that checkbox the update will not occur. 237 ?> 238 <table border="0" cellspacing="0" cellpadding="2"><tr> 239 <td><input type="hidden" name="data[<?php echo $name; ?>]" value="off" /><input type="checkbox" name="data[<?php echo $name; ?>]" <?php echo $checked; ?>/></td> 240 <td><?php echo oTxt($this->_data[$name]['corresponding_text']); ?></td> 241 </tr></table> 242 <?php 243 break; 244 } 245 } 246 } 247 248 /** 249 * Loops through the PEdit data array and prints all the HTML forms corresponding 250 * to all pedit variables, in the order in which they were 'set'. 251 * 252 * @access public 253 */ 254 function printAllForms() 255 { 256 if ($this->_authorized && $this->op == 'Edit' && is_array($this->_data) && !empty($this->_data)) { 257 foreach ($this->_data as $name=>$d) { 258 $this->printForm($name); 259 } 197 return $this->_data[$name]['content']; 198 } else { 199 return ''; 260 200 } 261 201 } … … 268 208 function formBegin() 269 209 { 270 if (!$this->_authorized || preg_match('/\.php__/', $this->_filename)) { 271 // Don't show form elements for versioned documents. 210 if (!$this->_authorized) { 272 211 return false; 273 212 } 274 213 ?> 275 <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post"> 276 <input type="hidden" name="filename" value="<?php echo $this->_filename; ?>" /> 277 <input type="hidden" name="file_hash" value="<?php echo md5('frog_guts' . $this->_filename); ?>" /> 214 <style type="text/css" media="screen"> 215 /* <![CDATA[ */ 216 #sc-pedit-form input[type="text"], textarea { 217 width: 100%; 218 } 219 #sc-pedit-form textarea { 220 height: 30em; 221 } 222 /* ]]> */ 223 </style> 224 225 <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" id="sc-pedit-form"> 226 <input type="hidden" name="filename" value="<?php echo $_SERVER['PHP_SELF']; ?>" /> 227 <input type="hidden" name="file_hash" value="<?php echo $this->_fileHash(); ?>" /> 278 228 <?php 279 229 App::printHiddenSession(); … … 281 231 case 'Edit' : 282 232 ?> 283 <div class=" pedit_buttons">284 <input type="submit" name="op" value="<?php echo _("Save"); ?>" />285 <input type="submit" name="op" value="<?php echo _("Cancel"); ?>" />233 <div class="sc-pedit-buttons"> 234 <input type="submit" name="op" value="<?php echo _("Save"); ?>" /> 235 <input type="submit" name="op" value="<?php echo _("Cancel"); ?>" /> 286 236 </div> 287 237 <?php 288 238 break; 239 case 'View' : 240 ?> 241 <input type="hidden" name="version" value="<?php echo getFormData('version'); ?>" /> 242 <?php 243 break; 244 } 245 } 246 247 /** 248 * Loops through the PEdit data array and prints all the HTML forms corresponding 249 * to all pedit variables, in the order in which they were 'set'. 250 * 251 * @access public 252 */ 253 function printAllForms() 254 { 255 if ($this->_authorized && $this->op == 'Edit' && is_array($this->_data) && $this->_data_loaded) { 256 foreach ($this->_data as $name=>$d) { 257 $this->printForm($name); 258 } 259 } 260 } 261 262 /** 263 * Prints the HTML forms corresponding to pedit variables. Each variable 264 * must first be 'set'. 265 * 266 * @access public 267 * @param string $name The name of the variable. 268 * @param string $type Type of form to print. Currently only 'text' and 'textarea' supported. 269 */ 270 function printForm($name, $type='text') 271 { 272 if ($this->_authorized && $this->op == 'Edit' && $this->_data_loaded) { 273 $type = (isset($this->_data[$name]['type'])) ? $this->_data[$name]['type'] : $type; 274 // Print edit form. 275 switch ($type) { 276 case 'text' : 277 default : 278 ?><br /><label><?php echo ucfirst(str_replace('_', ' ', $name)); ?> <input type="text" name="_pedit_data[<?php echo $name; ?>]" id="sc-pedit-<?php echo $name; ?>" value="<?php echo oTxt($this->_data[$name]['content']); ?>" /></label><?php 279 break; 280 case 'textarea' : 281 ?><br /><label><?php echo ucfirst(str_replace('_', ' ', $name)); ?> <textarea name="_pedit_data[<?php echo $name; ?>]" id="sc-pedit-<?php echo $name; ?>"><?php echo oTxt($this->_data[$name]['content']); ?></textarea></label><?php 282 break; 283 } 289 284 } 290 285 } … … 298 293 function formEnd() 299 294 { 300 if (!$this->_authorized || preg_match('/\.php__/', $ this->_filename)) {295 if (!$this->_authorized || preg_match('/\.php__/', $_SERVER['PHP_SELF'])) { 301 296 // Don't show form elements for versioned documents. 302 297 return false; … … 305 300 case 'Edit' : 306 301 ?> 307 <div class=" pedit_buttons">308 <input type="submit" name="op" value="<?php echo _("Save"); ?>" />309 <input type="submit" name="op" value="<?php echo _("Cancel"); ?>" />302 <div class="sc-pedit-buttons"> 303 <input type="submit" name="op" value="<?php echo _("Save"); ?>" /> 304 <input type="submit" name="op" value="<?php echo _("Cancel"); ?>" /> 310 305 </div> 311 306 </form> … … 314 309 case 'Versions' : 315 310 ?> 316 <div class=" pedit_buttons">317 <input type="submit" name="op" value="<?php echo _("Cancel"); ?>" />311 <div class="sc-pedit-buttons"> 312 <input type="submit" name="op" value="<?php echo _("Cancel"); ?>" /> 318 313 </div> 319 314 </form> 320 315 <?php 321 316 break; 317 case 'View' : 318 ?> 319 <div class="sc-pedit-buttons"> 320 <input type="submit" name="op" value="<?php echo _("Restore"); ?>" /> 321 <input type="submit" name="op" value="<?php echo _("Cancel"); ?>" /> 322 </div> 323 <?php 324 break; 322 325 default : 323 326 ?> 324 <div class=" pedit_buttons">325 <input type="submit" name="op" value="<?php echo _("Edit"); ?>" />326 <input type="submit" name="op" value="<?php echo _("Versions"); ?>" />327 <div class="sc-pedit-buttons"> 328 <input type="submit" name="op" value="<?php echo _("Edit"); ?>" /> 329 <input type="submit" name="op" value="<?php echo _("Versions"); ?>" /> 327 330 </div> 328 331 </form> … … 332 335 333 336 /** 337 * Prints an HTML list of versions of current file, with the filesize 338 * and links to view and restore the file. 339 * 340 * @access public 341 */ 342 function printVersions() 343 { 344 if ($this->_authorized && $this->op == 'Versions') { 345 // Print versions and commands to view/restore. 346 $version_files = $this->_getVersions(); 347 ?><h1><?php printf(_("%s saved versions of %s"), sizeof($version_files), basename($_SERVER['PHP_SELF'])); ?></h1><?php 348 if (is_array($version_files) && !empty($version_files)) { 349 ?><table id="sc-pedit-versions"><?php 350 foreach ($version_files as $v) { 351 ?> 352 <tr> 353 <td><?php echo date('r', $v['unixtime']); ?></td> 354 <td><?php printf(_("%s bytes"), $v['filesize']); ?></td> 355 <td><a href="<?php echo App::oHREF($_SERVER['PHP_SELF'] . '?op=View&version=' . $v['unixtime'] . '&file_hash=' . $this->_fileHash()); ?>"><?php echo _("View"); ?></a></td> 356 <td><a href="<?php echo App::oHREF($_SERVER['PHP_SELF'] . '?op=Restore&version=' . $v['unixtime'] . '&file_hash=' . $this->_fileHash()); ?>"><?php echo _("Restore"); ?></a></td> 357 </tr> 358 <?php 359 } 360 ?></table><?php 361 ?><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><?php 362 } 363 } 364 } 365 366 /* 367 * Returns a secreat hash for the current file. 368 * 369 * @access public 370 * @author Quinn Comendant <quinn@strangecode.com> 371 * @since 12 Apr 2006 10:52:35 372 */ 373 function _fileHash() 374 { 375 return md5(App::getParam('signing_key') . $_SERVER['PHP_SELF']); 376 } 377 378 /* 379 * Load the XML data file into $this->_data. 380 * 381 * @access public 382 * @return bool false on error 383 * @author Quinn Comendant <quinn@strangecode.com> 384 * @since 11 Apr 2006 20:36:26 385 */ 386 function _loadDataFile() 387 { 388 if (!file_exists($this->_data_file)) { 389 if (!$this->_initializeDataFile()) { 390 App::logMsg(sprintf('Initializing content file failed: %s', $this->_data_file), LOG_WARNING, __FILE__, __LINE__); 391 return false; 392 } 393 } 394 $xml_file_contents = file_get_contents($this->_data_file); 395 $status = $this->xml_unserializer->unserialize($xml_file_contents, false); 396 if (PEAR::isError($status)) { 397 App::logMsg(sprintf('XML_Unserialize error: %s', $status->getMessage()), LOG_WARNING, __FILE__, __LINE__); 398 return false; 399 } 400 $xml_file_data = $this->xml_unserializer->getUnserializedData(); 401 402 // Only load data specified with set(), even though there may be more in the xml file. 403 foreach ($this->_data as $name => $initial_data) { 404 if (isset($xml_file_data[$name])) { 405 $this->_data[$name] = array_merge($initial_data, $xml_file_data[$name]); 406 } else { 407 $this->_data[$name] = $initial_data; 408 } 409 } 410 411 $this->_data_loaded = true; 412 return true; 413 } 414 415 /* 416 * Start a new data file. 417 * 418 * @access public 419 * @return The success value of both xml_serializer->serialize() and _filePutContents() 420 * @author Quinn Comendant <quinn@strangecode.com> 421 * @since 11 Apr 2006 20:53:42 422 */ 423 function _initializeDataFile() 424 { 425 App::logMsg(sprintf('Initializing data file: %s', $this->_data_file), LOG_INFO, __FILE__, __LINE__); 426 $xml_file_contents = $this->xml_serializer->serialize($this->_data); 427 return $this->_filePutContents($this->_data_file, $xml_file_contents); 428 } 429 430 /** 334 431 * Saves the POSTed data by overwriting the pedit variables in the 335 432 * current file. 336 433 * 337 434 * @access private 338 *339 435 * @return bool False if unauthorized or on failure. True on success. 340 436 */ … … 344 440 return false; 345 441 } 346 if ( md5('frog_guts' . $this->_filename) != getFormData('file_hash')) {442 if ($this->_fileHash() != getFormData('file_hash')) { 347 443 // Posted data is NOT for this file! 348 trigger_error('PEdit error: file_hash does not match current file.', E_USER_WARNING);349 return false; 350 } 351 $whole_file = file_get_contents($this->_filename); 352 $new_data = getFormData('data');353 $ search = array();354 $replace = array(); 444 App::logMsg(sprintf('File_hash does not match current file.', null), LOG_WARNING, __FILE__, __LINE__); 445 return false; 446 } 447 448 // Scrub incoming data. Escape tags? 449 $new_data = getFormData('_pedit_data'); 450 355 451 if (is_array($new_data) && !empty($new_data)) { 356 foreach ($new_data as $name=>$d) { 357 if ('' != $d && !preg_match('/P_E_D_I_T_/', $d)) { 358 // If the new posted data is not empty, and the heredoc identifer is not in it. 359 $block_identifier = 'P_E_D_I_T_' . preg_quote($name); 360 if (substr_count($whole_file, $block_identifier) != 2) { 361 App::logMsg(sprintf('PEdit error: more than one %s heredoc identifier found.', $block_identifier), LOG_NOTICE, __FILE__, __LINE__); 362 return false; 363 } 364 // Strip extra linefeeds. For some reason POST data has double EOL chars when writing to a unix file. 365 $d = preg_replace("/[\n\r]{2,}/", "\n", $d); 366 $search[] = "/$block_identifier.*?\n$block_identifier;/s"; 367 $replace[] = "$block_identifier\n" . strip_tags($d, $this->allowed_tags) . "\n$block_identifier;"; 452 // Make certain a version is created. 453 $this->_deleteOldVersions(); 454 if (!$this->_createVersion()) { 455 App::logMsg(sprintf('Failed creating new version of file.', null), LOG_NOTICE, __FILE__, __LINE__); 456 return false; 457 } 458 459 // Collect posted data that is already specified in _data (by set()). 460 foreach ($new_data as $name => $content) { 461 if (isset($this->_data[$name])) { 462 $this->_data[$name]['content'] = $content; 368 463 } 369 464 } 370 371 // Search and replace all blocks. 372 $whole_file = preg_replace($search, $replace, $whole_file); 373 374 // Probably unnecessary, testing if resulting file is empty or smaller than input. 375 if (strlen($whole_file) < strlen(strip_tags(serialize($new_data), $this->allowed_tags))) { 376 App::logMsg(sprintf('PEdit error: saved file size (%s) is less than input data size (%s).', strlen($whole_file), strlen(strip_tags(serialize($new_data), $this->allowed_tags))), LOG_NOTICE, __FILE__, __LINE__); 377 return false; 378 } 379 380 // Make certain a version is created. 381 if (! $this->_createVersion()) { 382 App::logMsg(sprintf('PEdit error: failed creating new version of file.', null), LOG_NOTICE, __FILE__, __LINE__); 383 return false; 384 } 385 386 // Open file for writing and truncate to zero length. 387 if (is_writable($this->_filename) && $fp = fopen($this->_filename, 'w')) { 388 if (flock($fp, LOCK_EX)) { 389 fwrite($fp, $whole_file, strlen($whole_file)); 390 flock($fp, LOCK_UN); 391 } else { 392 App::logMsg(sprintf('PEdit error: could not lock file for writing: %s', $this->_filename), LOG_NOTICE, __FILE__, __LINE__); 465 466 if (is_array($this->_data) && !empty($this->_data)) { 467 $xml_file_contents = $this->xml_serializer->serialize($this->_data); 468 return $this->_filePutContents($this->_data_file, $xml_file_contents); 469 } 470 } 471 } 472 473 /* 474 * Writes content to the specified file. 475 * 476 * @access public 477 * @param string $filename Path to file. 478 * @param string $content Data to write into file. 479 * @return bool Success or failure. 480 * @author Quinn Comendant <quinn@strangecode.com> 481 * @since 11 Apr 2006 22:48:30 482 */ 483 function _filePutContents($filename, $content) 484 { 485 // Ensure requested filename is within the pedit data dir. 486 if (strpos($filename, $this->getParam('data_dir')) === false) { 487 App::logMsg(sprintf('Failed writing file outside pedit _data_dir: %s', $filename), LOG_ERR, __FILE__, __LINE__); 488 return false; 489 } 490 491 // Recursively create directories. 492 $subdirs = preg_split('!/!', str_replace($this->getParam('data_dir'), '', dirname($filename)), -1, PREG_SPLIT_NO_EMPTY); 493 // Start with the pedit _data_dir base. 494 $curr_path = $this->getParam('data_dir'); 495 while (!empty($subdirs)) { 496 $curr_path .= '/' . array_shift($subdirs); 497 if (!is_dir($curr_path)) { 498 if (!mkdir($curr_path)) { 499 App::logMsg(sprintf('Failed mkdir: %s', $curr_path), LOG_ERR, __FILE__, __LINE__); 393 500 return false; 394 501 } 395 fclose($fp); 396 // Success! 397 return true; 502 } 503 } 504 505 // Open file for writing and truncate to zero length. 506 if ($fp = fopen($filename, 'w')) { 507 if (flock($fp, LOCK_EX)) { 508 fwrite($fp, $content, strlen($content)); 509 flock($fp, LOCK_UN); 398 510 } else { 399 App::logMsg(sprintf(' PEdit error: could not open file for writing: %s', $this->_filename), LOG_NOTICE, __FILE__, __LINE__);511 App::logMsg(sprintf('Could not lock file for writing: %s', $filename), LOG_ERR, __FILE__, __LINE__); 400 512 return false; 401 513 } 514 fclose($fp); 515 // Success! 516 App::logMsg(sprintf('Wrote to file: %s', $filename), LOG_DEBUG, __FILE__, __LINE__); 517 return true; 518 } else { 519 App::logMsg(sprintf('Could not open file for writing: %s', $filename), LOG_ERR, __FILE__, __LINE__); 520 return false; 402 521 } 403 522 } … … 405 524 /** 406 525 * Makes a copy of the current file with the unix timestamp appended to the 407 * filename. Deletes old versions based on threshold of age and qty.526 * filename. 408 527 * 409 528 * @access private 410 * 411 * @param optional boolean $do_cleanup Set to false to turn off the 412 * cleanup routine. 413 * 414 * @return bool False if unauthorized or on failure. True on success. 415 */ 416 function _createVersion($do_cleanup=true) 529 * @return bool False on failure. True on success. 530 */ 531 function _createVersion() 417 532 { 418 533 if (!$this->_authorized) { 419 534 return false; 420 535 } 421 if ( md5('frog_guts' . $this->_filename) != getFormData('file_hash')) {536 if ($this->_fileHash() != getFormData('file_hash')) { 422 537 // Posted data is NOT for this file! 423 trigger_error('PEdit error: file_hash does not match current file.', E_USER_WARNING); 424 return false; 425 } 426 $versions = $this->_getVersions(); 427 428 // Clean up old versions. 429 if (is_array($versions) && sizeof($versions) > $this->versions_min_qty && $do_cleanup) { 538 App::logMsg(sprintf('File_hash does not match current file.', null), LOG_ERR, __FILE__, __LINE__); 539 return false; 540 } 541 542 // Ensure current data file exists. 543 if (!file_exists($this->_data_file)) { 544 App::logMsg(sprintf('Data file does not yet exist: %s', $this->_data_file), LOG_NOTICE, __FILE__, __LINE__); 545 return false; 546 } 547 548 // Do the actual copy. File naming scheme must be consistent! 549 // filename.php.xml becomes filename.php__1124124128.xml 550 $version_file = sprintf('%s__%s.xml', preg_replace('/\.xml$/', '', $this->_data_file), time()); 551 if (!copy($this->_data_file, $version_file)) { 552 App::logMsg(sprintf('Failed copying new version: %s -> %s', $this->_data_file, $version_file), LOG_ERR, __FILE__, __LINE__); 553 return false; 554 } 555 556 return true; 557 } 558 559 /* 560 * Delete all versions older than versions_min_days if there are more than versions_min_qty or 100. 561 * 562 * @access public 563 * @return bool False on failure. True on success. 564 * @author Quinn Comendant <quinn@strangecode.com> 565 * @since 12 Apr 2006 11:08:11 566 */ 567 function _deleteOldVersions() 568 { 569 $version_files = $this->_getVersions(); 570 if (is_array($version_files) && sizeof($version_files) > $this->getParam('versions_min_qty')) { 430 571 // Pop oldest ones off bottom of array. 431 $oldest = array_pop($version s);572 $oldest = array_pop($version_files); 432 573 // Loop while minimum X qty && minimum X days worth but never more than 100 qty. 433 while ((sizeof($versions) > $this->versions_min_qty 434 && $oldest['unixtime'] < mktime(date('H'),date('i'),date('s'),date('m'),date('d')-$this->versions_min_days,date('Y'))) 435 || sizeof($versions) > 100) { 436 unlink(dirname($this->_filename) . '/' . $oldest['filename']); 437 $oldest = array_pop($versions); 438 } 439 } 440 441 // Do the actual copy. File naming scheme must be consistent! 442 if (!copy($this->_filename, $this->_filename . '__' . time() . '.php')) { 443 trigger_error('PEdit error: failed copying new version. Check file and directory permissions.', E_USER_WARNING); 444 return false; 445 } 446 447 return true; 574 while ((sizeof($version_files) > $this->getParam('versions_min_qty') 575 && $oldest['unixtime'] < mktime(date('H'), date('i'), date('s'), date('m'), date('d') - $this->getParam('versions_min_days'), date('Y'))) 576 || sizeof($version_files) > 100) { 577 $del_file = dirname($this->_data_file) . '/' . $oldest['filename']; 578 if (!unlink($del_file)) { 579 App::logMsg(sprintf('Failed deleting version: %s', $del_file), LOG_ERR, __FILE__, __LINE__); 580 } 581 $oldest = array_pop($version_files); 582 } 583 } 448 584 } 449 585 … … 453 589 * 454 590 * @access private 455 *456 591 * @return array Array of versions. 457 592 */ 458 593 function _getVersions() 459 594 { 460 $version s = array();461 $dir_handle = opendir(dirname($this->_ filename));462 while ($dir_handle && ($version = readdir($dir_handle)) !== false) {463 if (!preg_match('/^\./', $version) && !is_dir($version) && preg_match('/^' . preg_quote(basename($this->_filename)) . '__.*/', $version)) {464 preg_match('/.+__(\d+)\.php/', $version, $time);465 $version s[] = array(466 'filename' => $version ,595 $version_files = array(); 596 $dir_handle = opendir(dirname($this->_data_file)); 597 $curr_file_preg_pattern = sprintf('/^%s__(\d+).xml$/', preg_quote(basename($_SERVER['PHP_SELF']))); 598 while ($dir_handle && ($version_file = readdir($dir_handle)) !== false) { 599 if (!preg_match('/^\./', $version_file) && !is_dir($version_file) && preg_match($curr_file_preg_pattern, $version_file, $time)) { 600 $version_files[] = array( 601 'filename' => $version_file, 467 602 'unixtime' => $time[1], 468 'filesize' => filesize(dirname($this->_ filename) . '/' . $version)603 'filesize' => filesize(dirname($this->_data_file) . '/' . $version_file) 469 604 ); 470 605 } 471 606 } 472 607 473 if (is_array($version s) && !empty($versions)) {474 array_multisort($version s, SORT_DESC);475 return $version s;608 if (is_array($version_files) && !empty($version_files)) { 609 array_multisort($version_files, SORT_DESC); 610 return $version_files; 476 611 } else { 477 612 return array(); … … 484 619 * 485 620 * @access private 486 * 487 * @param string $with_file Filename of archived version to restore. 488 * 489 * @return bool False if unauthorized. True on success. 490 */ 491 function _restoreVersion($with_file) 621 * @param string $version Unix timestamp of archived version to restore. 622 * @return bool False on failure. True on success. 623 */ 624 function _restoreVersion($version) 492 625 { 493 626 if (!$this->_authorized) { 494 627 return false; 495 628 } 496 497 if (is_writable($this->_filename)) { 498 // Make certain a version is created. 499 if (! $this->_createVersion(false)) { 500 App::logMsg(sprintf('PEdit error: failed creating new version of file.', null), LOG_NOTICE, __FILE__, __LINE__); 501 return false; 502 } 503 504 // Do the actual copy. 505 if (!copy(dirname($this->_filename) . '/' . $with_file, $this->_filename)) { 506 App::logMsg(sprintf('PEdit error: failed copying old version: %s', $with_file), LOG_NOTICE, __FILE__, __LINE__); 507 return false; 508 } 509 510 // Success! 511 return true; 512 } else { 513 App::logMsg(sprintf('PEdit error: could not open file for writing: %s', $this->_filename), LOG_NOTICE, __FILE__, __LINE__); 514 return false; 515 } 629 630 // The file to restore. 631 $version_file = sprintf('%s__%s.xml', preg_replace('/\.xml$/', '', $this->_data_file), $version); 632 633 // Ensure specified version exists. 634 if (!file_exists($version_file)) { 635 App::logMsg(sprintf('Cannot restore non-existant file: %s', $version_file), LOG_NOTICE, __FILE__, __LINE__); 636 return false; 637 } 638 639 // Make certain a version is created. 640 if (!$this->_createVersion()) { 641 App::logMsg(sprintf('Failed creating new version of file.', null), LOG_ERR, __FILE__, __LINE__); 642 return false; 643 } 644 645 // Do the actual copy. 646 if (!copy($version_file, $this->_data_file)) { 647 App::logMsg(sprintf('Failed copying old version: %s -> %s', $version_file, $this->_data_file), LOG_ERR, __FILE__, __LINE__); 648 return false; 649 } 650 651 // Success! 652 App::raiseMsg(sprintf(_("Page has been restored to version %s."), $version), MSG_SUCCESS, __FILE__, __LINE__); 653 return true; 516 654 } 517 655 -
trunk/lib/ScriptTimer.inc.php
r91 r92 1 1 <?php 2 2 /** 3 * ScriptTimer.inc.php 3 * ScriptTimer.inc.php 4 4 * Code by Strangecode :: www.strangecode.com :: This document contains copyrighted information 5 5 */ 6 6 7 7 class ScriptTimer { 8 8 9 var $time_format = '%.10f'; 10 var $_timing_start_times; 11 var $_timing_stop_times; 12 13 function start($name='default') 14 { 9 var $time_format = '%.10s'; 10 var $_timing_start_times = array(); 11 var $_timing_stop_times = array(); 12 var $_timing_cumulative_times = array(); 13 14 function start($name='default') 15 { 15 16 $this->_timing_start_times[$name] = explode(' ', microtime()); 16 17 18 19 17 } 18 19 function stop($name='default') 20 { 20 21 $this->_timing_stop_times[$name] = explode(' ', microtime()); 21 } 22 23 function getTime($name='default') 24 { 22 $this->_timing_cumulative_times[$name] += $this->getTime($name); 23 } 24 25 function getTime($name='default') 26 { 25 27 if (!isset($this->_timing_start_times[$name])) { 26 28 return 0; 27 29 } 28 29 30 if (!isset($this->_timing_stop_times[$name])) { 30 31 $stop_time = explode(' ', microtime()); … … 32 33 $stop_time = $this->_timing_stop_times[$name]; 33 34 } 34 35 35 // Do the big numbers first so the small ones aren't lost. 36 36 $current = $stop_time[1] - $this->_timing_start_times[$name][1]; 37 37 $current += $stop_time[0] - $this->_timing_start_times[$name][0]; 38 return $current; 39 } 40 41 function printAll($sort_by_time=false) 42 { 43 $names = array_map('strlen', array_keys($this->_timing_start_times)); 44 sort($names); 45 $name_len = end($names); 46 47 if ($sort_by_time) { 48 arsort($this->_timing_cumulative_times, SORT_NUMERIC); 49 } 50 51 $this->_timing_cumulative_times["TOTAL"] = array_sum($this->_timing_cumulative_times); 38 52 39 return sprintf($this->time_format, $current); 40 } 53 echo '<pre>'; 54 foreach ($this->_timing_cumulative_times as $name => $time) { 55 printf("\n%-{$name_len}s $this->time_format", $name, $time); 56 } 57 echo '</pre>'; 58 } 41 59 } 42 60
Note: See TracChangeset
for help on using the changeset viewer.