[ Index ] |
|
Code source de eGroupWare 1.2.106-2 |
1 <?php 2 /***************************************************************************\ 3 * eGroupWare - File Manager * 4 * http://www.egroupware.org * 5 * Written by: * 6 * - Vinicius Cubas Brand <viniciuscb@users.sourceforge.net> * 7 * sponsored by Thyamad - http://www.thyamad.com * 8 * ------------------------------------------------------------------------- * 9 * Description: File version class handler for VFS (SQL implementation v2) * 10 * ------------------------------------------------------------------------- * 11 * This program is free software; you can redistribute it and/or modify it * 12 * under the terms of the GNU General Public License as published by the * 13 * Free Software Foundation; either version 2 of the License, or (at your * 14 * option) any later version. * 15 \***************************************************************************/ 16 17 class vfs_versionsystem 18 { 19 20 /* The high level database handler object */ 21 // var $db_highlevel; 22 23 /* Stores the possible amount number of old file backups; In an 24 * inserction, this number will be verified and if there are already 25 * $backups backed up for a file, will delete backup of the oldest 26 * (although keeping the record of operations). 0 for no backup system 27 * and -1 to infinite versions. */ 28 var $backups; 29 30 /* tmp dir (without end slash) to store temporary file backups (when 31 * file is snapshotted) */ 32 var $tmp_dir; 33 34 /* Virtual file system base class */ 35 var $vfs; 36 37 /* Stores information about snapshotted files. Array with the file_id 38 as index. */ 39 var $snapshotted_files; 40 41 /* Database handling */ 42 var $db; 43 44 /* Now */ 45 var $now; 46 47 var $account_id; 48 49 var $last_saved_snapshot=-1; 50 51 var $backup_foldername = '_backup'; 52 53 //Operations that create file backups 54 var $backup_operations = array( 55 VFS_OPERATION_EDITED 56 ); 57 58 var $attributes = array( 59 'version_id', /* Integer. Unique to each modification. */ 60 'file_id', /* Integer. Id of the file that modif. belongs.*/ 61 'operation', /* Operation made in modification. */ 62 'modifiedby_id', /* phpGW account_id of who last modified */ 63 'modified', /* Datetime of modification, in SQL format */ 64 'version', /* Version of file prior modification. */ 65 'comment', /* Human-readable description of modification. */ 66 'backup_file_id', /* file_id of file that is a backup . */ 67 'backup_content', /* usable if files are stored in database. */ 68 'src', /* source directory in a copy or move operation.*/ 69 'dest' /* destination directory in a copy or move operation.*/ 70 ); 71 72 /*! 73 * @function vfs_versionsystem 74 * @abstract Object constructor 75 * @author Vinicius Cubas Brand 76 */ 77 function vfs_versionsystem($create_vfs=true) 78 { 79 //use create_vfs=false and after this use $this->set_vfs to keep the 80 // same object (i.e. use reference) in $this->vfs instead of 81 // creating a new object. 82 if ($create_vfs) 83 { 84 $this->vfs =& CreateObject('phpgwapi.vfs'); 85 } 86 87 /* FIXME this takes a value defined in the filescenter 88 * configuration. Better next time to take a value from global api 89 * configuration. must fix here and in the filescenter */ 90 if (array_key_exists('filescenter',$GLOBALS['phpgw_info']['user']['preferences'])) 91 { 92 $this->backups = $GLOBALS['phpgw_info']['user']['preferences']['filescenter']['vfs_backups']; 93 } 94 else 95 { 96 $this->backups = 5; 97 } 98 99 $this->snapshotted_files = array(); 100 $this->db = clone($GLOBALS['phpgw']->db); 101 $this->now = date('Y-m-d H:i:s'); 102 $this->account_id = $GLOBALS['phpgw_info']['user']['account_id']; 103 $this->tmp_dir = $GLOBALS['phpgw_info']['server']['temp_dir']; 104 105 } 106 107 /*! 108 * @function create_initial_version() 109 * @abstract Creates a initial journal entry for a file 110 * @description Must be used after a file has been created. Will create 111 * an initial journal entry in the database. If somehow 112 * the database already have any journal for that file, 113 * this method is wrongly called and will do nothing. 114 * Also if no file is found with that file_id, fails. 115 * 116 * @author Vinicius Cubas Brand 117 */ 118 function create_initial_version($file_id) 119 { 120 if ($GLOBALS['phpgw']->banish_journal) 121 { 122 return; 123 } 124 125 $GLOBALS['phpgw']->banish_journal = true; 126 127 //See if file exists 128 $this->db->select('phpgw_vfs2_files','*', 129 array('file_id'=>$file_id), __LINE__,__FILE__); 130 131 if (!$this->db->next_record()) 132 { 133 $GLOBALS['phpgw']->banish_journal = false; 134 return false; 135 } 136 137 $file_record = $this->db->Record; 138 139 //See if journal for the file already exists 140 $this->db->select('phpgw_vfs2_versioning','*', 141 array('file_id'=>$file_id),__LINE__,__FILE__); 142 143 if ($this->db->next_record()) 144 { 145 $GLOBALS['phpgw']->banish_journal = false; 146 return true; //TODO error message 147 } 148 149 $insert_data = array( 150 'file_id' => $file_record['file_id'], 151 'operation' => VFS_OPERATION_CREATED, 152 'modified' => $this->now, 153 'modifiedby_id' => $this->account_id, 154 'version' => '0.0.0.0' 155 ); 156 157 $res = $this->db->insert('phpgw_vfs2_versioning',$insert_data,null, 158 __LINE__,__FILE__); 159 160 /* $this->db->update('phpgw_vfs2_files',array( 161 'modified' => $insert_data['modified'], 162 'modifiedby_id' => $insert_data['modifiedby_id'] 163 ), 164 array('file_id' => $insert_data['file_id']).__LINE__,__FILE__ 165 );*/ 166 167 168 169 if ($res) 170 { 171 $GLOBALS['phpgw']->banish_journal = false; 172 return true; 173 } 174 175 $GLOBALS['phpgw']->banish_journal = false; 176 return false; 177 } 178 179 /*! 180 * @function save_snapshot() 181 * @abstract Saves a snapshot from a file 182 * @description Must be called before any modification in a file. After 183 * the modification was successful, one must do a vfs_version->commit() 184 * Depending of the type of operation and how backups are set, will 185 * handle backups. If a commit is not made until the end of the script, 186 * no modifications in the journal will be saved. 187 * 188 * @param $file_id int The file_id 189 * @param $operation int A VFS_OPERATION as defined in vfs_shared file 190 * @param $other string Its use will differ depending on the operation: 191 * Copy,Move: $other contains the fake_full_path_clean of destination 192 * 193 * @author Vinicius Cubas Brand 194 * @result bool 195 */ 196 function save_snapshot($file_id,$operation,$comment='',$other='') 197 { 198 //Prevent recursive reentrant when working in vfs->copy, f.inst 199 if ($GLOBALS['phpgw']->banish_journal) 200 { 201 return; 202 } 203 204 $GLOBALS['phpgw']->banish_journal = true; 205 206 $this->db->select('phpgw_vfs2_files','*', 207 array('file_id'=>$file_id), __LINE__,__FILE__); 208 209 if (!$this->db->next_record()) 210 { 211 212 $GLOBALS['phpgw']->banish_journal = false; 213 return false; 214 } 215 216 $file_record = $this->db->Record; 217 218 //If already snapshotted, will do a rollback in the old snapshot 219 //before make a new one. 220 if ($this->snapshotted_files[$file_record['file_id']]) 221 { 222 $this->rollback($file_record['file_id']); 223 } 224 225 //Create a backup if necessary 226 if ($this->backups != 0 && in_array($operation,$this->backup_operations)) 227 { 228 $random_filename = $this->tmp_dir.SEP.$this->random_filename(); 229 230 $this->vfs->cp(array( 231 'from' => $file_record['directory'].SEP.$file_record['name'], 232 'to' => $random_filename, 233 'relatives' => array(RELATIVE_ROOT,RELATIVE_NONE|VFS_REAL) 234 )); 235 236 $this->vfs->set_attributes(array( 237 'string' => $random_filename, 238 'relatives' => array(RELATIVE_NONE|VFS_REAL), 239 'attributes' => array('is_backup' => 'Y') 240 )); 241 242 } 243 244 //backup_file_id and backup_data will be set in commit() only. 245 $insert_data = array( 246 'file_id' => $file_record['file_id'], 247 'operation' => $operation, 248 'modifiedby_id' => $this->account_id, 249 'modified' => $this->now, //Datetime of entry 250 'version' => $file_record['version'], 251 'comment' => $comment, 252 ); 253 254 if ($operation == VFS_OPERATION_COPIED || $operation == VFS_OPERATION_MOVED) 255 { 256 $insert_data['src'] = $file_record['directory'].'/'.$file_record['name']; 257 $insert_data['dest'] = $other['dest']; 258 259 } 260 261 /* $file_data is the information of the file, stored in 262 * $this->snapshotted_files. 'insert_data' have the data to be 263 * inserted in the versioning table, 'tmp_filename' is the name of 264 * the temporary backup copy, if any, and 'record' is the 265 * information of the file before changes (that will be made between 266 * the call to save_snapshot() and the call to commit(). 267 */ 268 $file_data = array( 269 'insert_data' => $insert_data, 270 'tmp_filename' => $random_filename, 271 'record' => $file_record 272 ); 273 274 $this->snapshotted_files[$file_id] = $file_data; 275 $this->last_saved_snapshot = $file_id; 276 277 $GLOBALS['phpgw']->banish_journal = false; 278 return true; 279 } 280 281 /*! 282 * @function commit() 283 * @abstract Commits the creation of a journal entry 284 * @description Will have to be called after a save_snapshot is made. 285 * If a vfs_version->save_snapshot() call is not made before, this 286 * method does nothing. If no parameter is passed, will commit the 287 * file from the last saved snapshot. 288 * 289 * @param $file_id int The file_id 290 * 291 * @author Vinicius Cubas Brand 292 * @result bool 293 */ 294 function commit($file_id=-1) 295 { 296 //Prevent recursive reentrant when working in vfs->copy, f.inst 297 if ($GLOBALS['phpgw']->banish_journal) 298 { 299 return; 300 } 301 302 $GLOBALS['phpgw']->banish_journal = true; 303 304 if ($file_id == -1) 305 { 306 if ($this->last_saved_snapshot == -1) 307 { 308 309 $GLOBALS['phpgw']->banish_journal = false; 310 return false; 311 } 312 313 $file_id = $this->last_saved_snapshot; 314 } 315 316 if (!$this->snapshotted_files[$file_id]) 317 { 318 319 $GLOBALS['phpgw']->banish_journal = false; 320 return false; 321 } 322 323 $file_data = $this->snapshotted_files[$file_id]; 324 325 //if there is any backup to be made, will make these backups and 326 //remove too old backup versions, as defined in configuration. 327 if ($this->backups != 0 && in_array($file_data['insert_data']['operation'],$this->backup_operations)) 328 { 329 330 //counts the number of stored backups 331 $where = "file_id=$file_id AND (backup_file_id != NULL OR backup_file_id != 0)"; 332 333 $this->db->select('phpgw_vfs2_versioning','count(*)',$where, 334 __LINE__,__FILE__); 335 336 $this->db->next_record(); 337 338 if ($this->db->Record[0] >= $this->backups && $this->backups != -1) 339 { 340 //Remove old backups 341 342 //Deletes oldest backup(s) 343 $backups_to_be_deleted = $this->db->Record[0] - $this->backups + 1; 344 345 $sql = "SELECT vers.version_id as version_id, 346 vers.backup_file_id as backup_file_id, 347 files.directory as directory, 348 files.name as name 349 FROM phpgw_vfs2_versioning as vers, 350 phpgw_vfs2_files as files 351 WHERE vers.file_id=$file_id 352 AND vers.backup_file_id = files.file_id 353 ORDER BY vers.modified"; 354 355 $this->db->query($sql,__LINE__,__FILE__); 356 357 for ($i = 0; $i < $backups_to_be_deleted; $i++) 358 { 359 //FIXME don't know why this only works 4 the 1st cycle 360 $this->db->next_record(); 361 362 $version_file_id = $this->db->Record['backup_file_id']; 363 $version_id = $this->db->Record['version_id']; 364 365 366 $version_directory = $this->db->Record['directory']; 367 $version_name = $this->db->Record['name']; 368 369 //Removes old backup 370 $this->vfs->rm(array( 371 'string' => $version_directory.SEP.$version_name, 372 'relatives' => array(RELATIVE_ROOT) 373 )); 374 375 $versions_to_update[] = $version_id; 376 377 } 378 379 if ($versions_to_update) 380 { 381 //updates old journal 382 $update_data = array( 383 'backup_file_id' => '', 384 'backup_content' => '' 385 ); 386 387 foreach ($versions_to_update as $key => $val) 388 { 389 390 $update_where = array( 391 'version_id' => $val 392 ); 393 394 $this->db->update('phpgw_vfs2_versioning', 395 $update_data,$update_where,__LINE__,__FILE__); 396 } 397 398 } 399 unset($version_id); 400 } 401 402 //create backup folder, if not exists 403 //Important: the backup dir will be inside the virtual root 404 $backup_foldername = $file_data['record']['directory'].SEP.$this->backup_foldername; 405 406 $dir = array( 407 'string' => $backup_foldername, 408 'relatives' => array(RELATIVE_ROOT) 409 ); 410 411 if (!$this->vfs->file_exists($dir)) 412 { 413 $this->vfs->mkdir($dir); //TODO error messages 414 415 $attributes=array_merge($dir,array( 416 'attributes' => array( 417 'is_backup' => 'Y' 418 ) 419 )); 420 421 $this->vfs->set_attributes($attributes); 422 } 423 424 //create a backup filename 425 $backup_filename = $this->backup_filename( 426 $file_data['record']['name'], 427 $file_data['insert_data']['version'] 428 ); 429 430 //move file from temporary location to its definitive location 431 $res = $this->vfs->mv(array( 432 'from' => $file_data['tmp_filename'], 433 'to' => $backup_foldername.SEP.$backup_filename, 434 'relatives' => array(RELATIVE_NONE|VFS_REAL,RELATIVE_ROOT) 435 )); 436 437 //sets its attribute as backup 438 $this->vfs->set_attributes(array( 439 'string' => $backup_foldername.SEP.$backup_filename, 440 'relatives' => array(RELATIVE_ROOT), 441 'attributes' => array('is_backup' => 'Y') 442 )); 443 444 //TODO backup content in database support 445 446 //Fetch the backup file_id to put this information in the 447 //version table 448 if ($res) 449 { 450 $res_ls = $this->vfs->ls(array( 451 'string' => $backup_foldername.SEP.$backup_filename, 452 'relatives' => RELATIVE_ROOT, 453 'nofiles' => True, 454 'backups' => True 455 )); 456 457 if ($res_ls) 458 { 459 $file_data['insert_data']['backup_file_id'] = $res_ls[0]['file_id']; 460 } 461 } 462 } 463 464 $res = $this->db->insert('phpgw_vfs2_versioning', 465 $file_data['insert_data'],null,__LINE__,__FILE__); 466 467 468 469 if ($res) 470 { 471 //If operation is one of the type that increments file version 472 if (in_array($file_data['insert_data']['operation'],$this->backup_operations)) 473 { 474 475 $this->db->update('phpgw_vfs2_files', 476 array( 477 'version' => $this->inc($file_data['insert_data']['version']), 478 'modified' => $file_data['insert_data']['modified'], 479 'modifiedby_id' => $file_data['insert_data']['modifiedby_id'] 480 ), 481 array('file_id' => $file_data['insert_data']['file_id']), 482 __LINE__, __FILE__ 483 ); 484 } 485 486 unset($this->snapshotted_files[$file_id]); 487 $this->last_saved_snapshot = -1; 488 489 $GLOBALS['phpgw']->banish_journal = false; 490 return true; 491 } 492 493 $GLOBALS['phpgw']->banish_journal = false; 494 return false; 495 } 496 497 498 /*! 499 * @function rollback() 500 * @abstract Rollbacks the save of the snapshot 501 * @description Will have to be called after a save_snapshot is made. 502 * If a vfs_version->save_snapshot() call is not made before, this 503 * method does nothing. If no parameter is passed, will rollback the 504 * file from the last saved snapshot. This method only deletes the 505 * temporary backup file and the saved file information 506 * 507 * @param $file_id int The file_id 508 * 509 * @author Vinicius Cubas Brand 510 * @result bool 511 */ 512 function rollback($file_id=-1) 513 { 514 //Prevent recursive reentrant when working in vfs->copy, f.inst 515 if ($GLOBALS['phpgw']->banish_journal) 516 { 517 return; 518 } 519 520 $GLOBALS['phpgw']->banish_journal = true; 521 522 if ($file_id == -1) 523 { 524 if ($this->last_saved_snapshot == -1) 525 { 526 527 $GLOBALS['phpgw']->banish_journal = false; 528 return false; 529 } 530 531 $file_id = $this->last_saved_snapshot; 532 } 533 534 if (!$this->snapshotted_files[$file_id]) 535 { 536 537 $GLOBALS['phpgw']->banish_journal = false; 538 return false; 539 } 540 541 $file_data = $this->snapshotted_files[$file_id]; 542 543 $this->vfs->rm(array( 544 'string' => $file_data['tmp_filename'], 545 'relatives' => array(RELATIVE_NONE | VFS_REAL) 546 )); 547 548 unset($this->snapshotted_files[$file_id]); 549 $this->last_saved_snapshot = -1; 550 551 $GLOBALS['phpgw']->banish_journal = false; 552 return true; 553 } 554 555 /*! 556 * @function get_journal() 557 * @abstract Returns an array with the journal for a file 558 */ 559 function get_journal($file_id) 560 { 561 //TODO support for database-only storage. 562 $fields = array_diff($this->attributes,array('backup_content')); 563 564 $where = 'file_id='.$file_id.' ORDER BY modified DESC, version DESC, operation DESC'; 565 566 567 $this->db->select('phpgw_vfs2_versioning',$fields,$where, 568 __LINE__,__FILE__); 569 570 while ($this->db->next_record()) 571 { 572 $result[] = $this->db->Record; 573 } 574 575 return $result; 576 } 577 578 579 /*! 580 * @function inc() 581 * @abstract Given a file version, increments it using the vfs 582 * versioning pattern and returns the incremented file version. 583 * Analyzes operation and increments the file version taking 584 * consideration of this operation. 585 * 586 * @param $version string The file version 587 * @param $operation int Some VFS_OPERATION as defined in vfs_shared 588 * 589 * @result string 590 */ 591 function inc($version) 592 { 593 /* 594 * Let's increment the version for the file itself. We keep the 595 * current version when making the journal entry, because that was 596 * the version that was operated on. The maximum numbers for each 597 * part in the version string: none.99.9.9 598 */ 599 $version_parts = split ("\.", $version); 600 $newnumofparts = $numofparts = count ($version_parts); 601 602 if ($version_parts[3] >= 9) 603 { 604 $version_parts[3] = 0; 605 $version_parts[2]++; 606 $version_parts_3_update = 1; 607 } 608 elseif (isset ($version_parts[3])) 609 { 610 $version_parts[3]++; 611 } 612 613 if ($version_parts[2] >= 9 && $version_parts[3] == 0 && $version_parts_3_update) 614 { 615 $version_parts[2] = 0; 616 $version_parts[1]++; 617 } 618 619 if ($version_parts[1] > 99) 620 { 621 $version_parts[1] = 0; 622 $version_parts[0]++; 623 } 624 625 for ($i = 0; $i < $newnumofparts; $i++) 626 { 627 if (!isset ($version_parts[$i])) 628 { 629 break; 630 } 631 632 if ($i) 633 { 634 $newversion .= '.'; 635 } 636 637 $newversion .= $version_parts[$i]; 638 } 639 640 return $newversion; 641 } 642 643 function set_vfs(&$vfs) 644 { 645 $this->vfs =& $vfs; 646 } 647 648 #helper, private functions 649 650 /*! 651 * @function random_filename() 652 * @abstract Generates a Random Filename 653 * 654 * @result string 655 */ 656 function random_filename() 657 { 658 $filename = ''; 659 $filename_length = 8; 660 while (strlen($filename) < $filename_length) { 661 $filename .= chr(rand (97,122)); 662 } 663 664 return $filename.'.tmp'; 665 } 666 667 /*! 668 * @function backup_filename() 669 * @abstract Return the backup filename for a certain filename + version 670 * 671 * @result string 672 */ 673 function backup_filename($filename,$version) 674 { 675 $version = str_replace('.','_',$version); 676 $fbrk = explode('.',$filename); 677 $fbrk[0] .= '-'.$version; 678 return implode('.',$fbrk); 679 } 680 681 } 682 683 684 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 17:20:01 2007 | par Balluche grâce à PHPXref 0.7 |