| [ Index ] |
PHP Cross Reference of Joomla 2.5.4 DE |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * @package Joomla.Platform 4 * @subpackage Installer 5 * 6 * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved. 7 * @license GNU General Public License version 2 or later; see LICENSE 8 */ 9 10 defined('JPATH_PLATFORM') or die; 11 12 jimport('joomla.filesystem.file'); 13 jimport('joomla.filesystem.folder'); 14 jimport('joomla.filesystem.archive'); 15 jimport('joomla.filesystem.path'); 16 jimport('joomla.base.adapter'); 17 18 /** 19 * Joomla base installer class 20 * 21 * @package Joomla.Platform 22 * @subpackage Installer 23 * @since 11.1 24 */ 25 class JInstaller extends JAdapter 26 { 27 /** 28 * Array of paths needed by the installer 29 * 30 * @var array 31 * @since 11.1 32 */ 33 protected $_paths = array(); 34 35 /** 36 * True if package is an upgrade 37 * 38 * @var boolean 39 * @since 11.1 40 */ 41 protected $_upgrade = null; 42 43 /** 44 * The manifest trigger class 45 * 46 * @var object 47 * @since 11.1 48 */ 49 public $manifestClass = null; 50 51 /** 52 * True if existing files can be overwritten 53 * @var boolean 54 * @since 11.1 55 */ 56 protected $_overwrite = false; 57 58 /** 59 * Stack of installation steps 60 * - Used for installation rollback 61 * 62 * @var array 63 * @since 11.1 64 */ 65 protected $_stepStack = array(); 66 67 /** 68 * Extension Table Entry 69 * 70 * @var JTableExtension 71 * @since 11.1 72 */ 73 public $extension = null; 74 75 /** 76 * The output from the install/uninstall scripts 77 * 78 * @var string 79 * @since 11.1 80 * */ 81 public $message = null; 82 83 /** 84 * The installation manifest XML object 85 * 86 * @var object 87 * @since 11.1 88 */ 89 public $manifest = null; 90 91 /** 92 * The extension message that appears 93 * 94 * @var string 95 * @since 11.1 96 */ 97 protected $extension_message = null; 98 99 /** 100 * The redirect URL if this extension (can be null if no redirect) 101 * 102 * @var string 103 * @since 11.1 104 */ 105 protected $redirect_url = null; 106 107 /** 108 * @var JInstaller JInstaller instance container. 109 * @since 11.3 110 */ 111 protected static $instance; 112 113 /** 114 * Constructor 115 * 116 * @since 11.1 117 */ 118 public function __construct() 119 { 120 parent::__construct(dirname(__FILE__), 'JInstaller'); 121 } 122 123 /** 124 * Returns the global Installer object, only creating it 125 * if it doesn't already exist. 126 * 127 * @return object An installer object 128 * 129 * @since 11.1 130 */ 131 public static function getInstance() 132 { 133 if (!isset(self::$instance)) 134 { 135 self::$instance = new JInstaller; 136 } 137 return self::$instance; 138 } 139 140 /** 141 * Get the allow overwrite switch 142 * 143 * @return boolean Allow overwrite switch 144 * 145 * @since 11.1 146 * @deprecated 12.1 Use JInstaller::isOverwrite() 147 */ 148 public function getOverwrite() 149 { 150 JLog::add('JInstaller::getOverwrite() is deprecated. Please use JInstaller::isOverwrite() instead', JLog::WARNING, 'deprecated'); 151 return $this->isOverwrite(); 152 } 153 154 /** 155 * Get the allow overwrite switch 156 * 157 * @return boolean Allow overwrite switch 158 * 159 * @since 11.4 160 */ 161 public function isOverwrite() 162 { 163 return $this->_overwrite; 164 } 165 166 /** 167 * Set the allow overwrite switch 168 * 169 * @param boolean $state Overwrite switch state 170 * 171 * @return boolean True it state is set, false if it is not 172 * 173 * @since 11.1 174 */ 175 public function setOverwrite($state = false) 176 { 177 $tmp = $this->_overwrite; 178 179 if ($state) 180 { 181 $this->_overwrite = true; 182 } 183 else 184 { 185 $this->_overwrite = false; 186 } 187 188 return $tmp; 189 } 190 191 /** 192 * Get the redirect location 193 * 194 * @return string Redirect location (or null) 195 * 196 * @since 11.1 197 */ 198 public function getRedirectURL() 199 { 200 return $this->redirect_url; 201 } 202 203 /** 204 * Set the redirect location 205 * 206 * @param string $newurl New redirect location 207 * 208 * @return void 209 * 210 * @since 11.1 211 */ 212 public function setRedirectURL($newurl) 213 { 214 $this->redirect_url = $newurl; 215 } 216 217 /** 218 * Get the upgrade switch 219 * 220 * @return boolean 221 * 222 * @since 11.1 223 * @deprecated 12.1 Use JInstaller::isUpgrade() 224 */ 225 public function getUpgrade() 226 { 227 JLog::add('JInstaller::getUpgrade() is deprecated. Please use JInstaller::isUpgrade() instead', JLog::WARNING, 'deprecated'); 228 return $this->isUpgrade(); 229 } 230 231 /** 232 * Get the upgrade switch 233 * 234 * @return boolean 235 * 236 * @since 11.4 237 */ 238 public function isUpgrade() 239 { 240 return $this->_upgrade; 241 } 242 243 /** 244 * Set the upgrade switch 245 * 246 * @param boolean $state Upgrade switch state 247 * 248 * @return boolean True if upgrade, false otherwise 249 * 250 * @since 11.1 251 */ 252 public function setUpgrade($state = false) 253 { 254 $tmp = $this->_upgrade; 255 256 if ($state) 257 { 258 $this->_upgrade = true; 259 } 260 else 261 { 262 $this->_upgrade = false; 263 } 264 265 return $tmp; 266 } 267 268 /** 269 * Get the installation manifest object 270 * 271 * @return object Manifest object 272 * 273 * @since 11.1 274 */ 275 public function getManifest() 276 { 277 if (!is_object($this->manifest)) 278 { 279 $this->findManifest(); 280 } 281 282 return $this->manifest; 283 } 284 285 /** 286 * Get an installer path by name 287 * 288 * @param string $name Path name 289 * @param string $default Default value 290 * 291 * @return string Path 292 * 293 * @since 11.1 294 */ 295 public function getPath($name, $default = null) 296 { 297 return (!empty($this->_paths[$name])) ? $this->_paths[$name] : $default; 298 } 299 300 /** 301 * Sets an installer path by name 302 * 303 * @param string $name Path name 304 * @param string $value Path 305 * 306 * @return void 307 * 308 * @since 11.1 309 */ 310 public function setPath($name, $value) 311 { 312 $this->_paths[$name] = $value; 313 } 314 315 /** 316 * Pushes a step onto the installer stack for rolling back steps 317 * 318 * @param array $step Installer step 319 * 320 * @return void 321 * 322 * @since 11.1 323 */ 324 public function pushStep($step) 325 { 326 $this->_stepStack[] = $step; 327 } 328 329 /** 330 * Installation abort method 331 * 332 * @param string $msg Abort message from the installer 333 * @param string $type Package type if defined 334 * 335 * @return boolean True if successful 336 * 337 * @since 11.1 338 */ 339 public function abort($msg = null, $type = null) 340 { 341 // Initialise variables. 342 $retval = true; 343 $step = array_pop($this->_stepStack); 344 345 // Raise abort warning 346 if ($msg) 347 { 348 JError::raiseWarning(100, $msg); 349 } 350 351 while ($step != null) 352 { 353 switch ($step['type']) 354 { 355 case 'file': 356 // Remove the file 357 $stepval = JFile::delete($step['path']); 358 break; 359 360 case 'folder': 361 // Remove the folder 362 $stepval = JFolder::delete($step['path']); 363 break; 364 365 case 'query': 366 // Placeholder in case this is necessary in the future 367 // $stepval is always false because if this step was called it invariably failed 368 $stepval = false; 369 break; 370 371 case 'extension': 372 // Get database connector object 373 $db = $this->getDBO(); 374 $query = $db->getQuery(true); 375 376 // Remove the entry from the #__extensions table 377 $query->delete($db->quoteName('#__extensions')); 378 $query->where($db->quoteName('extension_id') . ' = ' . (int) $step['id']); 379 $db->setQuery($query); 380 $stepval = $db->query(); 381 382 break; 383 384 default: 385 if ($type && is_object($this->_adapters[$type])) 386 { 387 // Build the name of the custom rollback method for the type 388 $method = '_rollback_' . $step['type']; 389 // Custom rollback method handler 390 if (method_exists($this->_adapters[$type], $method)) 391 { 392 $stepval = $this->_adapters[$type]->$method($step); 393 } 394 } 395 else 396 { 397 $stepval = false; // set it to false 398 } 399 break; 400 } 401 402 // Only set the return value if it is false 403 if ($stepval === false) 404 { 405 $retval = false; 406 } 407 408 // Get the next step and continue 409 $step = array_pop($this->_stepStack); 410 } 411 412 $conf = JFactory::getConfig(); 413 $debug = $conf->get('debug'); 414 415 if ($debug) 416 { 417 JError::raiseError(500, JText::_('JLIB_INSTALLER_ABORT_DEBUG') . $msg); 418 } 419 420 return $retval; 421 } 422 423 // Adapter functions 424 425 /** 426 * Package installation method 427 * 428 * @param string $path Path to package source folder 429 * 430 * @return boolean True if successful 431 * 432 * @since 11.1 433 */ 434 public function install($path = null) 435 { 436 if ($path && JFolder::exists($path)) 437 { 438 $this->setPath('source', $path); 439 } 440 else 441 { 442 $this->abort(JText::_('JLIB_INSTALLER_ABORT_NOINSTALLPATH')); 443 return false; 444 } 445 446 if (!$this->setupInstall()) 447 { 448 $this->abort(JText::_('JLIB_INSTALLER_ABORT_DETECTMANIFEST')); 449 450 return false; 451 } 452 453 $type = (string) $this->manifest->attributes()->type; 454 455 if (is_object($this->_adapters[$type])) 456 { 457 // Add the languages from the package itself 458 if (method_exists($this->_adapters[$type], 'loadLanguage')) 459 { 460 $this->_adapters[$type]->loadLanguage($path); 461 } 462 463 // Fire the onExtensionBeforeInstall event. 464 JPluginHelper::importPlugin('extension'); 465 $dispatcher = JDispatcher::getInstance(); 466 $dispatcher->trigger( 467 'onExtensionBeforeInstall', 468 array('method' => 'install', 'type' => $type, 'manifest' => $this->manifest, 'extension' => 0) 469 ); 470 471 // Run the install 472 $result = $this->_adapters[$type]->install(); 473 474 // Fire the onExtensionAfterInstall 475 $dispatcher->trigger( 476 'onExtensionAfterInstall', 477 array('installer' => clone $this, 'eid' => $result) 478 ); 479 480 if ($result !== false) 481 { 482 return true; 483 } 484 else 485 { 486 return false; 487 } 488 } 489 490 return false; 491 } 492 493 /** 494 * Discovered package installation method 495 * 496 * @param integer $eid Extension ID 497 * 498 * @return boolean True if successful 499 * 500 * @since 11.1 501 */ 502 public function discover_install($eid = null) 503 { 504 if ($eid) 505 { 506 $this->extension = JTable::getInstance('extension'); 507 508 if (!$this->extension->load($eid)) 509 { 510 $this->abort(JText::_('JLIB_INSTALLER_ABORT_LOAD_DETAILS')); 511 512 return false; 513 } 514 515 if ($this->extension->state != -1) 516 { 517 $this->abort(JText::_('JLIB_INSTALLER_ABORT_ALREADYINSTALLED')); 518 519 return false; 520 } 521 522 // Lazy load the adapter 523 if (!isset($this->_adapters[$this->extension->type]) || !is_object($this->_adapters[$this->extension->type])) 524 { 525 if (!$this->setAdapter($this->extension->type)) 526 { 527 return false; 528 } 529 } 530 531 if (is_object($this->_adapters[$this->extension->type])) 532 { 533 if (method_exists($this->_adapters[$this->extension->type], 'discover_install')) 534 { 535 // Add the languages from the package itself 536 if (method_exists($this->_adapters[$this->extension->type], 'loadLanguage')) 537 { 538 $this->_adapters[$this->extension->type]->loadLanguage(); 539 } 540 541 // Fire the onExtensionBeforeInstall event. 542 JPluginHelper::importPlugin('extension'); 543 $dispatcher = JDispatcher::getInstance(); 544 $dispatcher->trigger( 545 'onExtensionBeforeInstall', 546 array( 547 'method' => 'discover_install', 548 'type' => $this->extension->get('type'), 549 'manifest' => null, 550 'extension' => $this->extension->get('extension_id') 551 ) 552 ); 553 554 // Run the install 555 $result = $this->_adapters[$this->extension->type]->discover_install(); 556 // Fire the onExtensionAfterInstall 557 $dispatcher->trigger( 558 'onExtensionAfterInstall', 559 array('installer' => clone $this, 'eid' => $result) 560 ); 561 if ($result !== false) 562 { 563 return true; 564 } 565 else 566 { 567 return false; 568 } 569 } 570 else 571 { 572 $this->abort(JText::_('JLIB_INSTALLER_ABORT_METHODNOTSUPPORTED')); 573 574 return false; 575 } 576 } 577 578 return false; 579 } 580 else 581 { 582 $this->abort(JText::_('JLIB_INSTALLER_ABORT_EXTENSIONNOTVALID')); 583 584 return false; 585 } 586 } 587 588 /** 589 * Extension discover method 590 * Asks each adapter to find extensions 591 * 592 * @return array JExtension 593 * 594 * @since 11.1 595 */ 596 public function discover() 597 { 598 $this->loadAllAdapters(); 599 $results = array(); 600 601 foreach ($this->_adapters as $adapter) 602 { 603 // Joomla! 1.5 installation adapter legacy support 604 if (method_exists($adapter, 'discover')) 605 { 606 $tmp = $adapter->discover(); 607 608 // if its an array and has entries 609 if (is_array($tmp) && count($tmp)) 610 { 611 // merge it into the system 612 $results = array_merge($results, $tmp); 613 } 614 } 615 } 616 617 return $results; 618 } 619 620 /** 621 * Package update method 622 * 623 * @param string $path Path to package source folder 624 * 625 * @return boolean True if successful 626 * 627 * @since 11.1 628 */ 629 public function update($path = null) 630 { 631 if ($path && JFolder::exists($path)) 632 { 633 $this->setPath('source', $path); 634 } 635 else 636 { 637 $this->abort(JText::_('JLIB_INSTALLER_ABORT_NOUPDATEPATH')); 638 } 639 640 if (!$this->setupInstall()) 641 { 642 return $this->abort(JText::_('JLIB_INSTALLER_ABORT_DETECTMANIFEST')); 643 } 644 645 $type = (string) $this->manifest->attributes()->type; 646 647 if (is_object($this->_adapters[$type])) 648 { 649 // Add the languages from the package itself 650 if (method_exists($this->_adapters[$type], 'loadLanguage')) 651 { 652 $this->_adapters[$type]->loadLanguage($path); 653 } 654 655 // Fire the onExtensionBeforeUpdate event. 656 JPluginHelper::importPlugin('extension'); 657 $dispatcher = JDispatcher::getInstance(); 658 $dispatcher->trigger('onExtensionBeforeUpdate', array('type' => $type, 'manifest' => $this->manifest)); 659 660 // Run the update 661 $result = $this->_adapters[$type]->update(); 662 663 // Fire the onExtensionAfterUpdate 664 $dispatcher->trigger( 665 'onExtensionAfterUpdate', 666 array('installer' => clone $this, 'eid' => $result) 667 ); 668 669 if ($result !== false) 670 { 671 return true; 672 } 673 else 674 { 675 return false; 676 } 677 } 678 679 return false; 680 } 681 682 /** 683 * Package uninstallation method 684 * 685 * @param string $type Package type 686 * @param mixed $identifier Package identifier for adapter 687 * @param integer $cid Application ID; deprecated in 1.6 688 * 689 * @return boolean True if successful 690 * 691 * @since 11.1 692 */ 693 public function uninstall($type, $identifier, $cid = 0) 694 { 695 if (!isset($this->_adapters[$type]) || !is_object($this->_adapters[$type])) 696 { 697 if (!$this->setAdapter($type)) 698 { 699 // We failed to get the right adapter 700 return false; 701 } 702 } 703 704 if (is_object($this->_adapters[$type])) 705 { 706 // We don't load languages here, we get the extension adapter to work it out 707 // Fire the onExtensionBeforeUninstall event. 708 JPluginHelper::importPlugin('extension'); 709 $dispatcher = JDispatcher::getInstance(); 710 $dispatcher->trigger('onExtensionBeforeUninstall', array('eid' => $identifier)); 711 // Run the uninstall 712 $result = $this->_adapters[$type]->uninstall($identifier); 713 // Fire the onExtensionAfterInstall 714 $dispatcher->trigger( 715 'onExtensionAfterUninstall', 716 array('installer' => clone $this, 'eid' => $identifier, 'result' => $result) 717 ); 718 719 return $result; 720 } 721 722 return false; 723 } 724 725 /** 726 * Refreshes the manifest cache stored in #__extensions 727 * 728 * @param integer $eid Extension ID 729 * 730 * @return mixed void on success, false on error @todo missing return value ? 731 * 732 * @since 11.1 733 */ 734 public function refreshManifestCache($eid) 735 { 736 if ($eid) 737 { 738 $this->extension = JTable::getInstance('extension'); 739 740 if (!$this->extension->load($eid)) 741 { 742 $this->abort(JText::_('JLIB_INSTALLER_ABORT_LOAD_DETAILS')); 743 return false; 744 } 745 746 if ($this->extension->state == -1) 747 { 748 $this->abort(JText::_('JLIB_INSTALLER_ABORT_REFRESH_MANIFEST_CACHE')); 749 return false; 750 } 751 752 // Lazy load the adapter 753 if (!isset($this->_adapters[$this->extension->type]) || !is_object($this->_adapters[$this->extension->type])) 754 { 755 if (!$this->setAdapter($this->extension->type)) 756 { 757 return false; 758 } 759 } 760 761 if (is_object($this->_adapters[$this->extension->type])) 762 { 763 if (method_exists($this->_adapters[$this->extension->type], 'refreshManifestCache')) 764 { 765 $result = $this->_adapters[$this->extension->type]->refreshManifestCache(); 766 767 if ($result !== false) 768 { 769 return true; 770 } 771 else 772 { 773 return false; 774 } 775 } 776 else 777 { 778 $this->abort(JText::sprintf('JLIB_INSTALLER_ABORT_METHODNOTSUPPORTED_TYPE', $this->extension->type)); 779 780 return false; 781 } 782 } 783 784 return false; 785 } 786 else 787 { 788 $this->abort(JText::_('JLIB_INSTALLER_ABORT_REFRESH_MANIFEST_CACHE_VALID')); 789 790 return false; 791 } 792 } 793 794 // Utility functions 795 796 /** 797 * Prepare for installation: this method sets the installation directory, finds 798 * and checks the installation file and verifies the installation type. 799 * 800 * @return boolean True on success 801 * 802 * @since 11.1 803 */ 804 public function setupInstall() 805 { 806 // We need to find the installation manifest file 807 if (!$this->findManifest()) 808 { 809 return false; 810 } 811 812 // Load the adapter(s) for the install manifest 813 $type = (string) $this->manifest->attributes()->type; 814 815 // Lazy load the adapter 816 if (!isset($this->_adapters[$type]) || !is_object($this->_adapters[$type])) 817 { 818 if (!$this->setAdapter($type)) 819 { 820 return false; 821 } 822 } 823 824 return true; 825 } 826 827 /** 828 * Backward compatible method to parse through a queries element of the 829 * installation manifest file and take appropriate action. 830 * 831 * @param JXMLElement $element The XML node to process 832 * 833 * @return mixed Number of queries processed or False on error 834 * 835 * @since 11.1 836 */ 837 public function parseQueries($element) 838 { 839 // Get the database connector object 840 $db = & $this->_db; 841 842 if (!$element || !count($element->children())) 843 { 844 // Either the tag does not exist or has no children therefore we return zero files processed. 845 return 0; 846 } 847 848 // Get the array of query nodes to process 849 $queries = $element->children(); 850 851 if (count($queries) == 0) 852 { 853 // No queries to process 854 return 0; 855 } 856 857 // Process each query in the $queries array (children of $tagName). 858 foreach ($queries as $query) 859 { 860 $db->setQuery($query->data()); 861 862 if (!$db->query()) 863 { 864 JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR', $db->stderr(true))); 865 866 return false; 867 } 868 } 869 870 return (int) count($queries); 871 } 872 873 /** 874 * Method to extract the name of a discreet installation sql file from the installation manifest file. 875 * 876 * @param object $element The XML node to process 877 * 878 * @return mixed Number of queries processed or False on error 879 * 880 * @since 11.1 881 */ 882 public function parseSQLFiles($element) 883 { 884 if (!$element || !count($element->children())) 885 { 886 // The tag does not exist. 887 return 0; 888 } 889 890 // Initialise variables. 891 $queries = array(); 892 $db = & $this->_db; 893 $dbDriver = strtolower($db->name); 894 895 if ($dbDriver == 'mysqli') 896 { 897 $dbDriver = 'mysql'; 898 } 899 elseif($dbDriver == 'sqlsrv') 900 { 901 $dbDriver = 'sqlazure'; 902 } 903 904 // Get the name of the sql file to process 905 $sqlfile = ''; 906 907 foreach ($element->children() as $file) 908 { 909 $fCharset = (strtolower($file->attributes()->charset) == 'utf8') ? 'utf8' : ''; 910 $fDriver = strtolower($file->attributes()->driver); 911 912 if ($fDriver == 'mysqli') 913 { 914 $fDriver = 'mysql'; 915 } 916 elseif($fDriver == 'sqlsrv') 917 { 918 $fDriver = 'sqlazure'; 919 } 920 921 if ($fCharset == 'utf8' && $fDriver == $dbDriver) 922 { 923 $sqlfile = $this->getPath('extension_root') . '/' . $file; 924 925 // Check that sql files exists before reading. Otherwise raise error for rollback 926 if (!file_exists($sqlfile)) 927 { 928 JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_SQL_FILENOTFOUND', $sqlfile)); 929 930 return false; 931 } 932 933 $buffer = file_get_contents($sqlfile); 934 935 // Graceful exit and rollback if read not successful 936 if ($buffer === false) 937 { 938 JError::raiseWarning(1, JText::_('JLIB_INSTALLER_ERROR_SQL_READBUFFER')); 939 940 return false; 941 } 942 943 // Create an array of queries from the sql file 944 $queries = JInstallerHelper::splitSql($buffer); 945 946 if (count($queries) == 0) 947 { 948 // No queries to process 949 return 0; 950 } 951 952 // Process each query in the $queries array (split out of sql file). 953 foreach ($queries as $query) 954 { 955 $query = trim($query); 956 957 if ($query != '' && $query{0} != '#') 958 { 959 $db->setQuery($query); 960 961 if (!$db->query()) 962 { 963 JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR', $db->stderr(true))); 964 965 return false; 966 } 967 } 968 } 969 } 970 } 971 972 return (int) count($queries); 973 } 974 975 /** 976 * Set the schema version for an extension by looking at its latest update 977 * 978 * @param JXMLElement $schema Schema Tag 979 * @param integer $eid Extension ID 980 * 981 * @return void 982 * 983 * @since 11.1 984 */ 985 public function setSchemaVersion($schema, $eid) 986 { 987 if ($eid && $schema) 988 { 989 $db = JFactory::getDBO(); 990 $schemapaths = $schema->children(); 991 992 if (!$schemapaths) 993 { 994 return; 995 } 996 997 if (count($schemapaths)) 998 { 999 $dbDriver = strtolower($db->name); 1000 if ($dbDriver == 'mysqli') 1001 { 1002 $dbDriver = 'mysql'; 1003 } 1004 elseif ($dbDriver == 'sqlsrv') 1005 { 1006 $dbDriver = 'sqlazure'; 1007 } 1008 1009 1010 $schemapath = ''; 1011 1012 foreach ($schemapaths as $entry) 1013 { 1014 $attrs = $entry->attributes(); 1015 if ($attrs['type'] == $dbDriver) 1016 { 1017 $schemapath = $entry; 1018 break; 1019 } 1020 } 1021 1022 if (strlen($schemapath)) 1023 { 1024 $files = str_replace('.sql', '', JFolder::files($this->getPath('extension_root') . '/' . $schemapath, '\.sql$')); 1025 usort($files, 'version_compare'); 1026 // Update the database 1027 $query = $db->getQuery(true); 1028 $query->delete() 1029 ->from('#__schemas') 1030 ->where('extension_id = ' . $eid); 1031 $db->setQuery($query); 1032 1033 if ($db->query()) 1034 { 1035 $query->clear(); 1036 $query->insert($db->quoteName('#__schemas')); 1037 $query->columns(array($db->quoteName('extension_id'), $db->quoteName('version_id'))); 1038 $query->values($eid . ', ' . $db->quote(end($files))); 1039 $db->setQuery($query); 1040 $db->query(); 1041 } 1042 } 1043 } 1044 } 1045 } 1046 1047 /** 1048 * Method to process the updates for an item 1049 * 1050 * @param JXMLElement $schema The XML node to process 1051 * @param integer $eid Extension Identifier 1052 * 1053 * @return boolean Result of the operations 1054 * 1055 * @since 11.1 1056 */ 1057 public function parseSchemaUpdates($schema, $eid) 1058 { 1059 $files = array(); 1060 $update_count = 0; 1061 1062 // Ensure we have an XML element and a valid extension id 1063 if ($eid && $schema) 1064 { 1065 $db = JFactory::getDBO(); 1066 $schemapaths = $schema->children(); 1067 1068 if (count($schemapaths)) 1069 { 1070 $dbDriver = strtolower($db->name); 1071 1072 if ($dbDriver == 'mysqli') 1073 { 1074 $dbDriver = 'mysql'; 1075 } 1076 elseif ($dbDriver == 'sqlsrv') 1077 { 1078 $dbDriver = 'sqlazure'; 1079 } 1080 1081 $schemapath = ''; 1082 foreach ($schemapaths as $entry) 1083 { 1084 $attrs = $entry->attributes(); 1085 if ($attrs['type'] == $dbDriver) 1086 { 1087 $schemapath = $entry; 1088 break; 1089 } 1090 } 1091 1092 if (strlen($schemapath)) 1093 { 1094 $files = str_replace('.sql', '', JFolder::files($this->getPath('extension_root') . '/' . $schemapath, '\.sql$')); 1095 usort($files, 'version_compare'); 1096 1097 if (!count($files)) 1098 { 1099 return false; 1100 } 1101 1102 $query = $db->getQuery(true); 1103 $query->select('version_id') 1104 ->from('#__schemas') 1105 ->where('extension_id = ' . $eid); 1106 $db->setQuery($query); 1107 $version = $db->loadResult(); 1108 1109 if ($version) 1110 { 1111 // We have a version! 1112 foreach ($files as $file) 1113 { 1114 if (version_compare($file, $version) > 0) 1115 { 1116 $buffer = file_get_contents($this->getPath('extension_root') . '/' . $schemapath . '/' . $file . '.sql'); 1117 1118 // Graceful exit and rollback if read not successful 1119 if ($buffer === false) 1120 { 1121 JError::raiseWarning(1, JText::_('JLIB_INSTALLER_ERROR_SQL_READBUFFER')); 1122 1123 return false; 1124 } 1125 1126 // Create an array of queries from the sql file 1127 $queries = JInstallerHelper::splitSql($buffer); 1128 1129 if (count($queries) == 0) 1130 { 1131 // No queries to process 1132 continue; 1133 } 1134 1135 // Process each query in the $queries array (split out of sql file). 1136 foreach ($queries as $query) 1137 { 1138 $query = trim($query); 1139 if ($query != '' && $query{0} != '#') 1140 { 1141 $db->setQuery($query); 1142 1143 if (!$db->query()) 1144 { 1145 JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR', $db->stderr(true))); 1146 1147 return false; 1148 } 1149 1150 $update_count++; 1151 } 1152 } 1153 } 1154 } 1155 } 1156 1157 // Update the database 1158 $query = $db->getQuery(true); 1159 $query->delete() 1160 ->from('#__schemas') 1161 ->where('extension_id = ' . $eid); 1162 $db->setQuery($query); 1163 1164 if ($db->Query()) 1165 { 1166 $query->clear(); 1167 $query->insert($db->quoteName('#__schemas')); 1168 $query->columns(array($db->quoteName('extension_id'), $db->quoteName('version_id'))); 1169 $query->values($eid . ', ' . $db->quote(end($files))); 1170 $db->setQuery($query); 1171 $db->Query(); 1172 } 1173 } 1174 } 1175 } 1176 1177 return $update_count; 1178 } 1179 1180 /** 1181 * Method to parse through a files element of the installation manifest and take appropriate 1182 * action. 1183 * 1184 * @param JXMLElement $element The XML node to process 1185 * @param integer $cid Application ID of application to install to 1186 * @param array $oldFiles List of old files (JXMLElement's) 1187 * @param array $oldMD5 List of old MD5 sums (indexed by filename with value as MD5) 1188 * 1189 * @return boolean True on success 1190 * 1191 * @since 11.1 1192 */ 1193 public function parseFiles($element, $cid = 0, $oldFiles = null, $oldMD5 = null) 1194 { 1195 // Get the array of file nodes to process; we checked whether this had children above. 1196 if (!$element || !count($element->children())) 1197 { 1198 // Either the tag does not exist or has no children (hence no files to process) therefore we return zero files processed. 1199 return 0; 1200 } 1201 1202 // Initialise variables. 1203 $copyfiles = array(); 1204 1205 // Get the client info 1206 $client = JApplicationHelper::getClientInfo($cid); 1207 1208 /* 1209 * Here we set the folder we are going to remove the files from. 1210 */ 1211 if ($client) 1212 { 1213 $pathname = 'extension_' . $client->name; 1214 $destination = $this->getPath($pathname); 1215 } 1216 else 1217 { 1218 $pathname = 'extension_root'; 1219 $destination = $this->getPath($pathname); 1220 } 1221 1222 // Here we set the folder we are going to copy the files from. 1223 1224 // Does the element have a folder attribute? 1225 // 1226 // If so this indicates that the files are in a subdirectory of the source 1227 // folder and we should append the folder attribute to the source path when 1228 // copying files. 1229 1230 $folder = (string) $element->attributes()->folder; 1231 1232 if ($folder && file_exists($this->getPath('source') . '/' . $folder)) 1233 { 1234 $source = $this->getPath('source') . '/' . $folder; 1235 } 1236 else 1237 { 1238 $source = $this->getPath('source'); 1239 } 1240 1241 // Work out what files have been deleted 1242 if ($oldFiles && ($oldFiles instanceof JXMLElement)) 1243 { 1244 $oldEntries = $oldFiles->children(); 1245 1246 if (count($oldEntries)) 1247 { 1248 $deletions = $this->findDeletedFiles($oldEntries, $element->children()); 1249 1250 foreach ($deletions['folders'] as $deleted_folder) 1251 { 1252 JFolder::delete($destination . '/' . $deleted_folder); 1253 } 1254 1255 foreach ($deletions['files'] as $deleted_file) 1256 { 1257 JFile::delete($destination . '/' . $deleted_file); 1258 } 1259 } 1260 } 1261 1262 // Copy the MD5SUMS file if it exists 1263 if (file_exists($source . '/MD5SUMS')) 1264 { 1265 $path['src'] = $source . '/MD5SUMS'; 1266 $path['dest'] = $destination . '/MD5SUMS'; 1267 $path['type'] = 'file'; 1268 $copyfiles[] = $path; 1269 } 1270 1271 // Process each file in the $files array (children of $tagName). 1272 foreach ($element->children() as $file) 1273 { 1274 $path['src'] = $source . '/' . $file; 1275 $path['dest'] = $destination . '/' . $file; 1276 1277 // Is this path a file or folder? 1278 $path['type'] = ($file->getName() == 'folder') ? 'folder' : 'file'; 1279 1280 // Before we can add a file to the copyfiles array we need to ensure 1281 // that the folder we are copying our file to exits and if it doesn't, 1282 // we need to create it. 1283 1284 if (basename($path['dest']) != $path['dest']) 1285 { 1286 $newdir = dirname($path['dest']); 1287 1288 if (!JFolder::create($newdir)) 1289 { 1290 JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_CREATE_DIRECTORY', $newdir)); 1291 return false; 1292 } 1293 } 1294 1295 // Add the file to the copyfiles array 1296 $copyfiles[] = $path; 1297 } 1298 1299 return $this->copyFiles($copyfiles); 1300 } 1301 1302 /** 1303 * Method to parse through a languages element of the installation manifest and take appropriate 1304 * action. 1305 * 1306 * @param JXMLElement $element The XML node to process 1307 * @param integer $cid Application ID of application to install to 1308 * 1309 * @return boolean True on success 1310 * 1311 * @since 11.1 1312 */ 1313 public function parseLanguages($element, $cid = 0) 1314 { 1315 // TODO: work out why the below line triggers 'node no longer exists' errors with files 1316 if (!$element || !count($element->children())) 1317 { 1318 // Either the tag does not exist or has no children therefore we return zero files processed. 1319 return 0; 1320 } 1321 1322 // Initialise variables. 1323 $copyfiles = array(); 1324 1325 // Get the client info 1326 $client = JApplicationHelper::getClientInfo($cid); 1327 1328 // Here we set the folder we are going to copy the files to. 1329 // 'languages' Files are copied to JPATH_BASE/language/ folder 1330 1331 $destination = $client->path . '/language'; 1332 1333 // Here we set the folder we are going to copy the files from. 1334 1335 // Does the element have a folder attribute? 1336 1337 // If so this indicates that the files are in a subdirectory of the source 1338 // folder and we should append the folder attribute to the source path when 1339 // copying files. 1340 1341 $folder = (string) $element->attributes()->folder; 1342 1343 if ($folder && file_exists($this->getPath('source') . '/' . $folder)) 1344 { 1345 $source = $this->getPath('source') . '/' . $folder; 1346 } 1347 else 1348 { 1349 $source = $this->getPath('source'); 1350 } 1351 1352 // Process each file in the $files array (children of $tagName). 1353 foreach ($element->children() as $file) 1354 { 1355 // Language files go in a subfolder based on the language code, ie. 1356 // <language tag="en-US">en-US.mycomponent.ini</language> 1357 // would go in the en-US subdirectory of the language folder. 1358 1359 // We will only install language files where a core language pack 1360 // already exists. 1361 1362 if ((string) $file->attributes()->tag != '') 1363 { 1364 $path['src'] = $source . '/' . $file; 1365 1366 if ((string) $file->attributes()->client != '') 1367 { 1368 // Override the client 1369 $langclient = JApplicationHelper::getClientInfo((string) $file->attributes()->client, true); 1370 $path['dest'] = $langclient->path . '/language/' . $file->attributes()->tag . '/' . basename((string) $file); 1371 } 1372 else 1373 { 1374 // Use the default client 1375 $path['dest'] = $destination . '/' . $file->attributes()->tag . '/' . basename((string) $file); 1376 } 1377 1378 // If the language folder is not present, then the core pack hasn't been installed... ignore 1379 if (!JFolder::exists(dirname($path['dest']))) 1380 { 1381 continue; 1382 } 1383 } 1384 else 1385 { 1386 $path['src'] = $source . '/' . $file; 1387 $path['dest'] = $destination . '/' . $file; 1388 } 1389 1390 // 1391 // Before we can add a file to the copyfiles array we need to ensure 1392 // that the folder we are copying our file to exits and if it doesn't, 1393 // we need to create it. 1394 1395 if (basename($path['dest']) != $path['dest']) 1396 { 1397 $newdir = dirname($path['dest']); 1398 1399 if (!JFolder::create($newdir)) 1400 { 1401 JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_CREATE_DIRECTORY', $newdir)); 1402 1403 return false; 1404 } 1405 } 1406 1407 // Add the file to the copyfiles array 1408 $copyfiles[] = $path; 1409 } 1410 1411 return $this->copyFiles($copyfiles); 1412 } 1413 1414 /** 1415 * Method to parse through a media element of the installation manifest and take appropriate 1416 * action. 1417 * 1418 * @param JXMLElement $element The XML node to process 1419 * @param integer $cid Application ID of application to install to 1420 * 1421 * @return boolean True on success 1422 * 1423 * @since 11.1 1424 */ 1425 public function parseMedia($element, $cid = 0) 1426 { 1427 if (!$element || !count($element->children())) 1428 { 1429 // Either the tag does not exist or has no children therefore we return zero files processed. 1430 return 0; 1431 } 1432 1433 // Initialise variables. 1434 $copyfiles = array(); 1435 1436 // Get the client info 1437 $client = JApplicationHelper::getClientInfo($cid); 1438 1439 // Here we set the folder we are going to copy the files to. 1440 // Default 'media' Files are copied to the JPATH_BASE/media folder 1441 1442 $folder = ((string) $element->attributes()->destination) ? '/' . $element->attributes()->destination : null; 1443 $destination = JPath::clean(JPATH_ROOT . '/media' . $folder); 1444 1445 // Here we set the folder we are going to copy the files from. 1446 1447 // Does the element have a folder attribute? 1448 // If so this indicates that the files are in a subdirectory of the source 1449 // folder and we should append the folder attribute to the source path when 1450 // copying files. 1451 1452 $folder = (string) $element->attributes()->folder; 1453 1454 if ($folder && file_exists($this->getPath('source') . '/' . $folder)) 1455 { 1456 $source = $this->getPath('source') . '/' . $folder; 1457 } 1458 else 1459 { 1460 $source = $this->getPath('source'); 1461 } 1462 1463 // Process each file in the $files array (children of $tagName). 1464 foreach ($element->children() as $file) 1465 { 1466 $path['src'] = $source . '/' . $file; 1467 $path['dest'] = $destination . '/' . $file; 1468 1469 // Is this path a file or folder? 1470 $path['type'] = ($file->getName() == 'folder') ? 'folder' : 'file'; 1471 1472 // Before we can add a file to the copyfiles array we need to ensure 1473 // that the folder we are copying our file to exits and if it doesn't, 1474 // we need to create it. 1475 1476 if (basename($path['dest']) != $path['dest']) 1477 { 1478 $newdir = dirname($path['dest']); 1479 1480 if (!JFolder::create($newdir)) 1481 { 1482 JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_CREATE_DIRECTORY', $newdir)); 1483 1484 return false; 1485 } 1486 } 1487 1488 // Add the file to the copyfiles array 1489 $copyfiles[] = $path; 1490 } 1491 1492 return $this->copyFiles($copyfiles); 1493 } 1494 1495 /** 1496 * Method to parse the parameters of an extension, build the INI 1497 * string for its default parameters, and return the INI string. 1498 * 1499 * @return string INI string of parameter values 1500 * 1501 * @since 11.1 1502 */ 1503 public function getParams() 1504 { 1505 // Validate that we have a fieldset to use 1506 if (!isset($this->manifest->config->fields->fieldset)) 1507 { 1508 return '{}'; 1509 } 1510 // Getting the fieldset tags 1511 $fieldsets = $this->manifest->config->fields->fieldset; 1512 1513 // Creating the data collection variable: 1514 $ini = array(); 1515 1516 // Iterating through the fieldsets: 1517 foreach ($fieldsets as $fieldset) 1518 { 1519 if (!count($fieldset->children())) 1520 { 1521 // Either the tag does not exist or has no children therefore we return zero files processed. 1522 return null; 1523 } 1524 1525 // Iterating through the fields and collecting the name/default values: 1526 foreach ($fieldset as $field) 1527 { 1528 // Check against the null value since otherwise default values like "0" 1529 // cause entire parameters to be skipped. 1530 1531 if (($name = $field->attributes()->name) === null) 1532 { 1533 continue; 1534 } 1535 1536 if (($value = $field->attributes()->default) === null) 1537 { 1538 continue; 1539 } 1540 1541 $ini[(string) $name] = (string) $value; 1542 } 1543 } 1544 1545 return json_encode($ini); 1546 } 1547 1548 /** 1549 * Copyfiles 1550 * 1551 * Copy files from source directory to the target directory 1552 * 1553 * @param array $files Array with filenames 1554 * @param boolean $overwrite True if existing files can be replaced 1555 * 1556 * @return boolean True on success 1557 * 1558 * @since 11.1 1559 */ 1560 public function copyFiles($files, $overwrite = null) 1561 { 1562 // To allow for manual override on the overwriting flag, we check to see if 1563 // the $overwrite flag was set and is a boolean value. If not, use the object 1564 // allowOverwrite flag. 1565 1566 if (is_null($overwrite) || !is_bool($overwrite)) 1567 { 1568 $overwrite = $this->_overwrite; 1569 } 1570 1571 /* 1572 * $files must be an array of filenames. Verify that it is an array with 1573 * at least one file to copy. 1574 */ 1575 if (is_array($files) && count($files) > 0) 1576 { 1577 1578 foreach ($files as $file) 1579 { 1580 // Get the source and destination paths 1581 $filesource = JPath::clean($file['src']); 1582 $filedest = JPath::clean($file['dest']); 1583 $filetype = array_key_exists('type', $file) ? $file['type'] : 'file'; 1584 1585 if (!file_exists($filesource)) 1586 { 1587 /* 1588 * The source file does not exist. Nothing to copy so set an error 1589 * and return false. 1590 */ 1591 JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_NO_FILE', $filesource)); 1592 1593 return false; 1594 } 1595 elseif (($exists = file_exists($filedest)) && !$overwrite) 1596 { 1597 1598 // It's okay if the manifest already exists 1599 if ($this->getPath('manifest') == $filesource) 1600 { 1601 continue; 1602 } 1603 1604 // The destination file already exists and the overwrite flag is false. 1605 // Set an error and return false. 1606 1607 JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_FILE_EXISTS', $filedest)); 1608 1609 return false; 1610 } 1611 else 1612 { 1613 // Copy the folder or file to the new location. 1614 if ($filetype == 'folder') 1615 { 1616 if (!(JFolder::copy($filesource, $filedest, null, $overwrite))) 1617 { 1618 JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_FAIL_COPY_FOLDER', $filesource, $filedest)); 1619 return false; 1620 } 1621 1622 $step = array('type' => 'folder', 'path' => $filedest); 1623 } 1624 else 1625 { 1626 if (!(JFile::copy($filesource, $filedest, null))) 1627 { 1628 JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_FAIL_COPY_FILE', $filesource, $filedest)); 1629 1630 return false; 1631 } 1632 1633 $step = array('type' => 'file', 'path' => $filedest); 1634 } 1635 1636 /* 1637 * Since we copied a file/folder, we want to add it to the installation step stack so that 1638 * in case we have to roll back the installation we can remove the files copied. 1639 */ 1640 if (!$exists) 1641 { 1642 $this->_stepStack[] = $step; 1643 } 1644 } 1645 } 1646 } 1647 else 1648 { 1649 // The $files variable was either not an array or an empty array 1650 return false; 1651 } 1652 1653 return count($files); 1654 } 1655 1656 /** 1657 * Method to parse through a files element of the installation manifest and remove 1658 * the files that were installed 1659 * 1660 * @param object $element The XML node to process 1661 * @param integer $cid Application ID of application to remove from 1662 * 1663 * @return boolean True on success 1664 * 1665 * @since 11.1 1666 */ 1667 public function removeFiles($element, $cid = 0) 1668 { 1669 if (!$element || !count($element->children())) 1670 { 1671 // Either the tag does not exist or has no children therefore we return zero files processed. 1672 return true; 1673 } 1674 1675 // Initialise variables. 1676 $removefiles = array(); 1677 $retval = true; 1678 1679 $debug = false; 1680 if (isset($GLOBALS['installerdebug']) && $GLOBALS['installerdebug']) 1681 { 1682 $debug = true; 1683 } 1684 1685 // Get the client info if we're using a specific client 1686 if ($cid > -1) 1687 { 1688 $client = JApplicationHelper::getClientInfo($cid); 1689 } 1690 else 1691 { 1692 $client = null; 1693 } 1694 1695 // Get the array of file nodes to process 1696 $files = $element->children(); 1697 1698 if (count($files) == 0) 1699 { 1700 // No files to process 1701 return true; 1702 } 1703 1704 $folder = ''; 1705 1706 /* 1707 * Here we set the folder we are going to remove the files from. There are a few 1708 * special cases that need to be considered for certain reserved tags. 1709 */ 1710 switch ($element->getName()) 1711 { 1712 case 'media': 1713 if ((string) $element->attributes()->destination) 1714 { 1715 $folder = (string) $element->attributes()->destination; 1716 } 1717 else 1718 { 1719 $folder = ''; 1720 } 1721 1722 $source = $client->path . '/media/' . $folder; 1723 1724 break; 1725 1726 case 'languages': 1727 $lang_client = (string) $element->attributes()->client; 1728 1729 if ($lang_client) 1730 { 1731 $client = JApplicationHelper::getClientInfo($lang_client, true); 1732 $source = $client->path . '/language'; 1733 } 1734 else 1735 { 1736 if ($client) 1737 { 1738 $source = $client->path . '/language'; 1739 } 1740 else 1741 { 1742 $source = ''; 1743 } 1744 } 1745 1746 break; 1747 1748 default: 1749 if ($client) 1750 { 1751 $pathname = 'extension_' . $client->name; 1752 $source = $this->getPath($pathname); 1753 } 1754 else 1755 { 1756 $pathname = 'extension_root'; 1757 $source = $this->getPath($pathname); 1758 } 1759 1760 break; 1761 } 1762 1763 // Process each file in the $files array (children of $tagName). 1764 foreach ($files as $file) 1765 { 1766 // If the file is a language, we must handle it differently. Language files 1767 // go in a subdirectory based on the language code, ie. 1768 // <language tag="en_US">en_US.mycomponent.ini</language> 1769 // would go in the en_US subdirectory of the languages directory. 1770 1771 if ($file->getName() == 'language' && (string) $file->attributes()->tag != '') 1772 { 1773 if ($source) 1774 { 1775 $path = $source . '/' . $file->attributes()->tag . '/' . basename((string) $file); 1776 } 1777 else 1778 { 1779 $target_client = JApplicationHelper::getClientInfo((string) $file->attributes()->client, true); 1780 $path = $target_client->path . '/language/' . $file->attributes()->tag . '/' . basename((string) $file); 1781 } 1782 1783 // If the language folder is not present, then the core pack hasn't been installed... ignore 1784 if (!JFolder::exists(dirname($path))) 1785 { 1786 continue; 1787 } 1788 } 1789 else 1790 { 1791 $path = $source . '/' . $file; 1792 } 1793 1794 // Actually delete the files/folders 1795 1796 if (is_dir($path)) 1797 { 1798 $val = JFolder::delete($path); 1799 } 1800 else 1801 { 1802 $val = JFile::delete($path); 1803 } 1804 1805 if ($val === false) 1806 { 1807 JError::raiseWarning(43, 'Failed to delete ' . $path); 1808 $retval = false; 1809 } 1810 } 1811 1812 if (!empty($folder)) 1813 { 1814 $val = JFolder::delete($source); 1815 } 1816 1817 return $retval; 1818 } 1819 1820 /** 1821 * Copies the installation manifest file to the extension folder in the given client 1822 * 1823 * @param integer $cid Where to copy the installfile [optional: defaults to 1 (admin)] 1824 * 1825 * @return boolean True on success, False on error 1826 * 1827 * @since 11.1 1828 */ 1829 public function copyManifest($cid = 1) 1830 { 1831 // Get the client info 1832 $client = JApplicationHelper::getClientInfo($cid); 1833 1834 $path['src'] = $this->getPath('manifest'); 1835 1836 if ($client) 1837 { 1838 $pathname = 'extension_' . $client->name; 1839 $path['dest'] = $this->getPath($pathname) . '/' . basename($this->getPath('manifest')); 1840 } 1841 else 1842 { 1843 $pathname = 'extension_root'; 1844 $path['dest'] = $this->getPath($pathname) . '/' . basename($this->getPath('manifest')); 1845 } 1846 1847 return $this->copyFiles(array($path), true); 1848 } 1849 1850 /** 1851 * Tries to find the package manifest file 1852 * 1853 * @return boolean True on success, False on error 1854 * 1855 * @since 1.0 1856 */ 1857 public function findManifest() 1858 { 1859 // Get an array of all the XML files from the installation directory 1860 $xmlfiles = JFolder::files($this->getPath('source'), '.xml$', 1, true); 1861 // If at least one XML file exists 1862 if (!empty($xmlfiles)) 1863 { 1864 1865 foreach ($xmlfiles as $file) 1866 { 1867 // Is it a valid Joomla installation manifest file? 1868 $manifest = $this->isManifest($file); 1869 1870 if (!is_null($manifest)) 1871 { 1872 // If the root method attribute is set to upgrade, allow file overwrite 1873 if ((string) $manifest->attributes()->method == 'upgrade') 1874 { 1875 $this->_upgrade = true; 1876 $this->_overwrite = true; 1877 } 1878 1879 // If the overwrite option is set, allow file overwriting 1880 if ((string) $manifest->attributes()->overwrite == 'true') 1881 { 1882 $this->_overwrite = true; 1883 } 1884 1885 // Set the manifest object and path 1886 $this->manifest = $manifest; 1887 $this->setPath('manifest', $file); 1888 1889 // Set the installation source path to that of the manifest file 1890 $this->setPath('source', dirname($file)); 1891 1892 return true; 1893 } 1894 } 1895 1896 // None of the XML files found were valid install files 1897 JError::raiseWarning(1, JText::_('JLIB_INSTALLER_ERROR_NOTFINDJOOMLAXMLSETUPFILE')); 1898 1899 return false; 1900 } 1901 else 1902 { 1903 // No XML files were found in the install folder 1904 JError::raiseWarning(1, JText::_('JLIB_INSTALLER_ERROR_NOTFINDXMLSETUPFILE')); 1905 return false; 1906 } 1907 } 1908 1909 /** 1910 * Is the XML file a valid Joomla installation manifest file. 1911 * 1912 * @param string $file An xmlfile path to check 1913 * 1914 * @return mixed A JXMLElement, or null if the file failed to parse 1915 * 1916 * @since 11.1 1917 */ 1918 public function isManifest($file) 1919 { 1920 // Initialise variables. 1921 $xml = JFactory::getXML($file); 1922 1923 // If we cannot load the XML file return null 1924 if (!$xml) 1925 { 1926 return null; 1927 } 1928 1929 // Check for a valid XML root tag. 1930 // @todo: Remove backwards compatibility in a future version 1931 // Should be 'extension', but for backward compatibility we will accept 'extension' or 'install'. 1932 1933 // 1.5 uses 'install' 1934 // 1.6 uses 'extension' 1935 if ($xml->getName() != 'install' && $xml->getName() != 'extension') 1936 { 1937 return null; 1938 } 1939 1940 // Valid manifest file return the object 1941 return $xml; 1942 } 1943 1944 /** 1945 * Generates a manifest cache 1946 * 1947 * @return string serialised manifest data 1948 * 1949 * @since 11.1 1950 */ 1951 public function generateManifestCache() 1952 { 1953 return json_encode(JApplicationHelper::parseXMLInstallFile($this->getPath('manifest'))); 1954 } 1955 1956 /** 1957 * Cleans up discovered extensions if they're being installed some other way 1958 * 1959 * @param string $type The type of extension (component, etc) 1960 * @param string $element Unique element identifier (e.g. com_content) 1961 * @param string $folder The folder of the extension (plugins; e.g. system) 1962 * @param integer $client The client application (administrator or site) 1963 * 1964 * @return object Result of query 1965 * 1966 * @since 11.1 1967 */ 1968 public function cleanDiscoveredExtension($type, $element, $folder = '', $client = 0) 1969 { 1970 $dbo = JFactory::getDBO(); 1971 $query = $dbo->getQuery(true); 1972 $query->delete($dbo->quoteName('#__extensions')); 1973 $query->where('type = ' . $dbo->Quote($type)); 1974 $query->where('element = ' . $dbo->Quote($element)); 1975 $query->where('folder = ' . $dbo->Quote($folder)); 1976 $query->where('client_id = ' . intval($client)); 1977 $query->where('state = -1'); 1978 1979 return $dbo->Query(); 1980 } 1981 1982 /** 1983 * Compares two "files" entries to find deleted files/folders 1984 * 1985 * @param array $old_files An array of JXMLElement objects that are the old files 1986 * @param array $new_files An array of JXMLElement objects that are the new files 1987 * 1988 * @return array An array with the delete files and folders in findDeletedFiles[files] and findDeletedFiles[folders] respectively 1989 * 1990 * @since 11.1 1991 */ 1992 public function findDeletedFiles($old_files, $new_files) 1993 { 1994 // The magic find deleted files function! 1995 // The files that are new 1996 $files = array(); 1997 // The folders that are new 1998 $folders = array(); 1999 // The folders of the files that are new 2000 $containers = array(); 2001 // A list of files to delete 2002 $files_deleted = array(); 2003 // A list of folders to delete 2004 $folders_deleted = array(); 2005 2006 foreach ($new_files as $file) 2007 { 2008 switch ($file->getName()) 2009 { 2010 case 'folder': 2011 // Add any folders to the list 2012 $folders[] = (string) $file; // add any folders to the list 2013 break; 2014 2015 case 'file': 2016 default: 2017 // Add any files to the list 2018 $files[] = (string) $file; 2019 // Now handle the folder part of the file to ensure we get any containers 2020 // Break up the parts of the directory 2021 $container_parts = explode('/', dirname((string) $file)); 2022 // Make sure this is clean and empty 2023 $container = ''; 2024 2025 foreach ($container_parts as $part) 2026 { 2027 // Iterate through each part 2028 // Add a slash if its not empty 2029 if (!empty($container)) 2030 { 2031 $container .= '/'; 2032 } 2033 $container .= $part; // append the folder part 2034 if (!in_array($container, $containers)) 2035 { 2036 $containers[] = $container; // add the container if it doesn't already exist 2037 } 2038 } 2039 break; 2040 } 2041 } 2042 2043 foreach ($old_files as $file) 2044 { 2045 switch ($file->getName()) 2046 { 2047 case 'folder': 2048 if (!in_array((string) $file, $folders)) 2049 { 2050 // See whether the folder exists in the new list 2051 if (!in_array((string) $file, $containers)) 2052 { 2053 // Check if the folder exists as a container in the new list 2054 // If it's not in the new list or a container then delete it 2055 $folders_deleted[] = (string) $file; 2056 } 2057 } 2058 break; 2059 2060 case 'file': 2061 default: 2062 if (!in_array((string) $file, $files)) 2063 { 2064 // look if the file exists in the new list 2065 if (!in_array(dirname((string) $file), $folders)) 2066 { 2067 // look if the file is now potentially in a folder 2068 $files_deleted[] = (string) $file; // not in a folder, doesn't exist, wipe it out! 2069 } 2070 } 2071 break; 2072 } 2073 } 2074 2075 return array('files' => $files_deleted, 'folders' => $folders_deleted); 2076 } 2077 2078 /** 2079 * Loads an MD5SUMS file into an associative array 2080 * 2081 * @param string $filename Filename to load 2082 * 2083 * @return array Associative array with filenames as the index and the MD5 as the value 2084 * 2085 * @since 11.1 2086 */ 2087 public function loadMD5Sum($filename) 2088 { 2089 if (!file_exists($filename)) 2090 { 2091 // Bail if the file doesn't exist 2092 return false; 2093 } 2094 2095 $data = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 2096 $retval = array(); 2097 2098 foreach ($data as $row) 2099 { 2100 $results = explode(' ', $row); // split up the data 2101 $results[1] = str_replace('./', '', $results[1]); // cull any potential prefix 2102 $retval[$results[1]] = $results[0]; // throw into the array 2103 } 2104 2105 return $retval; 2106 } 2107 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Tue Apr 3 11:40:28 2012 | Cross-referenced by PHPXref 0.7.1 |