PageRenderTime 59ms CodeModel.GetById 21ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/libraries/joomla/installer/adapters/component.php

https://bitbucket.org/eternaware/joomus
PHP | 1810 lines | 1023 code | 288 blank | 499 comment | 163 complexity | 7ae3306d694466c1cc74b6404906fde7 MD5 | raw file
Possible License(s): LGPL-2.1

Large files files are truncated, but you can click here to view the full file

   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
  10defined('JPATH_PLATFORM') or die;
  11
  12jimport('joomla.base.adapterinstance');
  13jimport('joomla.filesystem.folder');
  14
  15/**
  16 * Component installer
  17 *
  18 * @package     Joomla.Platform
  19 * @subpackage  Installer
  20 * @since       11.1
  21 */
  22class JInstallerComponent extends JAdapterInstance
  23{
  24	/**
  25	 * Copy of the XML manifest file
  26	 *
  27	 * @var    string
  28	 * @since  11.1
  29	 */
  30	protected $manifest = null;
  31
  32	/**
  33	 * Name of the extension
  34	 *
  35	 * @var    string
  36	 * @since  11.1
  37	 * */
  38	protected $name = null;
  39
  40	/**
  41	 * The unique identifier for the extension (e.g. mod_login)
  42	 *
  43	 * @var    string
  44	 * @since  11.1
  45	 * */
  46	protected $element = null;
  47
  48	/**
  49	 * The list of current files fo the Joomla! CMS administrator that are installed and is read
  50	 * from the manifest on disk in the update area to handle doing a diff
  51	 * and deleting files that are in the old files list and not in the new
  52	 * files list.
  53	 *
  54	 * @var    array
  55	 * @since  11.1
  56	 * */
  57	protected $oldAdminFiles = null;
  58
  59	/**
  60	 * The list of current files that are installed and is read
  61	 * from the manifest on disk in the update area to handle doing a diff
  62	 * and deleting files that are in the old files list and not in the new
  63	 * files list.
  64	 *
  65	 * @var    array
  66	 * @since  11.1
  67	 * */
  68	protected $oldFiles = null;
  69
  70	/**
  71	 * A path to the PHP file that the scriptfile declaration in
  72	 * the manifest refers to.
  73	 *
  74	 * @var    string
  75	 * @since  11.1
  76	 * */
  77	protected $manifest_script = null;
  78
  79	/**
  80	 * For legacy installations this is a path to the PHP file that the scriptfile declaration in the
  81	 * manifest refers to.
  82	 *
  83	 * @var    string
  84	 * @since  11.1
  85	 * */
  86	protected $install_script = null;
  87
  88	/**
  89	 * Custom loadLanguage method
  90	 *
  91	 * @param   string  $path  The path language files are on.
  92	 *
  93	 * @return  void
  94	 *
  95	 * @since   11.1
  96	 */
  97	public function loadLanguage($path = null)
  98	{
  99		$source = $this->parent->getPath('source');
 100
 101		if (!$source)
 102		{
 103			$this->parent
 104				->setPath(
 105				'source',
 106				($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) .
 107				'/components/' . $this->parent->extension->element
 108			);
 109		}
 110
 111		$this->manifest = $this->parent->getManifest();
 112		$name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'));
 113
 114		if (substr($name, 0, 4) == 'com_')
 115		{
 116			$extension = $name;
 117		}
 118		else
 119		{
 120			$extension = 'com_' . $name;
 121		}
 122
 123		$lang = JFactory::getLanguage();
 124		$source = $path ? $path : ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/components/' . $extension;
 125
 126		if ($this->manifest->administration->files)
 127		{
 128			$element = $this->manifest->administration->files;
 129		}
 130		elseif ($this->manifest->files)
 131		{
 132			$element = $this->manifest->files;
 133		}
 134		else
 135		{
 136			$element = null;
 137		}
 138
 139		if ($element)
 140		{
 141			$folder = (string) $element->attributes()->folder;
 142
 143			if ($folder && file_exists($path . '/' . $folder))
 144			{
 145				$source = $path . '/' . $folder;
 146			}
 147		}
 148		$lang->load($extension . '.sys', $source, null, false, false) || $lang->load($extension . '.sys', JPATH_ADMINISTRATOR, null, false, false)
 149			|| $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false)
 150			|| $lang->load($extension . '.sys', JPATH_ADMINISTRATOR, $lang->getDefault(), false, false);
 151	}
 152
 153	/**
 154	 * Custom install method for components
 155	 *
 156	 * @return  boolean  True on success
 157	 *
 158	 * @since   11.1
 159	 */
 160	public function install()
 161	{
 162		// Get a database connector object
 163		$db = $this->parent->getDbo();
 164
 165		// Get the extension manifest object
 166		$this->manifest = $this->parent->getManifest();
 167
 168		/*
 169		 * ---------------------------------------------------------------------------------------------
 170		 * Manifest Document Setup Section
 171		 * ---------------------------------------------------------------------------------------------
 172		 */
 173
 174		// Set the extension's name
 175		$name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'));
 176		if (substr($name, 0, 4) == 'com_')
 177		{
 178			$element = $name;
 179		}
 180		else
 181		{
 182			$element = 'com_' . $name;
 183		}
 184
 185		$this->set('name', $name);
 186		$this->set('element', $element);
 187
 188		// Get the component description
 189		$this->parent->set('message', JText::_((string) $this->manifest->description));
 190
 191		// Set the installation target paths
 192		$this->parent->setPath('extension_site', JPath::clean(JPATH_SITE . '/components/' . $this->get('element')));
 193		$this->parent->setPath('extension_administrator', JPath::clean(JPATH_ADMINISTRATOR . '/components/' . $this->get('element')));
 194
 195		// Copy the admin path as it's used as a common base
 196		$this->parent->setPath('extension_root', $this->parent->getPath('extension_administrator'));
 197
 198		/*
 199		 * ---------------------------------------------------------------------------------------------
 200		 * Basic Checks Section
 201		 * ---------------------------------------------------------------------------------------------
 202		 */
 203
 204		// Make sure that we have an admin element
 205		if (!$this->manifest->administration)
 206		{
 207			JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_INSTALL_ADMIN_ELEMENT'), JLog::WARNING, 'jerror');
 208			return false;
 209		}
 210
 211		/*
 212		 * ---------------------------------------------------------------------------------------------
 213		 * Filesystem Processing Section
 214		 * ---------------------------------------------------------------------------------------------
 215		 */
 216
 217		/*
 218		 * If the component site or admin directory already exists, then we will assume that the component is already
 219		 * installed or another component is using that directory.
 220		 */
 221
 222		if (file_exists($this->parent->getPath('extension_site')) || file_exists($this->parent->getPath('extension_administrator')))
 223		{
 224			// Look for an update function or update tag
 225			$updateElement = $this->manifest->update;
 226
 227			// Upgrade manually set or update function available or update tag detected
 228			if ($this->parent->isUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update'))
 229				|| $updateElement)
 230			{
 231				// Transfer control to the update function
 232				return $this->update();
 233			}
 234			elseif (!$this->parent->isOverwrite())
 235			{
 236				// Overwrite is set.
 237				// We didn't have overwrite set, find an update function or find an update tag so lets call it safe
 238				if (file_exists($this->parent->getPath('extension_site')))
 239				{
 240					// If the site exists say so.
 241					JLog::add(
 242						JText::sprintf('JLIB_INSTALLER_ERROR_COMP_INSTALL_DIR_SITE', $this->parent->getPath('extension_site')),
 243						JLog::WARNING, 'jerror'
 244					);
 245				}
 246				else
 247				{
 248					// If the admin exists say so
 249					JLog::add(
 250						JText::sprintf('JLIB_INSTALLER_ERROR_COMP_INSTALL_DIR_ADMIN', $this->parent->getPath('extension_administrator')),
 251						JLog::WARNING, 'jerror'
 252					);
 253				}
 254				return false;
 255			}
 256		}
 257
 258		/*
 259		 * ---------------------------------------------------------------------------------------------
 260		 * Installer Trigger Loading
 261		 * ---------------------------------------------------------------------------------------------
 262		 */
 263
 264		// If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet)
 265		$manifestScript = (string) $this->manifest->scriptfile;
 266
 267		if ($manifestScript)
 268		{
 269			$manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript;
 270
 271			if (is_file($manifestScriptFile))
 272			{
 273				// Load the file
 274				include_once $manifestScriptFile;
 275			}
 276
 277			// Set the class name
 278			$classname = $this->get('element') . 'InstallerScript';
 279
 280			if (class_exists($classname))
 281			{
 282				// Create a new instance
 283				$this->parent->manifestClass = new $classname($this);
 284
 285				// And set this so we can copy it later
 286				$this->set('manifest_script', $manifestScript);
 287			}
 288		}
 289
 290		// Run preflight if possible (since we know we're not an update)
 291		ob_start();
 292		ob_implicit_flush(false);
 293
 294		if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight'))
 295		{
 296			if ($this->parent->manifestClass->preflight('install', $this) === false)
 297			{
 298				// Install failed, rollback changes
 299				$this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_CUSTOM_INSTALL_FAILURE'));
 300				return false;
 301			}
 302		}
 303
 304		// Create msg object; first use here
 305		$msg = ob_get_contents();
 306		ob_end_clean();
 307
 308		// If the component directory does not exist, let's create it
 309		$created = false;
 310
 311		if (!file_exists($this->parent->getPath('extension_site')))
 312		{
 313			if (!$created = JFolder::create($this->parent->getPath('extension_site')))
 314			{
 315				JLog::add(
 316					JText::sprintf('JLIB_INSTALLER_ERROR_COMP_INSTALL_FAILED_TO_CREATE_DIRECTORY_SITE', $this->parent->getPath('extension_site')),
 317					JLog::WARNING, 'jerror'
 318				);
 319				return false;
 320			}
 321		}
 322
 323		/*
 324		 * Since we created the component directory and we will want to remove it if we have to roll back
 325		 * the installation, let's add it to the installation step stack
 326		 */
 327		if ($created)
 328		{
 329			$this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_site')));
 330		}
 331
 332		// If the component admin directory does not exist, let's create it
 333		$created = false;
 334
 335		if (!file_exists($this->parent->getPath('extension_administrator')))
 336		{
 337			if (!$created = JFolder::create($this->parent->getPath('extension_administrator')))
 338			{
 339				JLog::add(
 340					JText::sprintf('JLIB_INSTALLER_ERROR_COMP_INSTALL_FAILED_TO_CREATE_DIRECTORY_ADMIN', $this->parent->getPath('extension_administrator')),
 341					JLog::WARNING, 'jerror'
 342				);
 343
 344				// Install failed, rollback any changes
 345				$this->parent->abort();
 346
 347				return false;
 348			}
 349		}
 350
 351		/*
 352		 * Since we created the component admin directory and we will want to remove it if we have to roll
 353		 * back the installation, let's add it to the installation step stack
 354		 */
 355		if ($created)
 356		{
 357			$this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_administrator')));
 358		}
 359
 360		// Copy site files
 361		if ($this->manifest->files)
 362		{
 363			if ($this->parent->parseFiles($this->manifest->files) === false)
 364			{
 365				// Install failed, rollback any changes
 366				$this->parent->abort();
 367
 368				return false;
 369			}
 370		}
 371
 372		// Copy admin files
 373		if ($this->manifest->administration->files)
 374		{
 375			if ($this->parent->parseFiles($this->manifest->administration->files, 1) === false)
 376			{
 377				// Install failed, rollback any changes
 378				$this->parent->abort();
 379
 380				return false;
 381			}
 382		}
 383
 384		// Parse optional tags
 385		$this->parent->parseMedia($this->manifest->media);
 386		$this->parent->parseLanguages($this->manifest->languages);
 387		$this->parent->parseLanguages($this->manifest->administration->languages, 1);
 388
 389		// If there is a manifest script, let's copy it.
 390		if ($this->get('manifest_script'))
 391		{
 392			$path['src'] = $this->parent->getPath('source') . '/' . $this->get('manifest_script');
 393			$path['dest'] = $this->parent->getPath('extension_administrator') . '/' . $this->get('manifest_script');
 394
 395			if (!file_exists($path['dest']) || $this->parent->isOverwrite())
 396			{
 397				if (!$this->parent->copyFiles(array($path)))
 398				{
 399					// Install failed, rollback changes
 400					$this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_MANIFEST'));
 401
 402					return false;
 403				}
 404			}
 405		}
 406
 407		/*
 408		 * ---------------------------------------------------------------------------------------------
 409		 * Database Processing Section
 410		 * ---------------------------------------------------------------------------------------------
 411		 */
 412
 413		// Run the install queries for the component
 414		if (isset($this->manifest->install->sql))
 415		{
 416			$result = $this->parent->parseSQLFiles($this->manifest->install->sql);
 417
 418			if ($result === false)
 419			{
 420				// Install failed, rollback changes
 421				$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_SQL_ERROR', $db->stderr(true)));
 422
 423				return false;
 424			}
 425		}
 426
 427		/**
 428		 * ---------------------------------------------------------------------------------------------
 429		 * Custom Installation Script Section
 430		 * ---------------------------------------------------------------------------------------------
 431		 */
 432
 433		/*
 434		 * If we have an install script, let's include it, execute the custom
 435		 * install method, and append the return value from the custom install
 436		 * method to the installation message.
 437		 */
 438		ob_start();
 439		ob_implicit_flush(false);
 440
 441		if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'install'))
 442		{
 443			if ($this->parent->manifestClass->install($this) === false)
 444			{
 445				// Install failed, rollback changes
 446				$this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_CUSTOM_INSTALL_FAILURE'));
 447
 448				return false;
 449			}
 450		}
 451
 452		// Append messages
 453		$msg .= ob_get_contents();
 454		ob_end_clean();
 455
 456		/**
 457		 * ---------------------------------------------------------------------------------------------
 458		 * Finalization and Cleanup Section
 459		 * ---------------------------------------------------------------------------------------------
 460		 */
 461
 462		// Add an entry to the extension table with a whole heap of defaults
 463		$row = JTable::getInstance('extension');
 464		$row->set('name', $this->get('name'));
 465		$row->set('type', 'component');
 466		$row->set('element', $this->get('element'));
 467
 468		// There is no folder for components
 469		$row->set('folder', '');
 470		$row->set('enabled', 1);
 471		$row->set('protected', 0);
 472		$row->set('access', 0);
 473		$row->set('client_id', 1);
 474		$row->set('params', $this->parent->getParams());
 475		$row->set('manifest_cache', $this->parent->generateManifestCache());
 476
 477		if (!$row->store())
 478		{
 479			// Install failed, roll back changes
 480			$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_ROLLBACK', $db->stderr(true)));
 481			return false;
 482		}
 483
 484		$eid = $row->extension_id;
 485
 486		// Clobber any possible pending updates
 487		$update = JTable::getInstance('update');
 488		$uid = $update->find(array('element' => $this->get('element'), 'type' => 'component', 'client_id' => 1, 'folder' => ''));
 489
 490		if ($uid)
 491		{
 492			$update->delete($uid);
 493		}
 494
 495		// We will copy the manifest file to its appropriate place.
 496		if (!$this->parent->copyManifest())
 497		{
 498			// Install failed, rollback changes
 499			$this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_COPY_SETUP'));
 500			return false;
 501		}
 502
 503		// Time to build the admin menus
 504		if (!$this->_buildAdminMenus($row->extension_id))
 505		{
 506			JLog::add(JText::_('JLIB_INSTALLER_ABORT_COMP_BUILDADMINMENUS_FAILED'), JLog::WARNING, 'jerror');
 507
 508			// @todo remove code: $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_ROLLBACK', $db->stderr(true)));
 509			// @todo remove code: return false;
 510		}
 511
 512		// Set the schema version to be the latest update version
 513		if ($this->manifest->update)
 514		{
 515			$this->parent->setSchemaVersion($this->manifest->update->schemas, $eid);
 516		}
 517
 518		// Register the component container just under root in the assets table.
 519		$asset = JTable::getInstance('Asset');
 520		$asset->name = $row->element;
 521		$asset->parent_id = 1;
 522		$asset->rules = '{}';
 523		$asset->title = $row->name;
 524		$asset->setLocation(1, 'last-child');
 525		if (!$asset->store())
 526		{
 527			// Install failed, roll back changes
 528			$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_ROLLBACK', $db->stderr(true)));
 529			return false;
 530		}
 531
 532		// And now we run the postflight
 533		ob_start();
 534		ob_implicit_flush(false);
 535
 536		if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight'))
 537		{
 538			$this->parent->manifestClass->postflight('install', $this);
 539		}
 540
 541		// Append messages
 542		$msg .= ob_get_contents();
 543		ob_end_clean();
 544
 545		if ($msg != '')
 546		{
 547			$this->parent->set('extension_message', $msg);
 548		}
 549
 550		return $row->extension_id;
 551	}
 552
 553	/**
 554	 * Custom update method for components
 555	 *
 556	 * @return  boolean  True on success
 557	 *
 558	 * @since   11.1
 559	 */
 560	public function update()
 561	{
 562		// Get a database connector object
 563		$db = $this->parent->getDbo();
 564
 565		// Set the overwrite setting
 566		$this->parent->setOverwrite(true);
 567
 568		// Get the extension manifest object
 569		$this->manifest = $this->parent->getManifest();
 570
 571		/**
 572		 * ---------------------------------------------------------------------------------------------
 573		 * Manifest Document Setup Section
 574		 * ---------------------------------------------------------------------------------------------
 575		 */
 576
 577		// Set the extension's name
 578		$name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'));
 579		if (substr($name, 0, 4) == 'com_')
 580		{
 581			$element = $name;
 582		}
 583		else
 584		{
 585			$element = 'com_' . $name;
 586		}
 587
 588		$this->set('name', $name);
 589		$this->set('element', $element);
 590
 591		// Get the component description
 592		$description = (string) $this->manifest->description;
 593
 594		if ($description)
 595		{
 596			$this->parent->set('message', JText::_($description));
 597		}
 598		else
 599		{
 600			$this->parent->set('message', '');
 601		}
 602
 603		// Set the installation target paths
 604		$this->parent->setPath('extension_site', JPath::clean(JPATH_SITE . '/components/' . $this->get('element')));
 605		$this->parent->setPath('extension_administrator', JPath::clean(JPATH_ADMINISTRATOR . '/components/' . $this->get('element')));
 606
 607		// Copy the admin path as it's used as a common base
 608		$this->parent->setPath('extension_root', $this->parent->getPath('extension_administrator'));
 609
 610		// Hunt for the original XML file
 611		$old_manifest = null;
 612
 613		// Create a new installer because findManifest sets stuff
 614		// Look in the administrator first
 615		$tmpInstaller = new JInstaller;
 616		$tmpInstaller->setPath('source', $this->parent->getPath('extension_administrator'));
 617
 618		if (!$tmpInstaller->findManifest())
 619		{
 620			// Then the site
 621			$tmpInstaller->setPath('source', $this->parent->getPath('extension_site'));
 622			if ($tmpInstaller->findManifest())
 623			{
 624				$old_manifest = $tmpInstaller->getManifest();
 625			}
 626		}
 627		else
 628		{
 629			$old_manifest = $tmpInstaller->getManifest();
 630		}
 631
 632		// Should do this above perhaps?
 633		if ($old_manifest)
 634		{
 635			$this->oldAdminFiles = $old_manifest->administration->files;
 636			$this->oldFiles = $old_manifest->files;
 637		}
 638		else
 639		{
 640			$this->oldAdminFiles = null;
 641			$this->oldFiles = null;
 642		}
 643
 644		/**
 645		 * ---------------------------------------------------------------------------------------------
 646		 * Basic Checks Section
 647		 * ---------------------------------------------------------------------------------------------
 648		 */
 649
 650		// Make sure that we have an admin element
 651		if (!$this->manifest->administration)
 652		{
 653			JLog::add(JText::_('JLIB_INSTALLER_ABORT_COMP_UPDATE_ADMIN_ELEMENT'), JLog::WARNING, 'jerror');
 654			return false;
 655		}
 656
 657		/**
 658		 * ---------------------------------------------------------------------------------------------
 659		 * Installer Trigger Loading
 660		 * ---------------------------------------------------------------------------------------------
 661		 */
 662
 663		// If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet)
 664		$manifestScript = (string) $this->manifest->scriptfile;
 665
 666		if ($manifestScript)
 667		{
 668			$manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript;
 669
 670			if (is_file($manifestScriptFile))
 671			{
 672				// Load the file
 673				include_once $manifestScriptFile;
 674			}
 675
 676			// Set the class name
 677			$classname = $element . 'InstallerScript';
 678
 679			if (class_exists($classname))
 680			{
 681				// Create a new instance
 682				$this->parent->manifestClass = new $classname($this);
 683
 684				// And set this so we can copy it later
 685				$this->set('manifest_script', $manifestScript);
 686			}
 687		}
 688
 689		// Run preflight if possible (since we know we're not an update)
 690		ob_start();
 691		ob_implicit_flush(false);
 692
 693		if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight'))
 694		{
 695			if ($this->parent->manifestClass->preflight('update', $this) === false)
 696			{
 697				// Install failed, rollback changes
 698				$this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_CUSTOM_INSTALL_FAILURE'));
 699
 700				return false;
 701			}
 702		}
 703
 704		// Create msg object; first use here
 705		$msg = ob_get_contents();
 706		ob_end_clean();
 707
 708		/**
 709		 * ---------------------------------------------------------------------------------------------
 710		 * Filesystem Processing Section
 711		 * ---------------------------------------------------------------------------------------------
 712		 */
 713
 714		// If the component directory does not exist, let's create it
 715		$created = false;
 716
 717		if (!file_exists($this->parent->getPath('extension_site')))
 718		{
 719			if (!$created = JFolder::create($this->parent->getPath('extension_site')))
 720			{
 721				JLog::add(
 722					JText::sprintf('JLIB_INSTALLER_ERROR_COMP_UPDATE_FAILED_TO_CREATE_DIRECTORY_SITE', $this->parent->getPath('extension_site')),
 723					JLog::WARNING, 'jerror'
 724				);
 725				return false;
 726			}
 727		}
 728
 729		/*
 730		 * Since we created the component directory and will want to remove it if we have to roll back
 731		 * the installation, lets add it to the installation step stack
 732		 */
 733		if ($created)
 734		{
 735			$this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_site')));
 736		}
 737
 738		// If the component admin directory does not exist, let's create it
 739		$created = false;
 740
 741		if (!file_exists($this->parent->getPath('extension_administrator')))
 742		{
 743			if (!$created = JFolder::create($this->parent->getPath('extension_administrator')))
 744			{
 745				JLog::add(
 746					JText::sprintf('JLIB_INSTALLER_ERROR_COMP_UPDATE_FAILED_TO_CREATE_DIRECTORY_ADMIN', $this->parent->getPath('extension_administrator')),
 747					JLog::WARNING, 'jerror'
 748				);
 749
 750				// Install failed, rollback any changes
 751				$this->parent->abort();
 752
 753				return false;
 754			}
 755		}
 756
 757		/*
 758		 * Since we created the component admin directory and we will want to remove it if we have to roll
 759		 * back the installation, let's add it to the installation step stack
 760		 */
 761		if ($created)
 762		{
 763			$this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_administrator')));
 764		}
 765
 766		// Find files to copy
 767		if ($this->manifest->files)
 768		{
 769			if ($this->parent->parseFiles($this->manifest->files, 0, $this->oldFiles) === false)
 770			{
 771				// Install failed, rollback any changes
 772				$this->parent->abort();
 773
 774				return false;
 775			}
 776		}
 777
 778		if ($this->manifest->administration->files)
 779		{
 780			if ($this->parent->parseFiles($this->manifest->administration->files, 1, $this->oldAdminFiles) === false)
 781			{
 782				// Install failed, rollback any changes
 783				$this->parent->abort();
 784
 785				return false;
 786			}
 787		}
 788
 789		// Parse optional tags
 790		$this->parent->parseMedia($this->manifest->media);
 791		$this->parent->parseLanguages($this->manifest->languages);
 792		$this->parent->parseLanguages($this->manifest->administration->languages, 1);
 793
 794		// If there is a manifest script, let's copy it.
 795		if ($this->get('manifest_script'))
 796		{
 797			$path['src'] = $this->parent->getPath('source') . '/' . $this->get('manifest_script');
 798			$path['dest'] = $this->parent->getPath('extension_administrator') . '/' . $this->get('manifest_script');
 799
 800			if (!file_exists($path['dest']) || $this->parent->isOverwrite())
 801			{
 802				if (!$this->parent->copyFiles(array($path)))
 803				{
 804					// Install failed, rollback changes
 805					$this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_UPDATE_MANIFEST'));
 806
 807					return false;
 808				}
 809			}
 810		}
 811
 812		/**
 813		 * ---------------------------------------------------------------------------------------------
 814		 * Database Processing Section
 815		 * ---------------------------------------------------------------------------------------------
 816		 */
 817
 818		// Let's run the update queries for the component
 819		$row = JTable::getInstance('extension');
 820		$eid = $row->find(array('element' => strtolower($this->get('element')), 'type' => 'component'));
 821
 822		if ($this->manifest->update)
 823		{
 824			$result = $this->parent->parseSchemaUpdates($this->manifest->update->schemas, $eid);
 825
 826			if ($result === false)
 827			{
 828				// Install failed, rollback changes
 829				$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_UPDATE_SQL_ERROR', $db->stderr(true)));
 830
 831				return false;
 832			}
 833		}
 834
 835		// Time to build the admin menus
 836		if (!$this->_buildAdminMenus($eid))
 837		{
 838			JLog::add(JText::_('JLIB_INSTALLER_ABORT_COMP_BUILDADMINMENUS_FAILED'), JLog::WARNING, 'jerror');
 839
 840			// $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_ROLLBACK', $db->stderr(true)));
 841			// Return false;
 842		}
 843
 844		/**
 845		 * ---------------------------------------------------------------------------------------------
 846		 * Custom Installation Script Section
 847		 * ---------------------------------------------------------------------------------------------
 848		 */
 849
 850		/*
 851		 * If we have an install script, let's include it, execute the custom
 852		 * update method, and append the return value from the custom update
 853		 * method to the installation message.
 854		 */
 855		ob_start();
 856		ob_implicit_flush(false);
 857
 858		if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update'))
 859		{
 860			if ($this->parent->manifestClass->update($this) === false)
 861			{
 862				// Install failed, rollback changes
 863				$this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_CUSTOM_INSTALL_FAILURE'));
 864
 865				return false;
 866			}
 867		}
 868
 869		// Append messages
 870		$msg .= ob_get_contents();
 871		ob_end_clean();
 872
 873		/**
 874		 * ---------------------------------------------------------------------------------------------
 875		 * Finalization and Cleanup Section
 876		 * ---------------------------------------------------------------------------------------------
 877		 */
 878
 879		// Clobber any possible pending updates
 880		$update = JTable::getInstance('update');
 881		$uid = $update->find(array('element' => $this->get('element'), 'type' => 'component', 'client_id' => 1, 'folder' => ''));
 882
 883		if ($uid)
 884		{
 885			$update->delete($uid);
 886		}
 887
 888		// Update an entry to the extension table
 889		if ($eid)
 890		{
 891			$row->load($eid);
 892		}
 893		else
 894		{
 895			// Set the defaults
 896			// There is no folder for components
 897			$row->folder = '';
 898			$row->enabled = 1;
 899			$row->protected = 0;
 900			$row->access = 1;
 901			$row->client_id = 1;
 902			$row->params = $this->parent->getParams();
 903		}
 904
 905		$row->name = $this->get('name');
 906		$row->type = 'component';
 907		$row->element = $this->get('element');
 908		$row->manifest_cache = $this->parent->generateManifestCache();
 909
 910		if (!$row->store())
 911		{
 912			// Install failed, roll back changes
 913			$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_UPDATE_ROLLBACK', $db->stderr(true)));
 914
 915			return false;
 916		}
 917
 918		// We will copy the manifest file to its appropriate place.
 919		if (!$this->parent->copyManifest())
 920		{
 921			// Install failed, rollback changes
 922			$this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_UPDATE_COPY_SETUP'));
 923
 924			return false;
 925		}
 926
 927		// And now we run the postflight
 928		ob_start();
 929		ob_implicit_flush(false);
 930
 931		if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight'))
 932		{
 933			$this->parent->manifestClass->postflight('update', $this);
 934		}
 935
 936		// Append messages
 937		$msg .= ob_get_contents();
 938		ob_end_clean();
 939
 940		if ($msg != '')
 941		{
 942			$this->parent->set('extension_message', $msg);
 943		}
 944
 945		return $row->extension_id;
 946	}
 947
 948	/**
 949	 * Custom uninstall method for components
 950	 *
 951	 * @param   integer  $id  The unique extension id of the component to uninstall
 952	 *
 953	 * @return  mixed  Return value for uninstall method in component uninstall file
 954	 *
 955	 * @since   11.1
 956	 */
 957	public function uninstall($id)
 958	{
 959		$db = $this->parent->getDbo();
 960		$row = null;
 961		$retval = true;
 962
 963		// First order of business will be to load the component object table from the database.
 964		// This should give us the necessary information to proceed.
 965		$row = JTable::getInstance('extension');
 966		if (!$row->load((int) $id))
 967		{
 968			JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_ERRORUNKOWNEXTENSION'), JLog::WARNING, 'jerror');
 969			return false;
 970		}
 971
 972		// Is the component we are trying to uninstall a core one?
 973		// Because that is not a good idea...
 974		if ($row->protected)
 975		{
 976			JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_WARNCORECOMPONENT'), JLog::WARNING, 'jerror');
 977			return false;
 978		}
 979
 980		// Get the admin and site paths for the component
 981		$this->parent->setPath('extension_administrator', JPath::clean(JPATH_ADMINISTRATOR . '/components/' . $row->element));
 982		$this->parent->setPath('extension_site', JPath::clean(JPATH_SITE . '/components/' . $row->element));
 983
 984		// Copy the admin path as it's used as a common base
 985		$this->parent->setPath('extension_root', $this->parent->getPath('extension_administrator'));
 986
 987		/**
 988		 * ---------------------------------------------------------------------------------------------
 989		 * Manifest Document Setup Section
 990		 * ---------------------------------------------------------------------------------------------
 991		 */
 992
 993		// Find and load the XML install file for the component
 994		$this->parent->setPath('source', $this->parent->getPath('extension_administrator'));
 995
 996		// Get the package manifest object
 997		// We do findManifest to avoid problem when uninstalling a list of extension: getManifest cache its manifest file
 998		$this->parent->findManifest();
 999		$this->manifest = $this->parent->getManifest();
1000
1001		if (!$this->manifest)
1002		{
1003			// Make sure we delete the folders if no manifest exists
1004			JFolder::delete($this->parent->getPath('extension_administrator'));
1005			JFolder::delete($this->parent->getPath('extension_site'));
1006
1007			// Remove the menu
1008			$this->_removeAdminMenus($row);
1009
1010			// Raise a warning
1011			JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_ERRORREMOVEMANUALLY'), JLog::WARNING, 'jerror');
1012
1013			// Return
1014			return false;
1015		}
1016
1017		// Set the extensions name
1018		$name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'));
1019		if (substr($name, 0, 4) == 'com_')
1020		{
1021			$element = $name;
1022		}
1023		else
1024		{
1025			$element = 'com_' . $name;
1026		}
1027
1028		$this->set('name', $name);
1029		$this->set('element', $element);
1030
1031		// Attempt to load the admin language file; might have uninstall strings
1032		$this->loadLanguage(JPATH_ADMINISTRATOR . '/components/' . $element);
1033
1034		/**
1035		 * ---------------------------------------------------------------------------------------------
1036		 * Installer Trigger Loading and Uninstall
1037		 * ---------------------------------------------------------------------------------------------
1038		 */
1039
1040		// If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet)
1041		$scriptFile = (string) $this->manifest->scriptfile;
1042
1043		if ($scriptFile)
1044		{
1045			$manifestScriptFile = $this->parent->getPath('source') . '/' . $scriptFile;
1046
1047			if (is_file($manifestScriptFile))
1048			{
1049				// Load the file
1050				include_once $manifestScriptFile;
1051			}
1052
1053			// Set the class name
1054			$classname = $row->element . 'InstallerScript';
1055
1056			if (class_exists($classname))
1057			{
1058				// Create a new instance
1059				$this->parent->manifestClass = new $classname($this);
1060
1061				// And set this so we can copy it later
1062				$this->set('manifest_script', $scriptFile);
1063			}
1064		}
1065
1066		ob_start();
1067		ob_implicit_flush(false);
1068
1069		// Run uninstall if possible
1070		if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'uninstall'))
1071		{
1072			$this->parent->manifestClass->uninstall($this);
1073		}
1074
1075		$msg = ob_get_contents();
1076		ob_end_clean();
1077
1078		if ($msg != '')
1079		{
1080			$this->parent->set('extension_message', $msg);
1081		}
1082
1083		/**
1084		 * ---------------------------------------------------------------------------------------------
1085		 * Database Processing Section
1086		 * ---------------------------------------------------------------------------------------------
1087		 */
1088
1089		// Let's run the uninstall queries for the component
1090		if (isset($this->manifest->uninstall->sql))
1091		{
1092			$result = $this->parent->parseSQLFiles($this->manifest->uninstall->sql);
1093
1094			if ($result === false)
1095			{
1096				// Install failed, rollback changes
1097				JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_SQL_ERROR', $db->stderr(true)), JLog::WARNING, 'jerror');
1098				$retval = false;
1099			}
1100		}
1101
1102		$this->_removeAdminMenus($row);
1103
1104		/**
1105		 * ---------------------------------------------------------------------------------------------
1106		 * Filesystem Processing Section
1107		 * ---------------------------------------------------------------------------------------------
1108		 */
1109
1110		// Let's remove those language files and media in the JROOT/images/ folder that are
1111		// associated with the component we are uninstalling
1112		$this->parent->removeFiles($this->manifest->media);
1113		$this->parent->removeFiles($this->manifest->languages);
1114		$this->parent->removeFiles($this->manifest->administration->languages, 1);
1115
1116		// Remove the schema version
1117		$query = $db->getQuery(true);
1118		$query->delete()->from('#__schemas')->where('extension_id = ' . $id);
1119		$db->setQuery($query);
1120		$db->execute();
1121
1122		// Remove the component container in the assets table.
1123		$asset = JTable::getInstance('Asset');
1124		if ($asset->loadByName($element))
1125		{
1126			$asset->delete();
1127		}
1128
1129		// Remove categories for this component
1130		$query = $db->getQuery(true);
1131		$query->delete()->from('#__categories')->where('extension=' . $db->quote($element), 'OR')
1132			->where('extension LIKE ' . $db->quote($element . '.%'));
1133		$db->setQuery($query);
1134		$db->execute();
1135
1136		// Clobber any possible pending updates
1137		$update = JTable::getInstance('update');
1138		$uid = $update->find(array('element' => $row->element, 'type' => 'component', 'client_id' => 1, 'folder' => ''));
1139
1140		if ($uid)
1141		{
1142			$update->delete($uid);
1143		}
1144
1145		// Now we need to delete the installation directories. This is the final step in uninstalling the component.
1146		if (trim($row->element))
1147		{
1148			// Delete the component site directory
1149			if (is_dir($this->parent->getPath('extension_site')))
1150			{
1151				if (!JFolder::delete($this->parent->getPath('extension_site')))
1152				{
1153					JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_FAILED_REMOVE_DIRECTORY_SITE'), JLog::WARNING, 'jerror');
1154					$retval = false;
1155				}
1156			}
1157
1158			// Delete the component admin directory
1159			if (is_dir($this->parent->getPath('extension_administrator')))
1160			{
1161				if (!JFolder::delete($this->parent->getPath('extension_administrator')))
1162				{
1163					JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_FAILED_REMOVE_DIRECTORY_ADMIN'), JLog::WARNING, 'jerror');
1164					$retval = false;
1165				}
1166			}
1167
1168			// Now we will no longer need the extension object, so let's delete it and free up memory
1169			$row->delete($row->extension_id);
1170			unset($row);
1171
1172			return $retval;
1173		}
1174		else
1175		{
1176			// No component option defined... cannot delete what we don't know about
1177			JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_NO_OPTION'), JLog::WARNING, 'jerror');
1178			return false;
1179		}
1180	}
1181
1182	/**
1183	 * Method to build menu database entries for a component
1184	 *
1185	 * @return  boolean  True if successful
1186	 *
1187	 * @since   11.1
1188	 */
1189	protected function _buildAdminMenus()
1190	{
1191		$db = $this->parent->getDbo();
1192		$table = JTable::getInstance('menu');
1193		$option = $this->get('element');
1194
1195		// If a component exists with this option in the table then we don't need to add menus
1196		$query = $db->getQuery(true);
1197		$query->select('m.id, e.extension_id');
1198		$query->from('#__menu AS m');
1199		$query->leftJoin('#__extensions AS e ON m.component_id = e.extension_id');
1200		$query->where('m.parent_id = 1');
1201		$query->where('m.client_id = 1');
1202		$query->where('e.element = ' . $db->quote($option));
1203
1204		$db->setQuery($query);
1205
1206		$componentrow = $db->loadObject();
1207
1208		// Check if menu items exist
1209		if ($componentrow)
1210		{
1211			// Don't do anything if overwrite has not been enabled
1212			if (!$this->parent->isOverwrite())
1213			{
1214				return true;
1215			}
1216
1217			// Remove existing menu items if overwrite has been enabled
1218			if ($option)
1219			{
1220				// If something goes wrong, there's no way to rollback TODO: Search for better solution
1221				$this->_removeAdminMenus($componentrow);
1222			}
1223
1224			$component_id = $componentrow->extension_id;
1225		}
1226		else
1227		{
1228			// Lets find the extension id
1229			$query->clear();
1230			$query->select('e.extension_id');
1231			$query->from('#__extensions AS e');
1232			$query->where('e.element = ' . $db->quote($option));
1233
1234			$db->setQuery($query);
1235
1236			// TODO Find Some better way to discover the component_id
1237			$component_id = $db->loadResult();
1238		}
1239
1240		// Ok, now its time to handle the menus.  Start with the component root menu, then handle submenus.
1241		$menuElement = $this->manifest->administration->menu;
1242
1243		if ($menuElement)
1244		{
1245			$data = array();
1246			$data['menutype'] = 'main';
1247			$data['client_id'] = 1;
1248			$data['title'] = (string) $menuElement;
1249			$data['alias'] = (string) $menuElement;
1250			$data['link'] = 'index.php?option=' . $option;
1251			$data['type'] = 'component';
1252			$data['published'] = 0;
1253			$data['parent_id'] = 1;
1254			$data['component_id'] = $component_id;
1255			$data['img'] = ((string) $menuElement->attributes()->img) ? (string) $menuElement->attributes()->img : 'class:component';
1256			$data['home'] = 0;
1257
1258			try
1259			{
1260				$table->setLocation(1, 'last-child');
1261			}
1262			catch (InvalidArgumentException $e)
1263			{
1264				JLog::add($e->getMessage(), JLog::WARNING, 'jerror');
1265				return false;
1266			}
1267
1268			if (!$table->bind($data) || !$table->check() || !$table->store())
1269			{
1270				// Install failed, warn user and rollback changes
1271				JLog::add($table->getError(), JLog::WARNING, 'jerror');
1272				return false;
1273			}
1274
1275			/*
1276			 * Since we have created a menu item, we add it to the installation step stack
1277			 * so that if we have to rollback the changes we can undo it.
1278			 */
1279			$this->parent->pushStep(array('type' => 'menu', 'id' => $component_id));
1280		}
1281		// No menu element was specified, Let's make a generic menu item
1282		else
1283		{
1284			$data = array();
1285			$data['menutype'] = 'main';
1286			$data['client_id'] = 1;
1287			$data['title'] = $option;
1288			$data['alias'] = $option;
1289			$data['link'] = 'index.php?option=' . $option;
1290			$data['type'] = 'component';
1291			$data['published'] = 0;
1292			$data['parent_id'] = 1;
1293			$data['component_id'] = $component_id;
1294			$data['img'] = 'class:component';
1295			$data['home'] = 0;
1296
1297			try
1298			{
1299				$table->setLocation(1, 'last-child');
1300			}
1301			catch (InvalidArgumentException $e)
1302			{
1303				JLog::add($e->getMessage(), JLog::WARNING, 'jerror');
1304				return false;
1305			}
1306
1307			if (!$table->bind($data) || !$table->check() || !$table->store())
1308			{
1309				// Install failed, warn user and rollback changes
1310				JLog::add($table->getError(), JLog::WARNING, 'jerror');
1311				return false;
1312			}
1313
1314			/*
1315			 * Since we have created a menu item, we add it to the installation step stack
1316			 * so that if we have to rollback the changes we can undo it.
1317			 */
1318			$this->parent->pushStep(array('type' => 'menu', 'id' => $component_id));
1319		}
1320
1321		/*
1322		 * Process SubMenus
1323		 */
1324
1325		if (!$this->manifest->administration->submenu)
1326		{
1327			return true;
1328		}
1329
1330		$parent_id = $table->id;
1331
1332		foreach ($this->manifest->administration->submenu->menu as $child)
1333		{
1334			$data = array();
1335			$data['menutype'] = 'main';
1336			$data['client_id'] = 1;
1337			$data['title'] = (string) $child;
1338			$data['alias'] = (string) $child;
1339			$data['type'] = 'component';
1340			$data['published'] = 0;
1341			$data['parent_id'] = $parent_id;
1342			$data['component_id'] = $component_id;
1343			$data['img'] = ((string) $child->attributes()->img) ? (string) $child->attributes()->img : 'class:component';
1344			$data['home'] = 0;
1345
1346			// Set the sub menu link
1347			if ((string) $child->attributes()->link)
1348			{
1349				$data['link'] = 'index.php?' . $child->attributes()->link;
1350			}
1351			else
1352			{
1353				$request = array();
1354
1355				if ((string) $child->attributes()->act)
1356				{
1357					$request[] = 'act=' . $child->attributes()->act;
1358				}
1359
1360				if ((string) $child->attributes()->task)
1361				{
1362					$request[] = 'task=' . $child->attributes()->task;
1363				}
1364
1365				if ((string) $child->attributes()->controller)
1366				{
1367					$request[] = 'controller=' . $child->attributes()->controller;
1368				}
1369
1370				if ((string) $child->attributes()->view)
1371				{
1372					$request[] = 'view=' . $child->attributes()->view;
1373				}
1374
1375				if ((string) $child->attributes()->layout)
1376				{
1377					$request[] = 'layout=' . $child->attributes()->layout;
1378				}
1379
1380				if ((string) $child->attributes()->sub)
1381				{
1382					$request[] = 'sub=' . $child->attributes()->sub;
1383				}
1384
1385				$qstring = (count($request)) ? '&' . implode('&', $request) : '';
1386				$data['link'] = 'index.php?option=' . $option . $qstring;
1387			}
1388
1389			$table = JTable::getInstance('menu');
1390
1391			try
1392			{
1393				$table->setLocation($parent_id, 'last-child');
1394			}
1395			catch (InvalidArgumentException $e)
1396			{
1397				return false;
1398			}
1399
1400			if (!$table->bind($data) || !$table->check() || !$table->store())
1401			{
1402				// Install failed, rollback changes
1403				return false;
1404			}
1405
1406			/*
1407			 * Since we have created a menu item, we add it to the installation step stack
1408			 * so that if we have to rollback the changes we can undo it.
1409			 */
1410			$this->parent->pushStep(array('type' => 'menu', 'id' => $component_id));
1411		}
1412
1413		return true;
1414	}
1415
1416	/**
1417	 * Method to remove admin menu references to a component
1418	 *
1419	 * @param   object  &$row  Component table object.
1420	 *
1421	 * @return  boolean  True if successful.
1422	 *
1423	 * @since   11.1
1424	 */
1425	protected function _removeAdminMenus(&$row)
1426	{
1427		$db = $this->parent->getDbo();
1428		$table = JTable::getInstance('menu');
1429		$id = $row->extension_id;
1430
1431		// Get the ids of the menu items
1432		$query = $db->getQuery(true);
1433		$query->select('id');
1434		$query->from('#__menu');
1435		$query->where($query->qn('client_id') . ' = 1');
1436		$query->where($query->qn('component_id') . ' = ' . (int) $id);
1437
1438		$db->setQuery($query);
1439
1440		$ids = $db->loadColumn();
1441
1442		// Check for error
1443		if (!empty($ids))
1444		{
1445			// Iterate the items to delete each one.
1446			foreach ($ids as $menuid)
1447			{
1448				if (!$table->delete((int) $menuid))
1449				{
1450					$this->setError($table->getError());
1451					return false;
1452				}
1453			}
1454			// Rebuild the whole tree
1455			$table->rebuild();
1456
1457		}
1458		return true;
1459	}
1460
1461	/**
1462	 * Custom rollback method
1463	 * - Roll back the component menu item
1464	 *
1465	 * @param   array  $step  Installation step to rollback.
1466	 *
1467	 * @return  boolean  True on success
1468	 *
1469	 * @since   11.1
1470	 */
1471	protected function _rollback_menu($step)
1472	{
1473		return $this->_removeAdminMenus((object) array('extension_id' => $step['id']));
1474	}
1475
1476	/**
1477	 * Discover unregistered extensions.
1478	 *
1479	 * @return  array  A list of extensions.
1480	 *
1481	 * @since   11.1
1482	 */
1483	public function discover()
1484	{
1485		$results = array();
1486		$site_components = JFolder::folders(JPATH_SITE . '/components');
1487		$admin_components = JFolder::folders(JPATH_ADMINISTRATOR . '/components');
1488
1489		foreach ($site_components as $component)
1490		{
1491			if (file_exists(JPATH_SITE . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml'))
1492			{
1493				$manifest_details = JInstaller::parseXMLInstallFile(
1494					JPATH_SITE . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml'
1495				);
1496				$extension = JTable::getInstance('extension');
1497				$extension->set('type', 'component');
1498				$extension->set('client_id', 0);
1499				$extension->set('element', $component);
1500				$extension->set('name', $component);
1501				$extension->set('state', -1);
1502				$extension->set('manifest_cache', json_encode($manifest_details));
1503				$results[] = $extension;
1504			}
1505		}
1506
1507		foreach ($admin_components as $component)
1508		{
1509			if (file_exists(JPATH_ADMINISTRATOR . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml'))
1510			{
1511				$manifest_details = JInstaller::parseXMLInstallFile(
1512					JPATH_ADMINISTRATOR . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml'
1513				);
1514				$extension = JTable::getInstance('extension');
1515				$extension->set('type', 'component');
1516				$extension->set('client_id', 1);
1517				$extension->set('element', $component);
1518				$extension->set('name', $component);
1519				$extension->set('state', -1);
1520				$extension->set('manifest_cache', json_encode($manifest_details));
1521				$results[] = $extension;
1522			}
1523		}
1524		return $results;
1525	}
1526
1527	/**
1528	 * Install unregistered extensions that have been discovered.
1529	 *
1530	 * @return  mixed
1531	 *
1532	 * @since   11.1
1533	 */
1534	public function discover_install()
1535	{
1536		// Need to find to find where the XML file is since we don't store this normally
1537		$client = JApplicationHelper::getClientInfo($this->parent->extension->client_id);
1538		$short_element = str_replace('com_', '', $this->parent->extension->element);
1539		$manifestPath = $client->path . '/components/' . $this->parent->extension->element . '/' . $short_element . '.xml';
1540		$this->parent->manifest = $this->parent->isManifest($manifestPath);
1541		$this->parent->setPath('manifest', $manifestPath);
1542		$this->parent->setPath('source', $client->path . '/components/' . $this->parent->extension->element);
1543		$this->parent->setPath('extension_root', $this->parent->getPath('source'));
1544
1545		$manifest_details = JInstaller::parseXMLInstallFile($this->parent->getPath('manifest'));
1546		$this->parent->extension->manifest_cache = json_encode($manifest_details);
1547		$this->parent->extension->state = 0;
1548		$this->parent->extension->name = $manifest_details['name'];
1549		$this->parent->extension->enabled = 1;
1550		$this->parent->extension->params = $this->parent->getParams();
1551
1552		try
1553		{
1554			$this->parent->extension->store();
1555		}
1556		catch (RuntimeException $e)
1557		{
1558			JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_DISCOVER_STORE_DETAILS'), JLog::WARNING, 'jerror');
1559			return false;
1560		}
1561
1562		// Now we need to run any SQL it has, languages, media or menu stuff
1563
1564		// Get a database connector object
1565		$db = $this->parent->getDbo();
1566
1567		// Get the extension manifest object
1568		$this->manifest = $this->parent->getManifest();
1569
1570		/**
1571		 * ---------------------------------------------------------------------------------------------
1572		 * Manifest Document Setup Section
1573		 * ---------------------------------------------------------------------------------------------
1574		 */
1575
1576		// Set the extensions name
1577		$name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'));
1578		if (substr($name, 0, 4) == 'com_')
1579		{
1580			$element = $name;
1581		}
1582		else
1583		{
1584			$element = 'com_' . $name;
1585		}
1586
1587		$this->set('name', $name);
1588		$this->set('element', $element);
1589
1590		// Get the component description
1591		$description = (string) $this->manifest->description;
1592
1593		if ($description)
1594		{
1595			$this->parent->set('message', JText::_((string) $description));
1596		}
1597		else
1598		{
1599			$this->parent->set('message', '');
1600		}
1601
1602		// Set the installation target paths
1603		$this->parent->setPath('extension_site', JPath::clean(JPATH_SITE . '/components/' . $this->get('element')));
1604		$this->parent->setPath('extension_administrator', JPath::clean(JPATH_ADMINISTRATOR . '/components/' . $this->get('element')));
1605
1606		// Copy the admin path as it's used as a common base
1607		$this->parent->setPath('extension_root', $this->parent->getPath('extension_administrator'));
1608
1609		/**
1610		 * ---------------------------------------------------------------------------------------------
1611		 * Basic Checks Section
1612		 * ---------------------------------------------------------------------------------------------
1613		 */
1614
1615		// Make sure that we have an admin element
1616		if (!$this->manifest->administration)
1617		{
1618			JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_INSTALL_ADMIN_ELEMENT'), JLog::WARNING, 'jerror');
1619			return false;
1620		}
1621
1622		/**
1623		 * ---------------------------------------------------------------------------------------------
1624		 * Installer Trigger Loading
1625		 * ---------------------------------------------------------------------------------------------
1626		 */
1627		// If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet)
1628		$manifestScript = (string) $this->manifest->scriptfile;
1629
1630		if ($manifestScript)
1631		{
1632			$manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript;
1633
1634			if (is_file($manifestScriptFile))
1635			{
1636				// Load the file
1637				include_once $manifestScriptFile;
1638			}
1639
1640			// Set the class name
1641			$classname = $element . 'InstallerScript';
1642
1643			if (class_exists($classname))
1644			{
1645				// Create a new instance
1646				$this->parent->manifestClass = new $classname($this);
1647
1648				// And set this so we can copy it later
1649				$this->set('manifest_script', $manifestScript);
1650			}
1651		}
1652
1653		// Run preflight if possible (since we know we're not an update)
1654		ob_start();
1655		ob_implicit_flush(false);
1656
1657		if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight'))
1658		{
1659
1660			if ($this->parent->manifestClass->preflight('discover_install', $this) === false)
1661			{
1662				// Install failed, rollback changes
1663				$this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_CUSTOM_INSTALL_FAILURE'));
1664				return false;
1665			}
1666		}
1667
1668		// Create msg object; first use here
1669		$msg = ob_get_contents();
1670		ob_end_clean();
1671
1672		/*
1673		 *
1674		 * Normally we would copy files and create directories, lets skip to the optional files
1675		 * Note: need to dereference things!
1676		 * Parse optional tags
1677		 * @todo remove code: $this->parent->parseMedia($this->manifest->media);
1678		 *
1679		 * We don't do language because 1.6 suggests moving to extension based languages
1680		 * @todo remove code: $this->parent->parseLanguages($this->manifest->languages);
1681		 * @todo remove code: $this->parent->parseLanguages($this->manifest->administration->languages, 1);
1682		 */
1683
1684		/**
1685		 * ---------------------------------------------------------------------------------------------
1686		 * Database Processing Section
1687		 * ---------------------------------------------------------------------------------------------
1688		 */
1689
1690		// Let's run the install queries for the component
1691		if (isset($this->manifest->install->sql))
1692		{
1693			$utfresult = $this->parent->parseSQLFiles($this->manifest->install->sql);
1694
1695			if ($utfresult === false)
1696			{
1697				// Install failed, rollback changes
1698				$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_SQL_ERROR', $db->stderr(true)));
1699
1700				return false;
1701			}
1702		}
1703
1704		// Time to build the admin menus
1705		if (!$this->_buildAdminMenus($this->parent->

Large files files are truncated, but you can click here to view the full file