| [ Index ] |
PHP Cross Reference of Joomla 1.5.26 DE |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * @version $Id: installer.php 14401 2010-01-26 14:10:00Z louis $ 4 * @package Joomla.Framework 5 * @subpackage Installer 6 * @copyright Copyright (C) 2005 - 2010 Open Source Matters. All rights reserved. 7 * @license GNU/GPL, see LICENSE.php 8 * Joomla! is free software. This version may have been modified pursuant 9 * to the GNU General Public License, and as distributed it includes or 10 * is derivative of works licensed under the GNU General Public License or 11 * other free or open source software licenses. 12 * See COPYRIGHT.php for copyright notices and details. 13 */ 14 15 // Check to ensure this file is within the rest of the framework 16 defined('JPATH_BASE') or die(); 17 18 jimport('joomla.filesystem.file'); 19 jimport('joomla.filesystem.folder'); 20 jimport('joomla.filesystem.archive'); 21 jimport('joomla.filesystem.path'); 22 23 /** 24 * Joomla base installer class 25 * 26 * @package Joomla.Framework 27 * @subpackage Installer 28 * @since 1.5 29 */ 30 class JInstaller extends JObject 31 { 32 /** 33 * Array of paths needed by the installer 34 * @var array 35 */ 36 var $_paths = array(); 37 38 /** 39 * The installation manifest XML object 40 * @var object 41 */ 42 var $_manifest = null; 43 44 /** 45 * True if existing files can be overwritten 46 * @var boolean 47 */ 48 var $_overwrite = false; 49 50 /** 51 * A database connector object 52 * @var object 53 */ 54 var $_db = null; 55 56 /** 57 * Associative array of package installer handlers 58 * @var array 59 */ 60 var $_adapters = array(); 61 62 /** 63 * Stack of installation steps 64 * - Used for installation rollback 65 * @var array 66 */ 67 var $_stepStack = array(); 68 69 /** 70 * The output from the install/uninstall scripts 71 * @var string 72 */ 73 var $message = null; 74 75 /** 76 * Constructor 77 * 78 * @access protected 79 */ 80 function __construct() 81 { 82 $this->_db =& JFactory::getDBO(); 83 } 84 85 /** 86 * Returns a reference to the global Installer object, only creating it 87 * if it doesn't already exist. 88 * 89 * @static 90 * @return object An installer object 91 * @since 1.5 92 */ 93 function &getInstance() 94 { 95 static $instance; 96 97 if (!isset ($instance)) { 98 $instance = new JInstaller(); 99 } 100 return $instance; 101 } 102 103 /** 104 * Get the allow overwrite switch 105 * 106 * @access public 107 * @return boolean Allow overwrite switch 108 * @since 1.5 109 */ 110 function getOverwrite() 111 { 112 return $this->_overwrite; 113 } 114 115 /** 116 * Set the allow overwrite switch 117 * 118 * @access public 119 * @param boolean $state Overwrite switch state 120 * @return boolean Previous value 121 * @since 1.5 122 */ 123 function setOverwrite($state=false) 124 { 125 $tmp = $this->_overwrite; 126 if ($state) { 127 $this->_overwrite = true; 128 } else { 129 $this->_overwrite = false; 130 } 131 return $tmp; 132 } 133 134 /** 135 * Get the database connector object 136 * 137 * @access public 138 * @return object Database connector object 139 * @since 1.5 140 */ 141 function &getDBO() 142 { 143 return $this->_db; 144 } 145 146 /** 147 * Get the installation manifest object 148 * 149 * @access public 150 * @return object Manifest object 151 * @since 1.5 152 */ 153 function &getManifest() 154 { 155 if (!is_object($this->_manifest)) { 156 $this->_findManifest(); 157 } 158 return $this->_manifest; 159 } 160 161 /** 162 * Get an installer path by name 163 * 164 * @access public 165 * @param string $name Path name 166 * @param string $default Default value 167 * @return string Path 168 * @since 1.5 169 */ 170 function getPath($name, $default=null) 171 { 172 return (!empty($this->_paths[$name])) ? $this->_paths[$name] : $default; 173 } 174 175 /** 176 * Sets an installer path by name 177 * 178 * @access public 179 * @param string $name Path name 180 * @param string $value Path 181 * @return void 182 * @since 1.5 183 */ 184 function setPath($name, $value) 185 { 186 $this->_paths[$name] = $value; 187 } 188 189 /** 190 * Pushes a step onto the installer stack for rolling back steps 191 * 192 * @access public 193 * @param array $step Installer step 194 * @return void 195 * @since 1.5 196 */ 197 function pushStep($step) 198 { 199 $this->_stepStack[] = $step; 200 } 201 202 /** 203 * Set an installer adapter by name 204 * 205 * @access public 206 * @param string $name Adapter name 207 * @param object $adapter Installer adapter object 208 * @return boolean True if successful 209 * @since 1.5 210 */ 211 function setAdapter($name, $adapter = null) 212 { 213 if (!is_object($adapter)) 214 { 215 // Try to load the adapter object 216 require_once(dirname(__FILE__).DS.'adapters'.DS.strtolower($name).'.php'); 217 $class = 'JInstaller'.ucfirst($name); 218 if (!class_exists($class)) { 219 return false; 220 } 221 $adapter = new $class($this); 222 $adapter->parent =& $this; 223 } 224 $this->_adapters[$name] =& $adapter; 225 return true; 226 } 227 228 /** 229 * Installation abort method 230 * 231 * @access public 232 * @param string $msg Abort message from the installer 233 * @param string $type Package type if defined 234 * @return boolean True if successful 235 * @since 1.5 236 */ 237 function abort($msg=null, $type=null) 238 { 239 // Initialize variables 240 $retval = true; 241 $step = array_pop($this->_stepStack); 242 243 // Raise abort warning 244 if ($msg) { 245 JError::raiseWarning(100, $msg); 246 } 247 248 while ($step != null) 249 { 250 switch ($step['type']) 251 { 252 case 'file' : 253 // remove the file 254 $stepval = JFile::delete($step['path']); 255 break; 256 257 case 'folder' : 258 // remove the folder 259 $stepval = JFolder::delete($step['path']); 260 break; 261 262 case 'query' : 263 // placeholder in case this is necessary in the future 264 break; 265 266 default : 267 if ($type && is_object($this->_adapters[$type])) { 268 // Build the name of the custom rollback method for the type 269 $method = '_rollback_'.$step['type']; 270 // Custom rollback method handler 271 if (method_exists($this->_adapters[$type], $method)) { 272 $stepval = $this->_adapters[$type]->$method($step); 273 } 274 } 275 break; 276 } 277 278 // Only set the return value if it is false 279 if ($stepval === false) { 280 $retval = false; 281 } 282 283 // Get the next step and continue 284 $step = array_pop($this->_stepStack); 285 } 286 287 return $retval; 288 } 289 290 /** 291 * Package installation method 292 * 293 * @access public 294 * @param string $path Path to package source folder 295 * @return boolean True if successful 296 * @since 1.5 297 */ 298 function install($path=null) 299 { 300 if ($path && JFolder::exists($path)) { 301 $this->setPath('source', $path); 302 } else { 303 $this->abort(JText::_('Install path does not exist')); 304 return false; 305 } 306 307 if (!$this->setupInstall()) { 308 $this->abort(JText::_('Unable to detect manifest file')); 309 return false; 310 } 311 312 /* 313 * LEGACY CHECK 314 */ 315 $root =& $this->_manifest->document; 316 $version = $root->attributes('version'); 317 $rootName = $root->name(); 318 $config = &JFactory::getConfig(); 319 if ((version_compare($version, '1.5', '<') || $rootName == 'mosinstall') && !$config->getValue('config.legacy')) { 320 $this->abort(JText::_('MUSTENABLELEGACY')); 321 return false; 322 } 323 324 $type = $root->attributes('type'); 325 326 // Needed for legacy reasons ... to be deprecated in next minor release 327 if ($type == 'mambot') { 328 $type = 'plugin'; 329 } 330 331 if (is_object($this->_adapters[$type])) { 332 return $this->_adapters[$type]->install(); 333 } 334 return false; 335 } 336 337 /** 338 * Package update method 339 * 340 * @access public 341 * @param string $path Path to package source folder 342 * @return boolean True if successful 343 * @since 1.5 344 */ 345 function update($path=null) 346 { 347 if ($path && JFolder::exists($path)) { 348 $this->setPath('source', $path); 349 } else { 350 $this->abort(JText::_('Update path does not exist')); 351 } 352 353 if (!$this->setupInstall()) { 354 return $this->abort(JText::_('Unable to detect manifest file')); 355 } 356 357 /* 358 * LEGACY CHECK 359 */ 360 $root =& $this->_manifest->document; 361 $version = $root->attributes('version'); 362 $rootName = $root->name(); 363 $config = &JFactory::getConfig(); 364 if ((version_compare($version, '1.5', '<') || $rootName == 'mosinstall') && !$config->getValue('config.legacy')) { 365 return $this->abort(JText::_('MUSTENABLELEGACY')); 366 } 367 368 $type = $root->attributes('type'); 369 370 // Needed for legacy reasons ... to be deprecated in next minor release 371 if ($type == 'mambot') { 372 $type = 'plugin'; 373 } 374 375 if (is_object($this->_adapters[$type])) { 376 return $this->_adapters[$type]->update(); 377 } 378 return false; 379 } 380 381 /** 382 * Package uninstallation method 383 * 384 * @access public 385 * @param string $type Package type 386 * @param mixed $identifier Package identifier for adapter 387 * @param int $cid Application ID 388 * @return boolean True if successful 389 * @since 1.5 390 */ 391 function uninstall($type, $identifier, $cid=0) 392 { 393 if (!isset($this->_adapters[$type]) || !is_object($this->_adapters[$type])) { 394 if (!$this->setAdapter($type)) { 395 return false; 396 } 397 } 398 if (is_object($this->_adapters[$type])) { 399 return $this->_adapters[$type]->uninstall($identifier, $cid); 400 } 401 return false; 402 } 403 404 /** 405 * Prepare for installation: this method sets the installation directory, finds 406 * and checks the installation file and verifies the installation type 407 * 408 * @access public 409 * @return boolean True on success 410 * @since 1.0 411 */ 412 function setupInstall() 413 { 414 // We need to find the installation manifest file 415 if (!$this->_findManifest()) { 416 return false; 417 } 418 419 // Load the adapter(s) for the install manifest 420 $root =& $this->_manifest->document; 421 $type = $root->attributes('type'); 422 423 // Needed for legacy reasons ... to be deprecated in next minor release 424 if ($type == 'mambot') { 425 $type = 'plugin'; 426 } 427 428 // Lazy load the adapter 429 if (!isset($this->_adapters[$type]) || !is_object($this->_adapters[$type])) { 430 if (!$this->setAdapter($type)) { 431 return false; 432 } 433 } 434 435 return true; 436 } 437 438 /** 439 * Backward compatible Method to parse through a queries element of the 440 * installation manifest file and take appropriate action. 441 * 442 * @access public 443 * @param object $element The xml node to process 444 * @return mixed Number of queries processed or False on error 445 * @since 1.5 446 */ 447 function parseQueries($element) 448 { 449 // Get the database connector object 450 $db = & $this->_db; 451 452 if (!is_a($element, 'JSimpleXMLElement') || !count($element->children())) { 453 // Either the tag does not exist or has no children therefore we return zero files processed. 454 return 0; 455 } 456 457 // Get the array of query nodes to process 458 $queries = $element->children(); 459 if (count($queries) == 0) { 460 // No queries to process 461 return 0; 462 } 463 464 // Process each query in the $queries array (children of $tagName). 465 foreach ($queries as $query) 466 { 467 $db->setQuery($query->data()); 468 if (!$db->query()) { 469 JError::raiseWarning(1, 'JInstaller::install: '.JText::_('SQL Error')." ".$db->stderr(true)); 470 return false; 471 } 472 } 473 return (int) count($queries); 474 } 475 476 /** 477 * Method to extract the name of a discreet installation sql file from the installation manifest file. 478 * 479 * @access public 480 * @param object $element The xml node to process 481 * @param string $version The database connector to use 482 * @return mixed Number of queries processed or False on error 483 * @since 1.5 484 */ 485 function parseSQLFiles($element) 486 { 487 // Initialize variables 488 $queries = array(); 489 $db = & $this->_db; 490 $dbDriver = strtolower($db->get('name')); 491 if ($dbDriver == 'mysqli') { 492 $dbDriver = 'mysql'; 493 } 494 $dbCharset = ($db->hasUTF()) ? 'utf8' : ''; 495 496 if (!is_a($element, 'JSimpleXMLElement')) { 497 // The tag does not exist. 498 return 0; 499 } 500 501 // Get the array of file nodes to process 502 $files = $element->children(); 503 if (count($files) == 0) { 504 // No files to process 505 return 0; 506 } 507 508 // Get the name of the sql file to process 509 $sqlfile = ''; 510 foreach ($files as $file) 511 { 512 $fCharset = (strtolower($file->attributes('charset')) == 'utf8') ? 'utf8' : ''; 513 $fDriver = strtolower($file->attributes('driver')); 514 if ($fDriver == 'mysqli') { 515 $fDriver = 'mysql'; 516 } 517 518 if( $fCharset == $dbCharset && $fDriver == $dbDriver) { 519 $sqlfile = $file->data(); 520 // Check that sql files exists before reading. Otherwise raise error for rollback 521 if ( !file_exists( $this->getPath('extension_administrator').DS.$sqlfile ) ) { 522 return false; 523 } 524 $buffer = file_get_contents($this->getPath('extension_administrator').DS.$sqlfile); 525 526 // Graceful exit and rollback if read not successful 527 if ( $buffer === false ) { 528 return false; 529 } 530 531 // Create an array of queries from the sql file 532 jimport('joomla.installer.helper'); 533 $queries = JInstallerHelper::splitSql($buffer); 534 535 if (count($queries) == 0) { 536 // No queries to process 537 return 0; 538 } 539 540 // Process each query in the $queries array (split out of sql file). 541 foreach ($queries as $query) 542 { 543 $query = trim($query); 544 if ($query != '' && $query{0} != '#') { 545 $db->setQuery($query); 546 if (!$db->query()) { 547 JError::raiseWarning(1, 'JInstaller::install: '.JText::_('SQL Error')." ".$db->stderr(true)); 548 return false; 549 } 550 } 551 } 552 } 553 } 554 555 return (int) count($queries); 556 } 557 558 /** 559 * Method to parse through a files element of the installation manifest and take appropriate 560 * action. 561 * 562 * @access public 563 * @param object $element The xml node to process 564 * @param int $cid Application ID of application to install to 565 * @return boolean True on success 566 * @since 1.5 567 */ 568 function parseFiles($element, $cid=0) 569 { 570 // Initialize variables 571 $copyfiles = array (); 572 573 // Get the client info 574 jimport('joomla.application.helper'); 575 $client =& JApplicationHelper::getClientInfo($cid); 576 577 if (!is_a($element, 'JSimpleXMLElement') || !count($element->children())) { 578 // Either the tag does not exist or has no children therefore we return zero files processed. 579 return 0; 580 } 581 582 // Get the array of file nodes to process 583 $files = $element->children(); 584 if (count($files) == 0) { 585 // No files to process 586 return 0; 587 } 588 589 /* 590 * Here we set the folder we are going to remove the files from. 591 */ 592 if ($client) { 593 $pathname = 'extension_'.$client->name; 594 $destination = $this->getPath($pathname); 595 } else { 596 $pathname = 'extension_root'; 597 $destination = $this->getPath($pathname); 598 } 599 600 /* 601 * Here we set the folder we are going to copy the files from. 602 * 603 * Does the element have a folder attribute? 604 * 605 * If so this indicates that the files are in a subdirectory of the source 606 * folder and we should append the folder attribute to the source path when 607 * copying files. 608 */ 609 if ($folder = $element->attributes('folder')) { 610 $source = $this->getPath('source').DS.$folder; 611 } else { 612 $source = $this->getPath('source'); 613 } 614 615 // Process each file in the $files array (children of $tagName). 616 foreach ($files as $file) 617 { 618 $path['src'] = $source.DS.$file->data(); 619 $path['dest'] = $destination.DS.$file->data(); 620 621 // Is this path a file or folder? 622 $path['type'] = ( $file->name() == 'folder') ? 'folder' : 'file'; 623 624 /* 625 * Before we can add a file to the copyfiles array we need to ensure 626 * that the folder we are copying our file to exits and if it doesn't, 627 * we need to create it. 628 */ 629 if (basename($path['dest']) != $path['dest']) { 630 $newdir = dirname($path['dest']); 631 632 if (!JFolder::create($newdir)) { 633 JError::raiseWarning(1, 'JInstaller::install: '.JText::_('Failed to create directory').' "'.$newdir.'"'); 634 return false; 635 } 636 } 637 638 // Add the file to the copyfiles array 639 $copyfiles[] = $path; 640 } 641 642 return $this->copyFiles($copyfiles); 643 } 644 645 /** 646 * Method to parse through a languages element of the installation manifest and take appropriate 647 * action. 648 * 649 * @access public 650 * @param object $element The xml node to process 651 * @param int $cid Application ID of application to install to 652 * @return boolean True on success 653 * @since 1.5 654 */ 655 function parseLanguages($element, $cid=0) 656 { 657 // Initialize variables 658 $copyfiles = array (); 659 660 // Get the client info 661 jimport('joomla.application.helper'); 662 $client =& JApplicationHelper::getClientInfo($cid); 663 664 if (!is_a($element, 'JSimpleXMLElement') || !count($element->children())) { 665 // Either the tag does not exist or has no children therefore we return zero files processed. 666 return 0; 667 } 668 669 // Get the array of file nodes to process 670 $files = $element->children(); 671 if (count($files) == 0) { 672 // No files to process 673 return 0; 674 } 675 676 /* 677 * Here we set the folder we are going to copy the files to. 678 * 679 * 'languages' Files are copied to JPATH_BASE/language/ folder 680 */ 681 $destination = $client->path.DS.'language'; 682 683 /* 684 * Here we set the folder we are going to copy the files from. 685 * 686 * Does the element have a folder attribute? 687 * 688 * If so this indicates that the files are in a subdirectory of the source 689 * folder and we should append the folder attribute to the source path when 690 * copying files. 691 */ 692 if ($folder = $element->attributes('folder')) { 693 $source = $this->getPath('source').DS.$folder; 694 } else { 695 $source = $this->getPath('source'); 696 } 697 698 // Process each file in the $files array (children of $tagName). 699 foreach ($files as $file) 700 { 701 /* 702 * Language files go in a subfolder based on the language code, ie. 703 * 704 * <language tag="en-US">en-US.mycomponent.ini</language> 705 * 706 * would go in the en-US subdirectory of the language folder. 707 * 708 * We will only install language files where a core language pack 709 * already exists. 710 */ 711 if ($file->attributes('tag') != '') { 712 $path['src'] = $source.DS.$file->data(); 713 $path['dest'] = $destination.DS.$file->attributes('tag').DS.basename($file->data()); 714 715 // If the language folder is not present, then the core pack hasn't been installed... ignore 716 if (!JFolder::exists(dirname($path['dest']))) { 717 continue; 718 } 719 } else { 720 $path['src'] = $source.DS.$file->data(); 721 $path['dest'] = $destination.DS.$file->data(); 722 } 723 724 /* 725 * Before we can add a file to the copyfiles array we need to ensure 726 * that the folder we are copying our file to exits and if it doesn't, 727 * we need to create it. 728 */ 729 if (basename($path['dest']) != $path['dest']) { 730 $newdir = dirname($path['dest']); 731 732 if (!JFolder::create($newdir)) { 733 JError::raiseWarning(1, 'JInstaller::install: '.JText::_('Failed to create directory').' "'.$newdir.'"'); 734 return false; 735 } 736 } 737 738 // Add the file to the copyfiles array 739 $copyfiles[] = $path; 740 } 741 742 return $this->copyFiles($copyfiles); 743 } 744 745 /** 746 * Method to parse through a media element of the installation manifest and take appropriate 747 * action. 748 * 749 * @access public 750 * @param object $element The xml node to process 751 * @param int $cid Application ID of application to install to 752 * @return boolean True on success 753 * @since 1.5 754 */ 755 function parseMedia($element, $cid=0) 756 { 757 // Initialize variables 758 $copyfiles = array (); 759 760 // Get the client info 761 jimport('joomla.application.helper'); 762 $client =& JApplicationHelper::getClientInfo($cid); 763 764 if (!is_a($element, 'JSimpleXMLElement') || !count($element->children())) { 765 // Either the tag does not exist or has no children therefore we return zero files processed. 766 return 0; 767 } 768 769 // Get the array of file nodes to process 770 $files = $element->children(); 771 if (count($files) == 0) { 772 // No files to process 773 return 0; 774 } 775 776 /* 777 * Here we set the folder we are going to copy the files to. 778 * Default 'media' Files are copied to the JPATH_BASE/media folder 779 */ 780 $folder = ($element->attributes('destination')) ? DS.$element->attributes('destination') : null; 781 $destination = JPath::clean(JPATH_ROOT.DS.'media'.$folder); 782 783 /* 784 * Here we set the folder we are going to copy the files from. 785 * 786 * Does the element have a folder attribute? 787 * 788 * If so this indicates that the files are in a subdirectory of the source 789 * folder and we should append the folder attribute to the source path when 790 * copying files. 791 */ 792 if ($folder = $element->attributes('folder')) { 793 $source = $this->getPath('source').DS.$folder; 794 } else { 795 $source = $this->getPath('source'); 796 } 797 798 // Process each file in the $files array (children of $tagName). 799 foreach ($files as $file) 800 { 801 $path['src'] = $source.DS.$file->data(); 802 $path['dest'] = $destination.DS.$file->data(); 803 804 // Is this path a file or folder? 805 $path['type'] = ( $file->name() == 'folder') ? 'folder' : 'file'; 806 807 /* 808 * Before we can add a file to the copyfiles array we need to ensure 809 * that the folder we are copying our file to exits and if it doesn't, 810 * we need to create it. 811 */ 812 if (basename($path['dest']) != $path['dest']) { 813 $newdir = dirname($path['dest']); 814 815 if (!JFolder::create($newdir)) { 816 JError::raiseWarning(1, 'JInstaller::install: '.JText::_('Failed to create directory').' "'.$newdir.'"'); 817 return false; 818 } 819 } 820 821 // Add the file to the copyfiles array 822 $copyfiles[] = $path; 823 } 824 825 return $this->copyFiles($copyfiles); 826 } 827 828 /** 829 * Method to parse the parameters of an extension, build the INI 830 * string for it's default parameters, and return the INI string. 831 * 832 * @access public 833 * @return string INI string of parameter values 834 * @since 1.5 835 */ 836 function getParams() 837 { 838 // Get the manifest document root element 839 $root = & $this->_manifest->document; 840 841 // Get the element of the tag names 842 $element =& $root->getElementByPath('params'); 843 if (!is_a($element, 'JSimpleXMLElement') || !count($element->children())) { 844 // Either the tag does not exist or has no children therefore we return zero files processed. 845 return null; 846 } 847 848 // Get the array of parameter nodes to process 849 $params = $element->children(); 850 if (count($params) == 0) { 851 // No params to process 852 return null; 853 } 854 855 // Process each parameter in the $params array. 856 $ini = null; 857 foreach ($params as $param) { 858 if (!$name = $param->attributes('name')) { 859 continue; 860 } 861 862 if (!$value = $param->attributes('default')) { 863 continue; 864 } 865 866 $ini .= $name."=".$value."\n"; 867 } 868 return $ini; 869 } 870 871 /** 872 * Copy files from source directory to the target directory 873 * 874 * @access public 875 * @param array $files array with filenames 876 * @param boolean $overwrite True if existing files can be replaced 877 * @return boolean True on success 878 * @since 1.5 879 */ 880 function copyFiles($files, $overwrite=null) 881 { 882 /* 883 * To allow for manual override on the overwriting flag, we check to see if 884 * the $overwrite flag was set and is a boolean value. If not, use the object 885 * allowOverwrite flag. 886 */ 887 if (is_null($overwrite) || !is_bool($overwrite)) { 888 $overwrite = $this->_overwrite; 889 } 890 891 /* 892 * $files must be an array of filenames. Verify that it is an array with 893 * at least one file to copy. 894 */ 895 if (is_array($files) && count($files) > 0) 896 { 897 foreach ($files as $file) 898 { 899 // Get the source and destination paths 900 $filesource = JPath::clean($file['src']); 901 $filedest = JPath::clean($file['dest']); 902 $filetype = array_key_exists('type', $file) ? $file['type'] : 'file'; 903 904 if (!file_exists($filesource)) { 905 /* 906 * The source file does not exist. Nothing to copy so set an error 907 * and return false. 908 */ 909 JError::raiseWarning(1, 'JInstaller::install: '.JText::sprintf('File does not exist', $filesource)); 910 return false; 911 } elseif (file_exists($filedest) && !$overwrite) { 912 913 /* 914 * It's okay if the manifest already exists 915 */ 916 if ($this->getPath( 'manifest' ) == $filesource) { 917 continue; 918 } 919 920 /* 921 * The destination file already exists and the overwrite flag is false. 922 * Set an error and return false. 923 */ 924 JError::raiseWarning(1, 'JInstaller::install: '.JText::sprintf('WARNSAME', $filedest)); 925 return false; 926 } else { 927 928 // Copy the folder or file to the new location. 929 if ( $filetype == 'folder') { 930 931 if (!(JFolder::copy($filesource, $filedest, null, $overwrite))) { 932 JError::raiseWarning(1, 'JInstaller::install: '.JText::sprintf('Failed to copy folder to', $filesource, $filedest)); 933 return false; 934 } 935 936 $step = array ('type' => 'folder', 'path' => $filedest); 937 } else { 938 939 if (!(JFile::copy($filesource, $filedest))) { 940 JError::raiseWarning(1, 'JInstaller::install: '.JText::sprintf('Failed to copy file to', $filesource, $filedest)); 941 return false; 942 } 943 944 $step = array ('type' => 'file', 'path' => $filedest); 945 } 946 947 /* 948 * Since we copied a file/folder, we want to add it to the installation step stack so that 949 * in case we have to roll back the installation we can remove the files copied. 950 */ 951 $this->_stepStack[] = $step; 952 } 953 } 954 } else { 955 956 /* 957 * The $files variable was either not an array or an empty array 958 */ 959 return false; 960 } 961 return count($files); 962 } 963 964 /** 965 * Method to parse through a files element of the installation manifest and remove 966 * the files that were installed 967 * 968 * @access public 969 * @param object $element The xml node to process 970 * @param int $cid Application ID of application to remove from 971 * @return boolean True on success 972 * @since 1.5 973 */ 974 function removeFiles($element, $cid=0) 975 { 976 // Initialize variables 977 $removefiles = array (); 978 $retval = true; 979 980 // Get the client info 981 jimport('joomla.application.helper'); 982 $client =& JApplicationHelper::getClientInfo($cid); 983 984 if (!is_a($element, 'JSimpleXMLElement') || !count($element->children())) { 985 // Either the tag does not exist or has no children therefore we return zero files processed. 986 return true; 987 } 988 989 // Get the array of file nodes to process 990 $files = $element->children(); 991 if (count($files) == 0) { 992 // No files to process 993 return true; 994 } 995 996 /* 997 * Here we set the folder we are going to remove the files from. There are a few 998 * special cases that need to be considered for certain reserved tags. 999 */ 1000 switch ($element->name()) 1001 { 1002 case 'media': 1003 if ($element->attributes('destination')) { 1004 $folder = $element->attributes('destination'); 1005 } else { 1006 $folder = ''; 1007 } 1008 $source = $client->path.DS.'media'.DS.$folder; 1009 break; 1010 1011 case 'languages': 1012 $source = $client->path.DS.'language'; 1013 break; 1014 1015 default: 1016 if ($client) { 1017 $pathname = 'extension_'.$client->name; 1018 $source = $this->getPath($pathname); 1019 } else { 1020 $pathname = 'extension_root'; 1021 $source = $this->getPath($pathname); 1022 } 1023 break; 1024 } 1025 1026 // Process each file in the $files array (children of $tagName). 1027 foreach ($files as $file) 1028 { 1029 /* 1030 * If the file is a language, we must handle it differently. Language files 1031 * go in a subdirectory based on the language code, ie. 1032 * 1033 * <language tag="en_US">en_US.mycomponent.ini</language> 1034 * 1035 * would go in the en_US subdirectory of the languages directory. 1036 */ 1037 if ($file->name() == 'language' && $file->attributes('tag') != '') { 1038 $path = $source.DS.$file->attributes('tag').DS.basename($file->data()); 1039 1040 // If the language folder is not present, then the core pack hasn't been installed... ignore 1041 if (!JFolder::exists(dirname($path))) { 1042 continue; 1043 } 1044 } else { 1045 $path = $source.DS.$file->data(); 1046 } 1047 1048 /* 1049 * Actually delete the files/folders 1050 */ 1051 if (is_dir($path)) { 1052 $val = JFolder::delete($path); 1053 } else { 1054 $val = JFile::delete($path); 1055 } 1056 1057 if ($val === false) { 1058 $retval = false; 1059 } 1060 } 1061 1062 return $retval; 1063 } 1064 1065 /** 1066 * Copies the installation manifest file to the extension folder in the given client 1067 * 1068 * @access public 1069 * @param int $cid Where to copy the installfile [optional: defaults to 1 (admin)] 1070 * @return boolean True on success, False on error 1071 * @since 1.5 1072 */ 1073 function copyManifest($cid=1) 1074 { 1075 // Get the client info 1076 jimport('joomla.application.helper'); 1077 $client =& JApplicationHelper::getClientInfo($cid); 1078 1079 $path['src'] = $this->getPath('manifest'); 1080 1081 if ($client) { 1082 $pathname = 'extension_'.$client->name; 1083 $path['dest'] = $this->getPath($pathname).DS.basename($this->getPath('manifest')); 1084 } else { 1085 $pathname = 'extension_root'; 1086 $path['dest'] = $this->getPath($pathname).DS.basename($this->getPath('manifest')); 1087 } 1088 return $this->copyFiles(array ($path), true); 1089 } 1090 1091 /** 1092 * Tries to find the package manifest file 1093 * 1094 * @access private 1095 * @return boolean True on success, False on error 1096 * @since 1.0 1097 */ 1098 function _findManifest() 1099 { 1100 // Get an array of all the xml files from teh installation directory 1101 $xmlfiles = JFolder::files($this->getPath('source'), '.xml$', 1, true); 1102 // If at least one xml file exists 1103 if (!empty($xmlfiles)) { 1104 foreach ($xmlfiles as $file) 1105 { 1106 // Is it a valid joomla installation manifest file? 1107 $manifest = $this->_isManifest($file); 1108 if (!is_null($manifest)) { 1109 1110 // If the root method attribute is set to upgrade, allow file overwrite 1111 $root =& $manifest->document; 1112 if ($root->attributes('method') == 'upgrade') { 1113 $this->_overwrite = true; 1114 } 1115 1116 // Set the manifest object and path 1117 $this->_manifest =& $manifest; 1118 $this->setPath('manifest', $file); 1119 1120 // Set the installation source path to that of the manifest file 1121 $this->setPath('source', dirname($file)); 1122 return true; 1123 } 1124 } 1125 1126 // None of the xml files found were valid install files 1127 JError::raiseWarning(1, 'JInstaller::install: '.JText::_('ERRORNOTFINDJOOMLAXMLSETUPFILE')); 1128 return false; 1129 } else { 1130 // No xml files were found in the install folder 1131 JError::raiseWarning(1, 'JInstaller::install: '.JText::_('ERRORXMLSETUP')); 1132 return false; 1133 } 1134 } 1135 1136 /** 1137 * Is the xml file a valid Joomla installation manifest file 1138 * 1139 * @access private 1140 * @param string $file An xmlfile path to check 1141 * @return mixed A JSimpleXML document, or null if the file failed to parse 1142 * @since 1.5 1143 */ 1144 function &_isManifest($file) 1145 { 1146 // Initialize variables 1147 $null = null; 1148 $xml =& JFactory::getXMLParser('Simple'); 1149 1150 // If we cannot load the xml file return null 1151 if (!$xml->loadFile($file)) { 1152 // Free up xml parser memory and return null 1153 unset ($xml); 1154 return $null; 1155 } 1156 1157 /* 1158 * Check for a valid XML root tag. 1159 * @todo: Remove backwards compatability in a future version 1160 * Should be 'install', but for backward compatability we will accept 'mosinstall'. 1161 */ 1162 $root =& $xml->document; 1163 if (!is_object($root) || ($root->name() != 'install' && $root->name() != 'mosinstall')) { 1164 // Free up xml parser memory and return null 1165 unset ($xml); 1166 return $null; 1167 } 1168 1169 // Valid manifest file return the object 1170 return $xml; 1171 } 1172 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Mar 28 15:54:07 2012 | Cross-referenced by PHPXref 0.7.1 |