[ Index ] |
|
Code source de Horde 3.1.3 |
1 <?php 2 /** 3 * The History:: class provides a method of tracking changes in Horde 4 * objects, stored in a SQL table. 5 * 6 * $Horde: framework/History/History.php,v 1.28.2.14 2006/05/03 21:46:13 jan Exp $ 7 * 8 * Copyright 2003-2006 Chuck Hagenbuch <chuck@horde.org> 9 * 10 * See the enclosed file COPYING for license information (LGPL). If you 11 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. 12 * 13 * @author Chuck Hagenbuch <chuck@horde.org> 14 * @since Horde 2.1 15 * @package Horde_History 16 */ 17 class Horde_History { 18 19 /** 20 * Pointer to a DB instance to manage the history. 21 * 22 * @var DB 23 */ 24 var $_db; 25 26 /** 27 * Attempts to return a reference to a concrete History instance. 28 * It will only create a new instance if no History instance 29 * currently exists. 30 * 31 * This method must be invoked as: $var = &History::singleton() 32 * 33 * @return Horde_History The concrete History reference, or false on an 34 * error. 35 */ 36 function &singleton() 37 { 38 static $history; 39 40 if (!isset($history)) { 41 $history = &new Horde_History(); 42 } 43 44 return $history; 45 } 46 47 /** 48 * Constructor. 49 */ 50 function Horde_History() 51 { 52 global $conf; 53 54 if (empty($conf['sql'])) { 55 $this->_db = PEAR::raiseError(_("The History system is disabled.")); 56 } 57 58 require_once 'DB.php'; 59 $this->_db = &DB::connect($conf['sql']); 60 61 /* Set DB portability options. */ 62 if (is_a($this->_db, 'DB_common')) { 63 switch ($this->_db->phptype) { 64 case 'mssql': 65 $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); 66 break; 67 default: 68 $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); 69 } 70 } 71 } 72 73 /** 74 * Logs an event to an item's history log. The item must be uniquely 75 * identified by $guid. Any other details about the event are passed in 76 * $attributes. Standard suggested attributes are: 77 * 78 * 'who' => The id of the user that performed the action (will be added 79 * automatically if not present). 80 * 81 * 'ts' => Timestamp of the action (this will be added automatically if 82 * it is not present). 83 * 84 * @param string $guid The unique identifier of the entry to 85 * add to. 86 * @param array $attributes The hash of name => value entries that 87 * describe this event. 88 * @param boolean $replaceAction If $attributes['action'] is already 89 * present in the item's history log, 90 * update that entry instead of creating a 91 * new one. 92 * 93 * @return boolean|PEAR_Error True on success, PEAR_Error on failure. 94 */ 95 function log($guid, $attributes = array(), $replaceAction = false) 96 { 97 if (is_a($this->_db, 'PEAR_Error')) { 98 return $this->_db; 99 } 100 101 $history = &$this->getHistory($guid); 102 if (!$history || is_a($history, 'PEAR_Error')) { 103 return $history; 104 } 105 106 if (empty($attributes['who'])) { 107 $attributes['who'] = Auth::getAuth(); 108 } 109 if (empty($attributes['ts'])) { 110 $attributes['ts'] = time(); 111 } 112 113 /* If we want to replace an entry with the same action, try and find 114 * one. Track whether or not we succeed in $done, so we know whether 115 * or not to add the entry later. */ 116 $done = false; 117 if ($replaceAction && !empty($attributes['action'])) { 118 $count = count($history->data); 119 for ($i = 0; $i < $count; $i++) { 120 if (!empty($history->data[$i]['action']) && 121 $history->data[$i]['action'] == $attributes['action']) { 122 123 $values = array($attributes['ts'], 124 $attributes['who'], 125 isset($attributes['desc']) ? $attributes['desc'] : null); 126 unset($attributes['ts']); 127 unset($attributes['who']); 128 unset($attributes['desc']); 129 unset($attributes['action']); 130 if ($attributes) { 131 $values[] = serialize($attributes); 132 } else { 133 $values[] = null; 134 } 135 $values[] = $history->data[$i]['id']; 136 137 $r = $this->_db->query('UPDATE horde_histories SET history_ts = ?,' . 138 ' history_who = ?,' . 139 ' history_desc = ?,' . 140 ' history_extra = ? WHERE history_id = ?', $values); 141 if (is_a($r, 'PEAR_Error')) { 142 return $r; 143 } 144 $done = true; 145 break; 146 } 147 } 148 } 149 150 /* If we're not replacing by action, or if we didn't find an entry to 151 * replace, insert a new row. */ 152 if (!$done) { 153 $values = array($this->_db->nextId('horde_histories'), 154 $guid, 155 $attributes['ts'], 156 $attributes['who'], 157 isset($attributes['desc']) ? $attributes['desc'] : null, 158 isset($attributes['action']) ? $attributes['action'] : null); 159 unset($attributes['ts']); 160 unset($attributes['who']); 161 unset($attributes['desc']); 162 unset($attributes['action']); 163 if ($attributes) { 164 $values[] = serialize($attributes); 165 } else { 166 $values[] = null; 167 } 168 169 $r = $this->_db->query('INSERT INTO horde_histories (history_id, object_uid, history_ts, history_who, history_desc, history_action, history_extra)' . 170 ' VALUES (?, ?, ?, ?, ?, ?, ?)', $values); 171 if (is_a($r, 'PEAR_Error')) { 172 return $r; 173 } 174 } 175 176 return true; 177 } 178 179 /** 180 * Returns a HistoryObject corresponding to the named history 181 * entry, with the data retrieved appropriately. $autocreate has 182 * no affect. 183 * 184 * @param string $guid The name of the history entry to retrieve. 185 * @param boolean $autocreate Deprecated. 186 */ 187 function &getHistory($guid, $autocreate = null) 188 { 189 if (is_a($this->_db, 'PEAR_Error')) { 190 $false = false; 191 return $false; 192 } 193 194 $rows = $this->_db->getAll('SELECT * FROM horde_histories WHERE object_uid = ?', array($guid), DB_FETCHMODE_ASSOC); 195 $history = &new HistoryObject($guid, $rows); 196 return $history; 197 } 198 199 /** 200 * Finds history objects by timestamp, and optionally filter on other 201 * fields as well. 202 * 203 * @param string $cmp The comparison operator (<, >, <=, >=, or =) to 204 * check the timestamps with. 205 * @param integer $ts The timestamp to compare against. 206 * @param array $filters An array of additional (ANDed) criteria. 207 * Each array value should be an array with 3 208 * entries: 209 * <pre> 210 * 'op' - the operator to compare this field 211 * with. 212 * 'field' - the history field being compared 213 * (i.e. 'action'). 214 * 'value' - the value to check for (i.e. 'add'). 215 * </pre> 216 * @param string $parent The parent history to start searching at. If non-empty, 217 * will be searched for with a LIKE '$parent:%' clause. 218 * 219 * @return array An array of history object ids, or an empty array if 220 * none matched the criteria. 221 */ 222 function getByTimestamp($cmp, $ts, $filters = array(), $parent = null) 223 { 224 if (is_a($this->_db, 'PEAR_Error')) { 225 return false; 226 } 227 228 /* Build the timestamp test. */ 229 $where = array("history_ts $cmp $ts"); 230 231 /* Add additional filters, if there are any. */ 232 if ($filters) { 233 foreach ($filters as $filter) { 234 $where[] = 'history_' . $filter['field'] . ' ' . $filter['op'] . ' ' . $this->_db->quote($filter['value']); 235 } 236 } 237 238 if ($parent) { 239 $where[] = 'object_uid LIKE ' . $this->_db->quote($parent . ':%'); 240 } 241 242 return $this->_db->getAssoc('SELECT DISTINCT object_uid, history_id FROM horde_histories WHERE ' . implode(' AND ', $where)); 243 } 244 245 /** 246 * Gets the timestamp of the most recent change to $guid. 247 * 248 * @param string $guid The name of the history entry to retrieve. 249 * @param string $action An action: 'add', 'modify', 'delete', etc. 250 * 251 * @return integer The timestamp, or 0 if no matching entry is found. 252 */ 253 function getActionTimestamp($guid, $action) 254 { 255 /* This implementation still works, but we should be able to 256 * get much faster now with a SELECT MAX(history_ts) 257 * ... query. */ 258 $history = &$this->getHistory($guid); 259 if (!$history || is_a($history, 'PEAR_Error')) { 260 return 0; 261 } 262 263 $last = 0; 264 if (is_array($history->data)) { 265 foreach ($history->data as $entry) { 266 if ($entry['action'] == $action && $entry['ts'] > $last) { 267 $last = $entry['ts']; 268 } 269 } 270 } 271 272 return $last; 273 } 274 275 /** 276 * Remove one or more history entries by name. 277 * 278 * @param array $names The history entries to remove. 279 */ 280 function removeByNames($names) 281 { 282 if (is_a($this->_db, 'PEAR_Error')) { 283 return false; 284 } 285 286 $ids = array(); 287 foreach ($names as $name) { 288 $ids[] = $this->_db->quote($name); 289 } 290 291 return $this->_db->query('DELETE FROM horde_histories WHERE object_uid IN (' . implode(',', $ids) . ')'); 292 } 293 294 } 295 296 /** 297 * Class for presenting History information. 298 * 299 * @author Chuck Hagenbuch <chuck@horde.org> 300 * @since Horde 2.1 301 * @package Horde_History 302 */ 303 class HistoryObject { 304 305 var $uid; 306 var $data = array(); 307 308 function HistoryObject($uid, $data = array()) 309 { 310 $this->uid = $uid; 311 312 if (!$data || is_a($data, 'PEAR_Error')) { 313 return; 314 } 315 316 foreach ($data as $row) { 317 $history = array('action' => $row['history_action'], 318 'desc' => $row['history_desc'], 319 'who' => $row['history_who'], 320 'id' => $row['history_id'], 321 'ts' => $row['history_ts']); 322 if ($row['history_extra']) { 323 $extra = @unserialize($row['history_extra']); 324 if ($extra) { 325 $history = array_merge($history, $extra); 326 } 327 } 328 $this->data[] = $history; 329 } 330 } 331 332 function getData() 333 { 334 return $this->data; 335 } 336 337 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 18:01:28 2007 | par Balluche grâce à PHPXref 0.7 |